目录:
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: 需要解除映射的地址