操控树莓派IO口的驱动代码编写
要对I/O口操作,首先得把其对应的物理地址在代码中用变量表示出来,但内核和上层代码访问的都是虚拟地址,所以在驱动代码里不能直接写物理地址,需要把物理地址转化为虚拟地址。
先定义变量
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
volatile //特征修饰符 作为指令关键字
作用:
1.确保指令不会因编译器的优化而省略。(编译器自认为人为给的数据不行,可能会
被编译器给优化掉)
2.要求每次直接从寄存器读值。(寄存器随着硬件的执行可能会改变寄存器里面的数
据,如果没有volatile修饰,读取的数据是原先数据的一个备份,是个老数据,数据
时效性就很差。)
unsigned
作用:
整数分为有符号与无符号,如果要把类型声明为无符号数就需要使用unsigned来修饰
(除char以外的数据类型中,默认情况下声明的整型变量都是有符号的类型),两者
区别在于,有符号的数最高位的数作为符号位,无符号最高位作为值。如两个字节的
short,有符号表示范围是-32768~32767,无符号范围是0~65535.
查看树莓派型号
cat /proc/cupinfo
此处指令看到的型号不是树莓派cpu真的型号,其真正型号应该是BCM2837,也就是IO在物理地址上的基址应该是0x3F000000。
通过查看芯片手册发GPFSEL0寄存器VC CPU总线地址是0x7E200000,
相对基址(0x7E000000)偏移0x00200000,那么ARM物理地址也是偏移这
么多,所以GPIO的物理地址应该是从0x3f200000 开始 。
物理地址转化为虚拟地址(由芯片手册总线地址GPFSEL0、GPSET0、GPCLR0这三个地址的偏移量推出其物理地址)
GPFSEL0 = volatile(unsigned int *)ioremap(0x3F200000,4);//ioremap函数将物理地址转换成虚拟地址,io口寄存器映射成普通内存单元进行访问
GPSET0 = volatile(unsigned int *)ioremap(0x3F20001C,4);
GPCLR0 = volatile(unsigned int *)ioremap(0x3F200028,4);
把上面三条语句写入函数:int __init pin4_drv_init(void) //真实驱动入口
如此就实现了物理地址转化成虚拟地址。(注:退出驱动时,用iounmap(*GP)函数解除地址映射);
此处以pin4引脚为例,要把pin4引脚设置为输出引脚,根据芯片手册内容需要配置GPFSEL0的14-12位(位置由0开始)为001。
但要注意的是在改变14-12位置上的值时,其它位置上的内容不能变,不然将会影响其它的引脚。
设置pin4引脚为输出引脚
//把pin4变为输出引脚,配置14-12位置(由位置0开始)的内容为001
*GPFSEL0 &= ~(0x6<<12);//6的二进制是110左移12位后,110对应的位置是14-12,取反后110变为001其它位为1,和GPFSEL0进行与运算后就实现只有14、13位改变为0
*GPFSEL0