首先理解树莓派基于Linux内核驱动流程和框架 树莓派基于Linux内核驱动开发详解
一、树莓派基于Linux内核驱动GPIO前提工作
-
树莓派能够执行自己做好的设备文件(设备文件由驱动文件加载得到,驱动文件由设备驱动文件编译得到),那我们就可以往设备驱动文件里加入对GPIO操作,实现点灯,PWM,I2C,SPI等等 -
对GPIO操作,那我们就需要有芯片手册、板子的电路图。对树莓派来讲,芯片手册已经写的足够详细了 所以没用到电路图 树莓派文档 -
我们要点灯,所以要看芯片手册的点灯寄存器,有 GPFSEL0寄存器 (设置GPIO模式) GPSET0寄存器 (GPIO置1) GPCLR0寄存器 (GPIO清0) -
如何才能操作这些寄存器 总线地址 :CPU能够访问内存的范围(0x00000000——0xFFFFFFFF) 物理地址 :也称硬件的实际地址,物理地址是指处理器芯片发出,来进行地址空间寻址的地址,它与处理器地址引脚上发出的电信号相对应 虚拟地址 :程序所操作地址都是虚拟地址,在嵌入式Linux中,可以使用mmap函数或者ioremap函数将ARM物理地址映射成虚拟地址,就等同于直接操作GPIO硬件地址了。重要一点,在程序上需要把物理地址映射成虚拟地址才可以实现对硬件操作或对寄存器操作
- 由上图见IO的总线基地址为0x7E000000,通过MMU映射装载出的物理基地址为0x20000000(注意:芯片型号不同,映射装载出的物理基地址也不同,BCM2837芯片映射装载出的物理基地址为0x3F000000)
- 以BCM2837为例得到
总线地址 –>物理地址 GPFSEL0寄存器0x7E000000 --> 3F000000 GPSET0寄存器0x7E20001C --> 3F20001C GPCLR0寄存器0x7E200028 --> 3F200028 - 由上面的虚拟地址解释,程序上这样写
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>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>
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;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
static int pin4_open(struct inode *inode, struct file *file)
{
printk("pin4_open\n");
*GPFSEL0 &= ~(0x6 << 12);
*GPFSEL0 |= (0x1 << 12);
return 0;
}
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");
copy_from_user(&usercmd, buf, count);
if(usercmd == '1')
{
printk("set 1\n");
*GPSET0 |= 0x1 << 4;
}else if(usercmd == '0')
{
printk("set 0\n");
*GPCLR0 |= 0x1 << 4;
}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");
pin4_class_dev = device_create(pin4_class, NULL, devno, NULL, module_name);
GPFSEL0 = (volatile unsigned int *)ioremap(0x3F200000, 4);
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;
}
转载请标明出处,谢谢 作者:星辰~念
|