1.地址
(1)总线地址:CPU能够访问内存的范围
现象:装了32位的win7系统,明明内存条8G,可是系统只能识别3.8G,装了64位,才能识别到8G
(2)物理地址:硬件的实际地址或绝对地址
(3)虚拟地址:逻辑(基于算法的地址(软件层面的地址,假))地址称为虚拟地址
MMU的作用:将物理地址转化为虚拟地址
2.寄存器
翻阅【树莓派博通BCM2835芯片手册】第6章(89页):
The GPIO has 41 registers. All accesses are assumed to be 32-bit.
Address为(总线地址)
3.寄存器类型:
(1)GPFSEL:GPIO Function Select Registers(功能选择寄存器)
翻阅【树莓派博通BCM2835芯片手册】第6章(91页):
(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
4.内核驱动代码:
#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:拷贝的数据的长度,单位是字节
返回值:如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数
void iounmap(volatile void __iomem *addr);
功能: 解除映射
addr: 需要解除映射的地址