树莓派 IO 口驱动

1.寄存器:

翻阅【树莓派博通BCM2835芯片手册】第6章(89页):
The GPIO has 41 registers. All accesses are assumed to be 32-bit.

Address(总线地址) FieldName Description Size Read/Write
0x 7E20 0000 GPFSEL0 GPIO Function Select 0 32 R/W
0x 7E20 0000 GPFSEL0 GPIO Function Select 0 32 R/W
0x 7E20 0004 GPFSEL1 GPIO Function Select 1 32 R/W
0x 7E20 0008 GPFSEL2 GPIO Function Select 2 32 R/W
0x 7E20 000C GPFSEL3 GPIO Function Select 3 32 R/W
0x 7E20 0010 GPFSEL4 GPIO Function Select 4 32 R/W
0x 7E20 0014 GPFSEL5 GPIO Function Select 5 32 R/W
0x 7E20 0018 - Reserved - -
0x 7E20 001C GPSET0 GPIO Pin Output Set 0 32 W
0x 7E20 0020 GPSET1 GPIO Pin Output Set 1 32 W
0x 7E20 0024 - Reserved - -
0x 7E20 0028 GPCLR0 GPIO Pin Output Clear 0 32 W
0x 7E20 002C GPCLR1 GPIO Pin Output Clear 1 32 W
0x 7E20 0030 - Reserved - -
0x 7E20 0034 GPLEV0 GPIO Pin Level 0 32 R
0x 7E20 0038 GPLEV1 GPIO Pin Level 1 32 R
0x 7E20 003C - Reserved - -
0x 7E20 0040 GPEDS0 GPIO Pin Event Detect Status 0 32 R/W
0x 7E20 0044 GPEDS1 GPIO Pin Event Detect Status 1 32 R/W
0x 7E20 0048 - Reserved - -
0x 7E20 004C GPREN0 GPIO Pin Rising Edge Detect Enable 0 32 R/W
0x 7E20 0050 GPREN1 GPIO Pin Rising Edge Detect Enable 1 32 R/W
0x 7E20 0054 - Reserved - -
0x 7E20 0058 GPFEN0 GPIO Pin Falling Edge Detect Enable 0 32 R/W
0x 7E20 005C GPFEN1 GPIO Pin Falling Edge Detect Enable 1 32 R/W
0x 7E20 0060 - Reserved - -
0x 7E20 0064 GPHEN0 GPIO Pin High Detect Enable 0 32 R/W
0x 7E20 0068 GPHEN1 GPIO Pin High Detect Enable 1 32 R/W
0x 7E20 006C - Reserved - -
0x 7E20 0070 GPLEN0 GPIO Pin Low Detect Enable 0 32 R/W
0x 7E20 0074 GPLEN1 GPIO Pin Low Detect Enable 1 32 R/W
0x 7E20 0078 - Reserved - -
0x 7E20 007C GPAREN0 GPIO Pin Async. Rising Edge Detect 0 32 R/W
0x 7E20 0080 GPAREN1 GPIO Pin Async. Rising Edge Detect 1 32 R/W
0x 7E20 0084 - Reserved - -
0x 7E20 0088 GPAFEN0 GPIO Pin Async. Falling Edge Detect 0 32 R/W
0x 7E20 008C GPAFEN1 GPIO Pin Async. Falling Edge Detect 1 32 R/W
0x 7E20 0090 - Reserved - -
0x 7E20 0094 GPPUD GPIO Pin Pull-up/down Enable 32 R/W
0x 7E20 0098 GPPUDCLK0 GPIO Pin Pull-up/down Enable Clock 0 32 R/W
0x 7E20 009C GPPUDCLK1 GPIO Pin Pull-up/down Enable Clock 1 32 R/W
0x 7E20 00A0 - Reserved - -
0x 7E20 00B0 - Test 4 R/W

2.寄存器类型:

(1)GPFSEL:GPIO Function Select Registers(功能选择寄存器)

翻阅【树莓派博通BCM2835芯片手册】第6章(91页):
在这里插入图片描述
在这里插入图片描述
此处 FSEL0 ~ FSEL9 表示引脚 0 ~ 9
此处表示操控 FSEL0 ~ FSEL9(引脚 0 ~ 9)则要操控寄存器 GPFSEL0 的 bit(s)0 ~ bit(s)29,按照手册以此类推

(2)GPSET:GPIO Pin Output Set Registers(输出设置寄存器)

翻阅【树莓派博通BCM2835芯片手册】第6章(95页):
在这里插入图片描述在这里插入图片描述
操控 SETn(引脚n)则要操控寄存器 GPSET 0(pin0–pin31) /GPSET 1(pin32–53) 的 bit(s) 0 ~ bit(s)31

(3)GPCLR:GPIO Pin Output Clear Registers(输出清除寄存器)

翻阅【树莓派博通BCM2835芯片手册】第6章(95页):
在这里插入图片描述
在这里插入图片描述
操控 CLRn(引脚n)则要操控寄存器 GPCLR 0(pin0–pin31) /GPCLR 1(pin32–53) 的 bit(s) 0 ~ bit(s)31

3.内核驱动代码:

#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 = 231;
static int minor = 0;
static char* module_name = "pin4";

volatile unsigned int* GPFSEL0 = NULL;		//定义寄存器虚拟地址
volatile unsigned int* GPSET0  = NULL;		//定义寄存器虚拟地址
volatile unsigned int* GPCLR0  = NULL;		//定义寄存器虚拟地址
/*volatile:指令关键字,确保本条指令不会因编译器的优化而省略(避免定义的地址被编译器更改),
  且要求每次直接读值(保证数据时效性)*/


static int pin4_open(struct inode* inode, struct file* file)
{
    
    
    printk("pin4_open\n");

	*GPFSEL0 &= ~(0x6 << 12);		//将 GPFSEL0寄存器的 bit(s)14 和 bit(s)13 置 0
	*GPFSEL0 |= (0x1 << 12);		//将 GPFSEL0寄存器的 bit(s)12 置 1
	//配置 pin4 为输出引脚,即将 GPFSEL0寄存器 的 bit(s)14 ~ bit(s)12 配置为 001

    return 0;
}


static int pin4_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
{
    
    
    printk("pin4_read\n");
    return 0;
}


static ssize_t pin4_write(struct file* file, const char __user* buf, size_t count, loff_t* ppos)
{
    
    
	int userCmd;
    printk("pin4_write\n");
    
    copy_from_user(&userCmd,buf,count);		//获取用户空间的数据
    if(userCmd == 1){
    
    
    	printk("set 1\n");
        *GPSET0 |= 0x1 << 4;		//获取到指令1,pin4 输出高电平
    }else if(userCmd == 0){
    
    
    	printk("set 0\n");
        *GPCLR0 |= 0x1 << 4;		//获取到指令0,pin4 输出低电平
    }else{
    
    
        printk("invalid instruction\n");		//无效指令
    }
    
    return 0;
}


static struct file_operations pin4_fops = {
    
    
    .owner = THIS_MODULE,
    .open = pin4_open,
    .write = pin4_write,
    .read = pin4_read,
};


int __init pin4_drv_init(void)
{
    
    

    int ret;
    printk("insmod driver pin4 success!\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);		//将寄存器的物理地址映射为虚拟地址
/*IO空间的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的,在此基础上进行
  Linux系统的MMU内存虚拟化管理,映射到虚拟地址上*/
//查看芯片手册:GPFSEL0寄存器偏移量为0x00,GPSET0寄存器偏移量为0x1C,GPCLR0寄存器偏移量为0x28

    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 <io.h>
void *ioremap(unsigned long phys_addr, unsigned long size);

功能:      将一个IO的物理地址映射到内核的虚拟地址
phys_addr: 要映射的物理地址
size:      要映射的空间的大小,单位是字节
#include <linux/uaccess.h>
unsigned long copy_from_user(void * to, const void __user * from, unsigned long n);

功能:将用户空间的数据拷贝到内核空间
to:内核空间存放地址
from:用户空间的数据源地址
n:拷贝的数据的长度,单位是字节
返回值:如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数
#include <linux/uaccess.h>
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);

功能:将内核空间的数据拷贝到用户空间
to:用户空间存放地址
from:内核空间的数据源地址
n:拷贝的数据的长度,单位是字节
返回值:如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数
void iounmap(volatile void __iomem *addr);

功能:	解除映射
addr:	需要解除映射的地址

猜你喜欢

转载自blog.csdn.net/lcx1837/article/details/113743666