Linux驱动认知

为什么学习驱动

写驱动是为了不调库,比如树莓派厂家提供了wiringPi库函数,集成了诸多函数,学习驱动就是为了在另一块板子上即使没有wiringPi我们只要拿到了原理图、芯片手册我们就能驱动IO等。

主设备号和次设备号

Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管路这些设备,系统为设备编了号,每个设备又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如硬盘的主设备号是3。

一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和此设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的个设备。例如一个嵌入式系统,有两个LED指示灯,LED指示灯需要独立打开或者关闭。那么,可以写一个LED灯的字符设备驱动程序,可以将其主设备号注册成5号设备,次设备号分别为1和2.这里,次设备号就分别表示两个LED灯。

驱动实现过程

当用户层调用open,write等函数时 (例如:open(“/dev/pin4”, O_RDWR)),系统发生一次软中断,触发sys_call(系统调用处理入口 内核态)(中断号是0x80),接下来根据设备名找到设备号找到虚拟文件系统的sys_open,sys_open会找到引脚四驱动程序里的open函数,我们在引脚四里面肯定是进行寄存器操作。

如何编写驱动(GPIO)(最精简的方式)

1.在驱动中定义出寄存器

volatile unsigned int* GPFSEL0 = NULL; //volatile:防止编译器优化它
volatile unsigned int* GPSET0  = NULL; //先定义为空指针, 在pin4_drv_init();里面再将物
									   //理地址转化为虚拟地址,IO口寄存器映射成普通内存单元进行访问
volatile unsigned int* GPCLR0  = NULL; 
int __init pin4_drv_init(void)   
{

    int ret;
    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo");
    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); 
	//我们在编写驱动程序的时候,IO口空间的起始地址是0x3f000000,加上
	//GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的,
	//然后在这个基础上进行linux系统的MMU内存虚拟化管理,映射到虚拟地址上。
    return 0;
}

2.查看芯片手册,找到GPIO相关的寄存器

我们查手册,找到GPIO所对应的地方
在这里插入图片描述
GPIO Function Select Registers (GPFSELn)
(GPIO函数选择寄存器(GPFSELn))

GPIO有41个寄存器。所有的访问都假定是32位的。
在这里插入图片描述
功能寄存器:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述(还有FSEL30~FSEL53,就不一一截图)

以FSEL9为例描述了要选择的口的功能

FSEL0~FSEL9为一族,FSEL10 ~ FSEL19为一族,以此类推

GPFSEL0-----GPIO Function Select 0 功能选择 输入/输出
GPSET0------ GPIO Pin Output Set 1 输出1
GPCLR0------ GPIO Pin Output Clear 0 清零

(我们驱动pin4为输出)
所以功能选择寄存器FSEL4,14-12对应为001

//对应内核驱动中pin4_open中GPFSEL0的配置
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数和printf类似
    //配置pin4为输出引脚, bit12-14配置为001
    *GPFSEL0 &= ~(0x6 << 12); //把bit13 bit14引脚配置为0
    *GPFSEL0 |= (0x6 << 12); //把13 14引脚配置为0 

    return 0;
}
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    int usercmd;

    printfk("pin4_write\n");

    copy_from_user(&usercmd, buf, count); //获取上层write函数的值
    printk("get value\n");
    if (usercmd == 1){
        *GPSET0 |= 0x1 << 4; //置一寄存器配置
    }
    else if (usercmd == 0){
        *GPCLR0 |= 0x1 << 4;//清零寄存器配置
    }
    else {
        printfk("undo\n");
    }

	printk("pin4_write\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值