Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)
前文回顾
《Linux驱动开发(一)—环境搭建与hello world》
《Linux驱动开发(二)—驱动与设备的分离设计》
《Linux驱动开发(三)—设备树》
《Linux驱动开发(四)—树莓派内核编译》
《Linux驱动开发(五)—树莓派设备树配合驱动开发》
《Linux驱动开发(六)—树莓派配合硬件进行字符驱动开发》
《Linux驱动开发(七)—树莓派按键驱动开发》
《Linux驱动开发(八)—树莓派SR04驱动开发》
继续宣传一下韦老师的视频
70天30节Linux驱动开发快速入门系列课程【实战教学、技术讨论、直播答疑】
从第八节开始,就开始进行具体的设备驱动开发,今天来学习一下I2C总线设备的驱动开发。
开始学习!
BME280
为什么要用这个IIC设备呢,因为我这没有其他的传感器。
就这个还是用之前做手表留下的底板,焊接好外围做了一个传感器。顺带复习了一下焊接技术
设备树
这个不同于前面的虚拟总线模型,这个是真实存在了I2C总线,所以设备的DTS,需要放在已经定义好的I2C总线下,我们可以看到,DTS的前面有如下定义
后面又包含了
我们要定义自己的I2C的客户端,就需要放在I2C1的引用下。
那么为什么不放在I2C0下,因为没有找到,为啥不放在I2C2下,因为没有看到引脚定义,反正就是感觉放在1下比较稳妥。
这个DTS编译的时候,会报一个警告,
arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts:245.10-249.4: Warning (i2c_bus_reg): /soc/i2c@7e804000/mybme280: I2C bus unit address format error, expected "76"
解决办法就是
别问为什么,就是这么豪横。
更新设备树之后,不是在device-tree下查看有没有这个设备了,而是需要搜索一下才能看到。
然后进入相关路径,可以进行如下查看,发现数据没问题。注意查看reg参数的使用,用的是hexdump命令
root@raspberrypi:/home/pgg# cd /sys/firmware/devicetree/base/soc/i2c@7e804000/mybme280@76
root@raspberrypi:/sys/firmware/devicetree/base/soc/i2c@7e804000/mybme280@76# cat
compatible name reg status
root@raspberrypi:/sys/firmware/devicetree/base/soc/i2c@7e804000/mybme280@76# cat compatible
pgg,bme280root@raspberrypi:/sys/firmware/devicetree/base/soc/i2c@7e804000/mybme280@76# hex
hex2hcd hexdump
root@raspberrypi:/sys/firmware/devicetree/base/soc/i2c@7e804000/mybme280@76# hexdump reg
0000000 0000 7600
0000004
root@raspberrypi:/sys/firmware/devicetree/base/soc/i2c@7e804000/mybme280@76#
设备树创建设备没有问题,咱们继续
驱动框架
接下来写一个最简单的I2C驱动框架,包括注册i2c_driver
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>static int bme280_probe(struct i2c_client *client, const struct i2c_device_id *id)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int bme280_remove(struct i2c_client *client)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static const struct of_device_id bme280_of_match[] = {{.compatible = "pgg,bme280"},{}
};static struct i2c_driver bme280_drv = {.driver = {.name = "mybme280_drv",.of_match_table = bme280_of_match,},.probe = bme280_probe,.remove = bme280_remove,
};static int bme280_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(&bme280_drv);;
}static void bme280_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);i2c_del_driver(&bme280_drv);
}module_init(bme280_init);
module_exit(bme280_exit);MODULE_LICENSE("GPL");
编译 加载。
然后就是各种遇到问题了。
首先是i2c设备没有,执行i2cdetect报错
FATAL: Module i2c-dev not found in directory
各种方法之下,还是没有解决。于是!
插播解决内核升级问题
之前遇到的问题就开始显现了,没有i2c设备,加上之前的没有wifi设备,感觉还是不能这么下去了,于是重新编译内核。
先把树莓派用官方工具烧写好。
- 用正常的树莓派系统,导出配置
获取当前树莓派的config
已经开机的树莓派上会有这个节点:/proc/config.gz,从这个节点可以获取本树莓派的config。
如果没有这个节点的话则需要先加载模块:
sudo modprobe configs
把 config.gz 内容复制到要编译的电脑上:
scp pi@[ip]:/proc/config.gz .
解压,保存为.confg文件。
zcat config.gz > .config
注:必须在linux环境下解压,在mac下会乱码。
把此config文件复制到linux源码的根目录。
- 重新编译内核。并更新内核。
更新内核之后,连wifi都变得可用了。
继续驱动开发
测试模块加载及运行
root@raspberrypi:/home/pgg/work/driver# insmod mybme280.ko
root@raspberrypi:/home/pgg/work/driver# dmesg
[ 844.402925] drivers/char/mybmp280.c bme280_init 53
[ 844.403116] drivers/char/mybmp280.c bme280_probe 19
没问题,已经成功运行probe函数。
在观看韦老师的视频时,他发现了一个内核的问题,就是id_table参数,在他的版本中是必须有的,算是内核的一个缺陷。不过在我这个版本中,没有这个问题。
继续注册设备,回到我们原来想要的file_operation,于是,增加如下代码,注册一个bme280的operations
static int major;
static struct class *bme280_class;static long bme280_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 定义自己的file_operations结构体 */
static struct file_operations bme280_fops = {.owner = THIS_MODULE,.unlocked_ioctl = bme280_ioctl,
};static int bme280_probe(struct i2c_client *client, const struct i2c_device_id *id)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* register_chrdev */major = register_chrdev(0, "bme280", &bme280_fops ); /* class_create */bme280_class = class_create(THIS_MODULE, "bme280_class");/* device_create */device_create(bme280_class, NULL, MKDEV(major, 0), NULL, "mybme280");return 0;
}static int bme280_remove(struct i2c_client *client)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(bme280_class, MKDEV(major, 0));class_destroy(bme280_class);unregister_chrdev(major, "bme280");return 0;
}
不过这里使用了是ioctl,这个接口就比单纯的read或者write更加灵活,这类似于一个带参数的read。
用户侧程序
int main(int argc, char **argv)
{int fd;int buf[2];if ((argc != 4) && (argc != 5)){printf("Usage: %s <dev> r <addr>\n", argv[0]);printf(" %s <dev> w <addr> <val>\n", argv[0]);return -1;}fd = open(argv[1], O_RDWR);if (fd < 0){printf(" can not open %s\n", argv[1]);return -1;}buf[0]=1;buf[1]=2;ioctl(fd, 0, buf);close(fd);return 0;
}
再次尝试。
root@raspberrypi:/home/pgg/work/driver# gcc -o mybme280_user mybme280_user.c
root@raspberrypi:/home/pgg/work/driver# insmod mybme280.ko
root@raspberrypi:/home/pgg/work/driver# ./mybme280_user /dev/mybme280 r 10
Read addr 0xa, get data 0x0
root@raspberrypi:/home/pgg/work/driver# dmesg
[ 3621.693273] drivers/char/mybmp280.c bme280_init 83
[ 3621.693472] drivers/char/mybmp280.c bme280_probe 36
[ 3643.259323] drivers/char/mybmp280.c bme280_ioctl 22
妥妥的执行到了ioctl函数。
读取BME280数据
这里就需要理解BME280的操作手册了。我们这里简单的实现一个读取ID的操作。
ID的地址为0xD0,包含了芯片的身份标示码chip_id[7:0],上电复位后可读。成功的话会读到0x60。
网上说BMP280BME280一样,差点上了个大B当。ID不一样,BMP280读出来是0x58
每个读取的过程都是如下,参考单片机的过程,参考洛华x《 BMP280气压温度传感器详细使用教程》
IICBegin();IICWrite(0xEC);IICWrite(0xD0);//选定0xD0寄存器,开始读取修正参数IICBegin();IICWrite(0xED);//0x76地址加上1的最低位,R/W选为读id=IICRead(true);
IIC的发送,每次都以开始信号为起始,所以类似的这种操作是两组数据。那么封装到ioctl中,产生了如下代码。不知道为啥这里的读取地址不再需要加1,还是使用mybme280_client->addr。
static int bmp_readid(void)
{unsigned char addr=0xd0;unsigned char id=0xec;struct i2c_msg msgs[2];/* 读AT24C02 */msgs[0].addr = mybme280_client->addr;msgs[0].flags = 0; /* 写 */msgs[0].len = 1;msgs[0].buf = &addr;msgs[1].addr = (mybme280_client->addr);msgs[1].flags = I2C_M_RD; /* 读 */msgs[1].len = 1;msgs[1].buf = &id;i2c_transfer(mybme280_client->adapter, msgs, 2);printk("get bme280 id %x \n", id);return 0;
}
我们在ioctl中引用一下这个函数,就可以试着读出ID是不是0x60了。
root@raspberrypi:/home/pgg/work/driver# ./mybme280_user /dev/mybme280 -r 10
root@raspberrypi:/home/pgg/work/driver# dmesg
[ 314.434372] drivers/char/mybme280.c bme280_ioctl 65
[ 314.635714] get bme280 id 60
没毛病。
后续的读取温度及计算就先不搞了,这里主要还是学习IIC驱动的开发,并不是传感器的使用。
问题总结
查看设备树生成设备
这里有三种方式,可以用前面查找方式
find / -name "*mybme280*"
也可以直接查看I2C下的设备
cd /sys/bus/i2c/devices/|ls
还可以用i2c-tool带的命令i2cdetect检测一下1号总线
i2cdetect -y 1
查看设备匹配的驱动
开始的时候,无法挂载上驱动,不执行probe函数,后来发现设备提前加载了bmp280的驱动,
手动卸载了相关驱动,再安装自己编译的驱动,也还是加载原有的驱动。
所以先把驱动移除备份
然后再次加载自己开发的驱动,才可以了
自己的驱动加载之后,会出现在下面目录
查看驱动的匹配可以通过下面方式,进入设备目录,就能看到driver对应的是自己编写的驱动。
结束语
今年十一放假还是七天,放七天,上七天,是不是感觉和没休一样,放的都上回来了。我们这时代到底是不是进步,怎么感觉假期一点没多呢,而且感觉就算放假了,也没什么精力去玩了,
哎,就这样吧,周五了,再不去,食堂的烧饼加鸡蛋就没了
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)相关推荐
- Linux I2C设备驱动编写(二)
I2C对外API I2C client的注册 i2c_register_board_info具体实现 i2c_new_device I2C driver 关于I2C设备驱动的小总结 I2C adapt ...
- MTK开发之—添加aw21024 i2c设备驱动
从供应商处拿到一驱动led模块的 i2c设备驱动,人家驱动已经基本给我们写好,我们需要做的就是将驱动添加进我们的内核,烧录进我们的板子为我们所用,aw21024驱动与资料:https://downlo ...
- 迅为IMX6ULL开发板Linux I2C设备驱动编写流程-信息描述
1 .不使用设备树文件 当开始编写 I2C 设备驱动时,首先要添加设备信息.先来看一下在不使用设备树,使用平台文件时, 如何在平台文件中添加 I2C 设备信息. 在平台文件中通过 i2c_board_ ...
- 手把手教你写Linux I2C设备驱动
手把手教你写Linux I2C设备驱动 标签:Linux 设备 驱动 详解 i2c 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:/ ...
- linux探测i2c设备连接状态,手把手教你写Linux I2C设备驱动
Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...
- 手把手教你写Linux I2C设备驱动 tvp5158
Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...
- Linux 设备驱动篇之I2c设备驱动
******************************************************************************************** 装载声明:希望 ...
- Linux设备驱动篇——[I2C设备驱动-1]
Linux 设备驱动篇之I2c设备驱动 fulinux 一.I2C驱动体系 虽然I2C硬件体系结构和协议都很容易理解,但是Linux I2C驱动体系结构却有相当的复杂度,它主要由3部分组成,即I2C设 ...
- linux i2c detect函数,手把手教你写Linux I2C设备驱动
Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...
- Linux添加一个i2c设备,手把手教你写Linux I2C设备驱动
Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...
最新文章
- Python全栈工程师(Python3 所有基础内容 0-0)
- Redis高级实用特性:发布及订阅消息
- 如何隐藏SAP CRM WebClient UI配置页面的字段
- 贷款为什么要查看征信?
- bzoj4278[ONTAK2015]Tasowanie bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明...
- C语言试题四十二之假定输入的字符串中只包含字母和*号。请编写函数fun,它的功能是:将字符串中的前导*号全部移到字符串的尾部。
- ux和ui_使用UX设计师为Amazon的Alexa学习会话式UI的基础
- PSD分层立体数据模板立体数据统计素材
- mastercam后处理升级_Mastercam中文版后处理MPFan 有了它就可以自己修改后处理了 呵呵...
- 美国爱荷华州批准参议院541号文件,允许使用分布式账本技术和智能合约
- 猜数字游戏python程序_【自学编程】python 小游戏—猜数字
- CodeCanyon上的20种最佳WordPress登录表单
- 【常用模块】HC-05蓝牙串口通信模块使用详解(实例:手机蓝牙控制STM32单片机)
- SQL注入漏洞--2
- GDPR 和个人信息保护的小知识
- 编程之类的文案_精选50句文案,个个都是让你灵感喷涌的句子!
- 浅谈动态规划 ——by cbw
- Android调用新版百度天气api,解决地理编码问题
- Layabox开发微信小游戏好友排行榜功能流程
- mysql excel 同步数据_mysql导入excel数据