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

2023-11-17

编写引脚驱动代码

这边写的是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;
}

编译加测试

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

在这里插入图片描述

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • 为什么 Linux 原始套接字的 RX 环大小限制为 4GB?

    背景 我试图mmap 我的原始套接字的 RX 环形缓冲区64 bitLinux 应用程序 我的环由 4096 个块组成 每个块大小为 1MB 总共 4GB 请注意 每个 1MB 块中可以有许多帧 如果您好奇 请参阅此文档了解背景信息 htt
  • 为什么 Linux 没有 DirectX API?

    在考虑现代显卡的 Windows 系统上 DirectX API 的驱动程序端实现时 我想知道为什么此实现在非 Windows 系统 尤其是 Linux 上不可用 由于明显缺乏此功能 我只能假设有一个我无视的充分理由 但在我的原始理解中 我
  • 调用 printf 系统子例程在汇编代码中输出整数错误[重复]

    这个问题在这里已经有答案了 来回 在windows7控制台窗口中运行gcc s2 asm 然后生成一个exe文件 运行a exe 然后崩溃 为什么 s2 asm 代码由以下源代码生成 int m m 1 iprint m s2 asm请参考
  • 在 Linux 上以编程方式设置 DNS 名称服务器

    我希望能够通过我的 C C 程序为 Linux 上的 DNS 名称服务器添加 IP 地址 我在一个带有只读 etc resolv conf 的嵌入式平台上 这意味着我不能简单地将 nameserver xxx xxx xxx xxx 行添加
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • linux-x64 二进制文件无法在 linuxmusl-x64 平台上使用错误

    我正在安装Sharp用于使用 package json 的 Nodejs 项目的 docker 映像上的映像压缩包 当我创建容器时 我收到有关 Sharp 包的以下错误 app node modules sharp lib libvips
  • 执行“minikube start”命令时出现问题

    malik malik minikube start minikube v1 12 0 on Ubuntu 18 04 Using the docker driver based on existing profile Starting c
  • 从 ttyUSB0 写入和读取,无法得到响应

    我对 Linux tty 不太有经验 我的环境是带有丰富 USB 串行的 Raspbian 什么有效 stty F dev ttyUSB0 38400 cu l dev ttyUSB0 s 38400 cu to dev ttyUSB0作品
  • 如何获取 (Linux) 机器的 IP 地址?

    这个问题和之前问的几乎一样如何获取本地计算机的IP地址 https stackoverflow com questions 122208 get the ip address of local computer 问题 但是我需要找到一个的I
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • 如何让R使用所有处理器?

    我有一台运行 Windows XP 的四核笔记本电脑 但查看任务管理器 R 似乎一次只使用一个处理器 如何让 R 使用全部四个处理器并加速我的 R 程序 我有一个基本系统 我使用它在 for 循环上并行化我的程序 一旦您了解需要做什么 此方
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 如何使用Android获取Linux内核的版本?

    如何在 Android 应用程序中获取 Linux 内核的版本 不是 100 确定 但我认为调用 uname r 需要 root 访问权限 无论如何 有一种不太肮脏的方法可以做到这一点 那就是 System getProperty os v
  • 尽管我已在 python ctypes 中设置了信号处理程序,但并未调用它

    我尝试过使用 sigaction 和 ctypes 设置信号处理程序 我知道它可以与python中的信号模块一起使用 但我想尝试学习 当我向该进程发送 SIGTERM 时 但它没有调用我设置的处理程序 只打印 终止 为什么它不调用处理程序
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的

随机推荐

  • 魔方机器人之下位机编程------下位机完整程序

    头文件包含 include Includes h 总头文件 在此添加全局变量定义 uint8 msg 14 Hello World void PWM Init void PWM0 上侧旋转舵机 PWME PWME0 0x00 Disable
  • 查找恢复密钥

    登陆自己的微软账号可查看恢复密钥 点击以下链接查找恢复密钥 https account microsoft com devices recoverykey 根据密钥ID 输入对应的恢复密钥
  • win10蓝牙无法连接,可以尝试在此Windows设备上打开蓝牙

    win10蓝牙无法连接 可以尝试在此Windows设备上打开蓝牙 笔记本右下角蓝牙图标消失不见 操作步骤 1 首先在打开电脑中 按下 Win R 打开运行窗口输入 services msc 并进入 2 2 打开服务列表后 不断的向下翻 找到
  • 【华为OD机试真题】对称字符串(python)100%通过率 超详细代码注释 代码解读

    华为OD机试真题 2022 2023 真题目录 点这里 华为OD机试真题 信号发射和接收 试读 点这里 华为OD机试真题 租车骑绿道 试读 点这里 对称字符串 时间限制 1s空间限制 256MB限定语言 不限 题目描述 对称就是最大的美学
  • 最长公共上升子序列(LCIS)

    前置知识 LCS LIS 注意 刚开始看这个问题的时候 第一反应是先求出LCS再求出LCS的LIS 事实上这是有问题的 我们并不能保证这么求出的LCIS是最长的 比如下面这个例子 Example a 7 1 5 6 4 2 7 b 7 1
  • 【python + opencv + pytorch】车牌提取、分割、识别 pro版

    老规矩 先看最后成果图 如果想要全部工程 文章最后我会把github链接放上 1 分割车牌 2 分割字符 3 识别字符 最终识别的车牌号码是 浙F99999 整个车牌识别分五步 1 一个分割车牌的语义分割模型 2 用训练好DeepLab V
  • 复旦微单片机FM33LG系列之GPIO操作(FL库)

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 一 引用文件 二 快速IO操作指南 1 GPIO位输出高电平 2 GPIO位输出低电平 3 GPIO位输出电平翻转 4 GPIO端口8位并口输出 5 GPIO端口1
  • 数据结构之快速排序算法

    文章目录 快速排序的思想 快速排序的递归实现 快速排序的非递归实现 快速排序的思想 设置两个变量i j 排序开始的时候 令i 0 j length 1 以第一个数组元素作为比较 赋值给temp 即temp nums 0 从j开始向前扫描 找
  • 一篇了解Containerd容器运行时及安装

    文章目录 一 Containerd简介 1 什么是Containerd 2 Containerd和Docker的区别是什么 二 使用yum仓库安装Containerd 三 使用源码安装Containerd 四 配置国内镜像加速地址 一 Co
  • 寻找第K大的数的方法总结

    寻找第K大的数的方法总结 今天看算法分析是 看到一个这样的问题 就是在一堆数据中查找到第k个大的值 名称是 设计一组N个数 确定其中第k个最大值 这是一个选择问题 当然 解决这个问题的方法很多 本人在网上搜索了一番 查找到以下的方式 决定很
  • vscode 跳转到指定的行数的快捷键

    在工作中 尤其是容易产生错误的 js 代码 报错之后会提示有错误代码的位置 也就是在哪一行出问题了 在vscode 编辑器中 可以使用快捷键 跳转到指定的行数 快捷键 Ctrl G 然后在弹出的框中输入行数就可以了
  • 业务风控思考:如何建立识别、防御和决策体系?

    导语 在疫情扰乱生活节奏的三年中 经常会看到企业 降本增效 的话题 如 让员工感受寒气 搬走办公室绿植 降低食堂伙食标准等等 就企业运作成本而言 降低黑灰产 羊毛党 打码平台等 盗夺的有限资源无疑是最有效的降本方式之一 据不完全统计 我国现
  • CSS高级篇——多背景&背景原点

    CSS 提供了很多处理背景的手段 多背景 background image 可以同时设置多个背景图片 background image url this jpg url that gif url theother png 效果如下 back
  • 深度学习-Tensorflow2.2-卷积神经网络{3}-卫星图像识别卷积综合实例(二分类)-13

    import tensorflow as tf import matplotlib pyplot as plt matplotlib inline import numpy as np import pathlib 数据读取及预处理 dat
  • imap服务器收缓存pst文件夹,Outlook 转移OST数据文件 IMAP账户

    问题 windows8系统 装了Outlook2013 占用了C盘大约10G空间 主要都是数据文件 OST文件 占用的 希望能够把数据文件从C盘移至其他盘 并且账户是IMAP账户 不是Exchange google一下 绝大多数解决方案是针
  • 儿童趣味编程是什么?如何启迪编程思维?

    从前在大多数人的认知里 编程是成年人才能接触和应用到的知识和技术 它好像很晦涩难懂 离小孩子们的世界非常远 但是身处互联网时代 编程已经面向所有人群 孩子们也不必等到大学才能接触到相关知识 而是在小学或者更早的学龄前阶段 就有了系统学习编程
  • 日志的管理

    日志介绍 1 日志文件是重要的系统信息文件 记录了许多的重要的系统事件 包括有用户的登录信息 系统的启动信息 系统的安全信息 邮件相关信息 各种服务相关信息等 2 日志对于安全来说非常的重要 它记录了系统每天发生的各种事情 通过日志来检查错
  • mysql_real_connect 连接失败 问题!

    mysql 的c函数mysql real connect 用localhost为参数进行连接 第一次会成功 但第二次就会抛出异常 本人遇到这个问题 查找了好久 最后才发现问题所在 解决办法 改用ip地址就可以 经验共享
  • 目标检测之 IoU

    转载自 https blog csdn net u014061630 article details 82818112 IoU 作为目标检测算法性能 mAP 计算的一个非常重要的函数 但纵观 IoU 计算的介绍知识 都是直接给出代码 给出计
  • 基于树莓派博通BCM2835芯片手册导读写编简单引脚驱动代码编译和测试(树莓派)

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