树莓派IO口驱动

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值