前文回顾

《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设备,感觉还是不能这么下去了,于是重新编译内核。

先把树莓派用官方工具烧写好。

  1. 用正常的树莓派系统,导出配置

获取当前树莓派的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源码的根目录。

  1. 重新编译内核。并更新内核。

更新内核之后,连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)相关推荐

  1. Linux I2C设备驱动编写(二)

    I2C对外API I2C client的注册 i2c_register_board_info具体实现 i2c_new_device I2C driver 关于I2C设备驱动的小总结 I2C adapt ...

  2. MTK开发之—添加aw21024 i2c设备驱动

    从供应商处拿到一驱动led模块的 i2c设备驱动,人家驱动已经基本给我们写好,我们需要做的就是将驱动添加进我们的内核,烧录进我们的板子为我们所用,aw21024驱动与资料:https://downlo ...

  3. 迅为IMX6ULL开发板Linux I2C设备驱动编写流程-信息描述

    1 .不使用设备树文件 当开始编写 I2C 设备驱动时,首先要添加设备信息.先来看一下在不使用设备树,使用平台文件时, 如何在平台文件中添加 I2C 设备信息. 在平台文件中通过 i2c_board_ ...

  4. 手把手教你写Linux I2C设备驱动

    手把手教你写Linux I2C设备驱动 标签:Linux 设备 驱动 详解 i2c 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:/ ...

  5. linux探测i2c设备连接状态,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  6. 手把手教你写Linux I2C设备驱动 tvp5158

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  7. Linux 设备驱动篇之I2c设备驱动

    ******************************************************************************************** 装载声明:希望 ...

  8. Linux设备驱动篇——[I2C设备驱动-1]

    Linux 设备驱动篇之I2c设备驱动 fulinux 一.I2C驱动体系 虽然I2C硬件体系结构和协议都很容易理解,但是Linux I2C驱动体系结构却有相当的复杂度,它主要由3部分组成,即I2C设 ...

  9. linux i2c detect函数,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  10. Linux添加一个i2c设备,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

最新文章

  1. Python全栈工程师(Python3 所有基础内容 0-0)
  2. Redis高级实用特性:发布及订阅消息
  3. 如何隐藏SAP CRM WebClient UI配置页面的字段
  4. 贷款为什么要查看征信?
  5. bzoj4278[ONTAK2015]Tasowanie bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明...
  6. C语言试题四十二之假定输入的字符串中只包含字母和*号。请编写函数fun,它的功能是:将字符串中的前导*号全部移到字符串的尾部。
  7. ux和ui_使用UX设计师为Amazon的Alexa学习会话式UI的基础
  8. PSD分层立体数据模板立体数据统计素材
  9. mastercam后处理升级_Mastercam中文版后处理MPFan 有了它就可以自己修改后处理了 呵呵...
  10. 美国爱荷华州批准参议院541号文件,允许使用分布式账本技术和智能合约
  11. 猜数字游戏python程序_【自学编程】python 小游戏—猜数字
  12. CodeCanyon上的20种最佳WordPress登录表单
  13. 【常用模块】HC-05蓝牙串口通信模块使用详解(实例:手机蓝牙控制STM32单片机)
  14. SQL注入漏洞--2
  15. GDPR 和个人信息保护的小知识
  16. 编程之类的文案_精选50句文案,个个都是让你灵感喷涌的句子!
  17. 浅谈动态规划 ——by cbw
  18. Android调用新版百度天气api,解决地理编码问题
  19. Layabox开发微信小游戏好友排行榜功能流程
  20. mysql excel 同步数据_mysql导入excel数据

热门文章

  1. vue 窗口变小文字_页面字体随窗口变化大小
  2. Axure原型图小字体在浏览器显示变大
  3. 【报告分享】 2020-2021智能手机消费趋势与用户忠诚和流失度报告-企鹅智库(附下载)
  4. 服务器slot槽位位置,华为OSN2500子架及其槽位的分配
  5. LoRa网关实现水表抄表无线远程数采方案
  6. Js获取屏幕宽度、高度 移动端H5适配
  7. 获取手机屏幕的宽高 html,js获取手机屏幕宽度、高度
  8. 地级市交通基础设施数据,省份交通基础设施数据,处理好的面板数据(excel或stata版本)
  9. 根据卡号查询银行卡信息
  10. svn和git的区别,为什么使用git?