树莓派 IO 驱动

1.寄存器:

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

Address(总线地址)FieldNameDescriptionSizeRead/Write
0x 7E20 0000GPFSEL0GPIO Function Select 032R/W
0x 7E20 0000GPFSEL0GPIO Function Select 032R/W
0x 7E20 0004GPFSEL1GPIO Function Select 132R/W
0x 7E20 0008GPFSEL2GPIO Function Select 232R/W
0x 7E20 000CGPFSEL3GPIO Function Select 332R/W
0x 7E20 0010GPFSEL4GPIO Function Select 432R/W
0x 7E20 0014GPFSEL5GPIO Function Select 532R/W
0x 7E20 0018-Reserved--
0x 7E20 001CGPSET0GPIO Pin Output Set 032W
0x 7E20 0020GPSET1GPIO Pin Output Set 132W
0x 7E20 0024-Reserved--
0x 7E20 0028GPCLR0GPIO Pin Output Clear 032W
0x 7E20 002CGPCLR1GPIO Pin Output Clear 132W
0x 7E20 0030-Reserved--
0x 7E20 0034GPLEV0GPIO Pin Level 032R
0x 7E20 0038GPLEV1GPIO Pin Level 132R
0x 7E20 003C-Reserved--
0x 7E20 0040GPEDS0GPIO Pin Event Detect Status 032R/W
0x 7E20 0044GPEDS1GPIO Pin Event Detect Status 132R/W
0x 7E20 0048-Reserved--
0x 7E20 004CGPREN0GPIO Pin Rising Edge Detect Enable 032R/W
0x 7E20 0050GPREN1GPIO Pin Rising Edge Detect Enable 132R/W
0x 7E20 0054-Reserved--
0x 7E20 0058GPFEN0GPIO Pin Falling Edge Detect Enable 032R/W
0x 7E20 005CGPFEN1GPIO Pin Falling Edge Detect Enable 132R/W
0x 7E20 0060-Reserved--
0x 7E20 0064GPHEN0GPIO Pin High Detect Enable 032R/W
0x 7E20 0068GPHEN1GPIO Pin High Detect Enable 132R/W
0x 7E20 006C-Reserved--
0x 7E20 0070GPLEN0GPIO Pin Low Detect Enable 032R/W
0x 7E20 0074GPLEN1GPIO Pin Low Detect Enable 132R/W
0x 7E20 0078-Reserved--
0x 7E20 007CGPAREN0GPIO Pin Async. Rising Edge Detect 032R/W
0x 7E20 0080GPAREN1GPIO Pin Async. Rising Edge Detect 132R/W
0x 7E20 0084-Reserved--
0x 7E20 0088GPAFEN0GPIO Pin Async. Falling Edge Detect 032R/W
0x 7E20 008CGPAFEN1GPIO Pin Async. Falling Edge Detect 132R/W
0x 7E20 0090-Reserved--
0x 7E20 0094GPPUDGPIO Pin Pull-up/down Enable32R/W
0x 7E20 0098GPPUDCLK0GPIO Pin Pull-up/down Enable Clock 032R/W
0x 7E20 009CGPPUDCLK1GPIO Pin Pull-up/down Enable Clock 132R/W
0x 7E20 00A0-Reserved--
0x 7E20 00B0-Test4R/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:	需要解除映射的地址
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~莘莘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值