基于博通bcm2835芯片手册进行简单的树莓派引脚驱动

目录

1.配置寄存器

 2.设置寄存器的地址

3.编写驱动代码 

 4.编写应用层代码

 5.编译

6.测试 


1.配置寄存器

我们要进行树莓派引脚的驱动就要对树莓派的引脚进行一些配置,比如我想把树莓派的某个 I/O 引脚置为高电平,那么我就需要对该引脚相关的寄存器进行配置:

  •  这里以pin4为例,要把树莓派pin4 引脚配置成输出引脚需要配置 GPFSEL 这个寄存器(每个寄存器都是32位)

 

  而pin4属于 GPFSEL0 这个寄存器所以需要把 GPFSEL0 寄存器的14-12 位配置成 001 

*GPFSEL0 &=~(0x6<<12);      把bit 13 -14 配置成0

*GPFSEL0 |=(0x1<<12);          把第12位配置成1

  • 配置好输出引脚后,把输出引脚置为高电平需要配置 GPSET 这个寄存器

而pin4属于 GPSET0 这个寄存器所以需要把 GPSET0 寄存器的第4位配置成 1 

*GPSET0 |=0x1<<4;    

  •  把输出引脚置为低电平需要配置 GPCLR 这个寄存器

而pin4属于 GPCLR​​​​​​​0 这个寄存器所以需要把 GPCLR​​​​​​​0 寄存器的第4位配置成 1 

*GPSET0 |=0x1<<4;    

 2.设置寄存器的地址

由于bcm2835芯片手册给的地址是总线地址,但是上层并不能对这个地址进行访问

而我们在编写树莓派驱动程序的时候,IO空间的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的。参考图中寄存器的偏移量得出我们选用的寄存器的物理地址是:

GPFSEL0             0x3f200000
GPSET0               0x3f20001c
GPCLR0               0x3f200028

然后在这个基础上进行Linux系统的MMU内存虚拟化管理,映射到虚拟地址上,这里用到了一个​​​​​​​ioremap函数将物理地址转成虚拟地址,这样上层就能访问的到了。

 GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);
 GPSET0 =(volatile unsigned int *)ioremap(0x3f20001C,4);
 GPCLR0 =(volatile unsigned int *)ioremap(0x3f200028,4);

3.编写驱动代码 


#include <linux/fs.h>		 //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>	 //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件


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不会因编译器的优化而省略,每次直接读值
volatile unsigned int* GPSET0=NULL;
volatile unsigned int* GPCLR0=NULL;

//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数和printf类似
    *GPFSEL0 &=~(0x6<<12);      //0x6  0110左移12位  取反后1001与上 结果为把bit13-14配置成0
    *GPFSEL0 |=(0x1<<12);       //由于不确定bit 12一定是1,所以还需把bit 12 配置成1
  
    return 0;
}

//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    int userCmd;
    printk("pin4_write\n");  //内核的打印函数和printf类似
    copy_from_user(&userCmd,buf,count);   //从上层获取函数的值第一个参数是一个char类型的指针const char __user *buf,用int也行

      if(userCmd==1)

      {
         printk("set 1\n");
         *GPSET0 |=0x1<<4;         //把第四引脚置一
      }else if(userCmd==0)
      {
         printk("set 0\n");

         *GPCLR0 |=0x1<<4;          //把第四引脚清零

      }else
      {
         printk("undo\n");
      }
    return 0;
}

static struct file_operations pin4_fops = {

    .owner = THIS_MODULE,
    .open  = pin4_open,
    .write = pin4_write,
};

int __init pin4_drv_init(void)   
{

    int ret;
    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo");  //让代码在dev下自动生成设备
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件

    GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);   //第一个参数真正的物理地址,第二个参数映射的大小  一个寄存器4个字节 4*8=32 bit
    GPSET0=(volatile unsigned int*)ioremap(0x3f20001c,4);   
    GPCLR0=(volatile unsigned int*)ioremap(0x3f200028,4);

    return 0;
}

void __exit pin4_drv_exit(void)
{
    iounmap(GPFSEL0);    //取消 ioremap 的映射
    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");

 4.编写应用层代码

#include  <sys/types.h>
#include  <sys/stat.h>
#include  <fcntl.h>
#include  <stdio.h>

int main()
{
	int fd;
	int cmd;
	fd=open("/dev/pin4",O_RDWR);
	if(fd < 0){
        printf("open failed\n");
        perror("reason:");
    }else{
        printf("succeed\n");
    }
	printf("输入 1/0 控制 pin4 输出");
	scnaf("%d",cmd);
	printf("%d\n",cmd);
	
	if(cmd==1){
		write(fd,&cmd,1);
	}else if(cmd == 0){
		write(fd,&cmd,1);
	}
	

	return 0;
}

 5.编译

  • 把写好的驱动代码(pin4_driver.c)放到源码树目录的 /drivers/char 目录下
  • 修改Makefile (放在那个目录就在那个目录修改Makefile)

参考:树莓派驱动编译

6.测试 

运行应用层代码,按下1时pin4引脚输出高电平

 按下0时pin4引脚输出低电平

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值