IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 实现树莓派基于Linux内核驱动GPIO -> 正文阅读

[嵌入式]实现树莓派基于Linux内核驱动GPIO

首先理解树莓派基于Linux内核驱动流程和框架
树莓派基于Linux内核驱动开发详解

一、树莓派基于Linux内核驱动GPIO前提工作

  1. 树莓派能够执行自己做好的设备文件(设备文件由驱动文件加载得到,驱动文件由设备驱动文件编译得到),那我们就可以往设备驱动文件里加入对GPIO操作,实现点灯,PWM,I2C,SPI等等

  2. 对GPIO操作,那我们就需要有芯片手册、板子的电路图。对树莓派来讲,芯片手册已经写的足够详细了 所以没用到电路图
    树莓派文档

  3. 我们要点灯,所以要看芯片手册的点灯寄存器,有
    GPFSEL0寄存器(设置GPIO模式)
    GPSET0寄存器(GPIO置1)
    GPCLR0寄存器(GPIO清0)

  4. 如何才能操作这些寄存器
    在这里插入图片描述
    在这里插入图片描述
    总线地址:CPU能够访问内存的范围(0x00000000——0xFFFFFFFF)
    物理地址:也称硬件的实际地址,物理地址是指处理器芯片发出,来进行地址空间寻址的地址,它与处理器地址引脚上发出的电信号相对应
    虚拟地址:程序所操作地址都是虚拟地址,在嵌入式Linux中,可以使用mmap函数或者ioremap函数将ARM物理地址映射成虚拟地址,就等同于直接操作GPIO硬件地址了。重要一点,在程序上需要把物理地址映射成虚拟地址才可以实现对硬件操作或对寄存器操作

  1. 由上图见IO的总线基地址为0x7E000000,通过MMU映射装载出的物理基地址为0x20000000(注意:芯片型号不同,映射装载出的物理基地址也不同,BCM2837芯片映射装载出的物理基地址为0x3F000000)
  2. 以BCM2837为例得到总线地址–>物理地址
    GPFSEL0寄存器0x7E000000 --> 3F000000
    GPSET0寄存器0x7E20001C --> 3F20001C
    GPCLR0寄存器0x7E200028 --> 3F200028
  3. 由上面的虚拟地址解释,程序上这样写
    GPFSEL0 = (volatile unsigned int *)ioremap(0x3F200000, 4);
    GPSET0 = (volatile unsigned int *)ioremap(0x3F20001C, 4);
    GPCLR0 = (volatile unsigned int *)ioremap(0x3F200028, 4);
    ioremap把物理地址转换成虚拟地址,(volatile unsigned int *)把地址映射成普通内存单元进行访问,从而程序可对寄存器操作(即可对内存单元进行访问),便可控制硬件了

二、代码实现

设备文件代码

#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init  module_exit声明
#include <linux/init.h> //__init  __exit 宏定义声明
#include <linux/device.h> //class  devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号  dev_t 类型声明
#include <asm/io.h>  //ioremap iounmap的头文件

static struct class *pin4_class;
static struct device *pin4_class_dev;

static dev_t devno;          //设备号
static int major = 232;      //主设备号
static int minor = 0;        //次设备号
static char *module_name = "pin4"; //设备文件名

volatile unsigned int* GPFSEL0 = NULL; //设置IO口模式
volatile unsigned int* GPSET0 = NULL; //置IO口为1
volatile unsigned int* GPCLR0 = NULL; //置IO口为0

//.open = pin4_open,所以执行open(fd,O_RDWR)时会进入此函数
static int pin4_open(struct inode *inode, struct file *file)
{
        printk("pin4_open\n"); //内核的打印函数和printf类似

        *GPFSEL0 &= ~(0x6 << 12);
        *GPFSEL0 |= (0x1 << 12);

        return 0;
}

//.write= pin4_write,所以执行write(fd,'1',1)时会进入此函数
static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        char usercmd = '\0';

        printk("pin4_write\n"); //内核的打印函数和printf类似

        copy_from_user(&usercmd, buf, count);

        if(usercmd == '1')  //应用层写'1'进入
        {
                printk("set 1\n");
                *GPSET0 |= 0x1 << 4; //第4号IO口置1
        }else if(usercmd == '0') //应用层写'0'进入
        {
                printk("set 0\n");
                *GPCLR0 |= 0x1 << 4; //第4号IO口置0
        }else
        {
                printk("set %c\n",usercmd);
        }

        return 0;
}

//内核链表里的结构体相关配置
static struct file_operations pin4_fops =
{
        .owner = THIS_MODULE,
        .open = pin4_open,
        .write = pin4_write,
};

//入口函数
int __init pin4_drv_init(void)
{
        int ret;
        printk("Driver installation succeeded\n");
        devno = MKDEV(major,minor); //创建设备号
        //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中
        ret = register_chrdev(major, module_name, &pin4_fops);

        pin4_class = class_create(THIS_MODULE, "myfirstdemo"); //让代码在/dev目录下自动生成设备
        pin4_class_dev = device_create(pin4_class, NULL, devno, NULL, module_name); //创建设备文件

        GPFSEL0 = (volatile unsigned int *)ioremap(0x3F200000, 4); //物理地址转换成虚拟地址,io口寄存器映射成普通内存单元进行访问
        GPSET0 = (volatile unsigned int *)ioremap(0x3F20001C, 4);
        GPCLR0 = (volatile unsigned int *)ioremap(0x3F200028, 4);

        return 0;
}

void __exit pin4_drv_exit(void)
{
        iounmap(GPFSEL0); //释放内存单元
        iounmap(GPSET0);
        iounmap(GPCLR0);

        device_destroy(pin4_class, devno);
        class_destroy(pin4_class);
        unregister_chrdev(major, module_name); //卸载驱动
}

module_init(pin4_drv_init); //入口
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

应用层代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
        int fd;
        char c = 0;

        fd = open("/dev/pin4",O_RDWR);
        if(fd < 0){
                printf("open failed\n");
                perror("reson");
        }else{
                printf("open success\n");
        }

        scanf("%c",&c);
        printf("data=%c\n",c);

        if(c == '1')
                write(fd, &c, 1);
        else if(c == '0')
                write(fd, &c, 1);

        close(fd);

        return 0;
}

转载请标明出处,谢谢
作者:星辰~念

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-26 12:16:06  更:2021-08-26 12:17:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 5:18:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码