基于树莓派博通BCM2835芯片手册导读写编简单引脚驱动代码编译和测试(树莓派)

编写引脚驱动代码

这边写的是17引脚的驱动代码代码(IO口控制的代码在下面),这边只是简单的代码
驱动代码

#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 device声明
#include <linux/uaccess.h>              //copy_from_user的头文件
#include <linux/types.h>                //设备号 dev_t 类型声明
#include <asm/io.h>                             // ioremap iounmap 的头文件

static struct class  *pin17_class;
static struct device *pin17_class_dev;

static dev_t devno;					//设备号
static int major = 232;				//主设备号
static int minor = 0;				//次设备号
static char *module_name = "pin17";	//模块名字





volatile unsigned int *GPFSEL0 = NULL;
volatile unsigned int *GPSET0  = NULL;
volatile unsigned int *GPCLR0  = NULL;

static int pin17_read(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	printk("pin17_read\n");//内核打印函数
	return 0;
}
//pin17_open函数
static int pin17_open(struct inod *inod,struct file *file)
{
	printk("pin17_open\n");



	/**GPFSEL0 &= ~(0x6 << 12);
	*GPFSEL0 |= (0x6 << 12);*/
	return 0;

}

//pin17_write函数
static int pin17_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	//int userCmd;
	printk("pin17_write\n");

	/*copy_from_user(&userCmd,buf,count);//获取上层write函数的值

	//根据值来操作IO口,高电平或者低电平
	if(userCmd == 1){
		printk("set 1\n");
		*GPSET0 |= 0x1 << 17
	}*/
	return 0;

}

static struct file_operations pin17_fops = {
	.owner = THIS_MODULE,
	.open  = pin17_open,
	.write = pin17_write,
	.read  = pin17_read,

};

int __init pin17_drv_init(void)//真实驱动入口
{
	int ret;
	printk("insmod driver pin17 success\n");
	devno = MKDEV(major,minor);//创建设备号
	ret = register_chrdev(major,module_name,&pin17_fops);//注册驱动,告诉内核把这个驱动加到内核链表中

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

	return 0;

}

void __exit pin17_drv_exit(void)
{
	device_destroy(pin17_class,devno);
	class_destroy(pin17_class);
	unregister_chrdev(major,module_name);//卸载驱动
}

module_init(pin17_drv_init);//入口,内核加载驱动的时候,这个宏会被调用
module_exit(pin17_drv_exit);
MODULE_LICENSE("GPL v2");//模块的许可证声明

测试代码

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

int main(int argc, char const *argv[])
{
	int fd;
	fd = open("/dev/pin17",O_RDWR);
	if(fd < 0){
		printf("open failed\n");
		perror("open:");
	}else{
		printf("open success\n");
	}
	fd = write(fd,"1",1);
	return 0;
}

编译代码

在linux内核里面编译放到树莓派上去运行

  • 打开Linux内核源码目录,进入到驱动的目录
    在这里插入图片描述

  • 选择子目录 char,并把刚刚编写的驱动代码拷进来(pin17Driver2.c)
    在这里插入图片描述

  • 为了让工程编译的时候编译上面pin17Driver2.c的代码,我们需要修改Makefile,
    我们这里将他编译成模块的方式

 vi Makefile

在这里插入图片描述
改成obj-m,驱动程序的文件名字改成叫做pin17Driver2.o,这样Makefile就配置完成了
在这里插入图片描述

  • 接着我们要进行模块编译
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules

这里就算是编译通过
在这里插入图片描述

  • 接下来来我们就拷贝到树莓派上去(pin17Driver2.ko和pin17Test)
scp drivers/char/pin17Driver2.ko pi@(这里是你的树莓派IP地址):/home/pi

把pin17Test.c也编译一下放松到树莓派

arm-linux-gnueabihf-gcc pin17Test.c -o pin17Test
scp pin17Test pi@192.168.0.108:/home/pi

在这里插入图片描述

在树莓派测试

  • 加载内核驱动
sudo insmod pin17Driver2.ko

检查dev底下是否生成pin17
在这里插入图片描述

  • 来运行测试代码
    在这里插入图片描述
    我们可以下发现运行失败了,问题是:没有权限去打开这个设备,所以我们要设置权限
    在这里插入图片描述
    ****检查一下dmesg是打印内核的printk
    在这里插入图片描述
    卸载驱动
sudo rmmod pin17Driver2

IO操控代码编程

选择查看IO口这一块内容
在这里插入图片描述

寄存器介绍
寄存器视图
GPIO有41个寄存器。所有访问都假定为32位。
在这里插入图片描述
由于我接下来我是编写树莓派17号引脚,所以我选择功能寄存器1
要区分寄存器的引脚和wpi的引脚
在这里插入图片描述
这个是查看各个引脚的网址
https://pinout.xyz/pinout/pin12_gpio18
在这里插入图片描述
在这里插入图片描述
GPFSEL1 GPIO Function Select 1 :GPFSEL1 GPIO功能选择1,功能选择输出/输入
在这里插入图片描述

GPSET0 GPIO Pin Output Set 0 :GPSET0 GPIO引脚输出设置0
在这里插入图片描述

GPCLR0 GPIO Pin Output Clear 0 :GPCLR0 GPIO引脚输出清除0
在这里插入图片描述
编写代码
我们编写寄存器地址的时候会出现问题,不能直接选择下面这个
在这里插入图片描述
我们在编写驱动程序的时候,IO空间的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,映射到虚拟地址上。
在这里插入图片描述
从上面在这个图得出
GPFSEL0 0x3f200000
GPSET0 0x3f20001c
GPCLR0 0x3f200028

具体代码

#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 device声明
#include <linux/uaccess.h>              //copy_from_user的头文件
#include <linux/types.h>                //设备号 dev_t 类型声明
#include <asm/io.h>                             // ioremap iounmap 的头文件

static struct class  *pin17_class;
static struct device *pin17_class_dev;

static dev_t devno;					//设备号
static int major = 232;				//主设备号
static int minor = 0;				//次设备号
static char *module_name = "pin17";	//模块名字




//定义寄存器
volatile unsigned int *GPFSEL1 = NULL;
volatile unsigned int *GPSET0  = NULL;
volatile unsigned int *GPCLR0  = NULL;

static int pin17_read(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	printk("pin17_read\n");//内核打印函数
	return 0;
}
//pin17_open函数
static int pin17_open(struct inode *inode,struct file *file)
{
	printk("pin17_open\n");

	//配置pin17引脚为输出引脚,把bit 23-21位配置成001就行了,23-0,22-0,21-1

	*GPFSEL1 &= ~(0x6 << 21);//把bit24 和bit23配置成0,~是取反的意思,&是按位与,<< 21是左移21位
	*GPFSEL1 |= (0x1 << 21);//把bit21配置成1
	return 0;

}

//pin17_write函数
static ssize_t pin17_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	int userCmd;
	printk("pin17_write\n");

	copy_from_user(&userCmd,buf,count);//获取上层write函数的值

	//根据值来操作IO口,高电平或者低电平
	if(userCmd == 1){
		printk("set 1\n");
		*GPSET0 |= 0x1 << 17;//让GPSET0寄存器工作,把bit17设置陈高电平
	}else if(userCmd == 0){
		printk("set 0\n");
		*GPCLR0 |= 0x1 << 17;//让GPCLR0清零寄存器工作,把bit17设置陈低电平
	}else{
		printk("undo\n");
	}
	return 0;

}

static struct file_operations pin17_fops = {
	.owner = THIS_MODULE,
	.open  = pin17_open,
	.write = pin17_write,
	.read  = pin17_read,

};

int __init pin17_drv_init(void)//真实驱动入口
{
	int ret;
	printk("insmod driver pin17 success\n");
	devno = MKDEV(major,minor);//创建设备号
	ret = register_chrdev(major,module_name,&pin17_fops);//注册驱动,告诉内核把这个驱动加到内核链表中

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

	//寄存器的物理地址转换
	GPFSEL1 = (volatile unsigned int *)ioremap(0x3f200004,4);//物理地址转换为虚拟地址,io口寄存器映射成普通内存单元进行访问
	GPSET0 	= (volatile unsigned int *)ioremap(0x3f20001C,4);
	GPCLR0 	= (volatile unsigned int *)ioremap(0x3f200028,4);
	return 0;

}

void __exit pin17_drv_exit(void)
{
	iounmap(GPFSEL1);
	iounmap(GPSET0);
	iounmap(GPCLR0);
	device_destroy(pin17_class,devno);
	class_destroy(pin17_class);
	unregister_chrdev(major,module_name);//卸载驱动
}

module_init(pin17_drv_init);//入口,内核加载驱动的时候,这个宏会被调用
module_exit(pin17_drv_exit);
MODULE_LICENSE("GPL v2");//模块的许可证声明

其中这里要注意

	//寄存器的物理地址转换
	GPFSEL1 = (volatile unsigned int *)ioremap(0x3f200004,4);//物理地址转换为虚拟地址,io口寄存器映射成普通内存单元进行访问
	GPSET0 	= (volatile unsigned int *)ioremap(0x3f20001C,4);
	GPCLR0 	= (volatile unsigned int *)ioremap(0x3f200028,4);

这里我们得到的是物理地址是不可操作的,我们需要转化成虚拟地址借用下面的函数

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);

ioremap宏定义在asm/io.h内:

#define ioremap(cookie,size)           __ioremap(cookie,size,0)

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);

参数:

phys_addr:要映射的起始的IO地址

size:要映射的空间的大小

flags:要映射的IO空间和权限有关的标志

该函数返回映射后的内核虚拟地址(3G-4G). 接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源。
将写好的代码放入内核去编译驱动,跟上面的一样操作
应用层测试代码

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


int main(int argc, char const *argv[])
{
	int fd;
	int cmd;
	int data;
	fd = open("/dev/pin17",O_RDWR);
	if(fd < 0){
		printf("open failed\n");
		perror("open:");
	}else{
		printf("open success\n");
	}
	
	printf("请输入指令0或1(0低电平,1高电平)\n");
	scanf("%d",&cmd);
	if(cmd == 1){
		data = 1;
	}else if(cmd == 0){
			data = 0;
	}else{
			exit(0);
		}
	printf("data = %d\n",data);
	fd = write(fd,&data,1);
	
	return 0;
}

编译加测试

在树莓派上装载驱动的步骤和上面的都一样,直接跳到结果
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值