Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)

前文回顾

《Linux驱动开发(一)—环境搭建与hello world》
《Linux驱动开发(二)—驱动与设备的分离设计》
《Linux驱动开发(三)—设备树》
《Linux驱动开发(四)—树莓派内核编译》
《Linux驱动开发(五)—树莓派设备树配合驱动开发》
《Linux驱动开发(六)—树莓派配合硬件进行字符驱动开发》
《Linux驱动开发(七)—树莓派按键驱动开发》
《Linux驱动开发(八)—树莓派SR04驱动开发》
继续宣传一下韦老师的视频

70天30节Linux驱动开发快速入门系列课程【实战教学、技术讨论、直播答疑】

在这里插入图片描述
从第八节开始,就开始进行具体的设备驱动开发,今天来学习一下I2C总线设备的驱动开发。
开始学习!
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第1张图片

BME280

Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第2张图片

Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第3张图片
为什么要用这个IIC设备呢,因为我这没有其他的传感器。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第4张图片

就这个还是用之前做手表留下的底板,焊接好外围做了一个传感器。顺带复习了一下焊接技术
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第5张图片

设备树

这个不同于前面的虚拟总线模型,这个是真实存在了I2C总线,所以设备的DTS,需要放在已经定义好的I2C总线下,我们可以看到,DTS的前面有如下定义
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第6张图片
后面又包含了
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第7张图片
我们要定义自己的I2C的客户端,就需要放在I2C1的引用下。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第8张图片

那么为什么不放在I2C0下,因为没有找到,为啥不放在I2C2下,因为没有看到引脚定义,反正就是感觉放在1下比较稳妥。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第9张图片
这个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"

解决办法就是
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第10张图片

别问为什么,就是这么豪横。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第11张图片

更新设备树之后,不是在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# 

设备树创建设备没有问题,咱们继续
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第12张图片

驱动框架

接下来写一个最简单的I2C驱动框架,包括注册i2c_driver

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


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");

编译 加载。
然后就是各种遇到问题了。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第13张图片

首先是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都变得可用了。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第14张图片

继续驱动开发

测试模块加载及运行

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参数,在他的版本中是必须有的,算是内核的一个缺陷。不过在我这个版本中,没有这个问题。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第15张图片

继续注册设备,回到我们原来想要的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  r \n", argv[0]);
		printf("       %s  w  \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函数。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第16张图片

读取BME280数据

这里就需要理解BME280的操作手册了。我们这里简单的实现一个读取ID的操作。
ID的地址为0xD0,包含了芯片的身份标示码chip_id[7:0],上电复位后可读。成功的话会读到0x60。
网上说BMP280BME280一样,差点上了个大B当。ID不一样,BMP280读出来是0x58
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第17张图片

每个读取的过程都是如下,参考单片机的过程,参考洛华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驱动的开发,并不是传感器的使用。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第18张图片

问题总结

查看设备树生成设备

这里有三种方式,可以用前面查找方式

find / -name "*mybme280*"

也可以直接查看I2C下的设备

cd /sys/bus/i2c/devices/|ls

还可以用i2c-tool带的命令i2cdetect检测一下1号总线

i2cdetect -y 1

Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第19张图片

查看设备匹配的驱动

开始的时候,无法挂载上驱动,不执行probe函数,后来发现设备提前加载了bmp280的驱动,
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第20张图片
手动卸载了相关驱动,再安装自己编译的驱动,也还是加载原有的驱动。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第21张图片

所以先把驱动移除备份
在这里插入图片描述
然后再次加载自己开发的驱动,才可以了
自己的驱动加载之后,会出现在下面目录
在这里插入图片描述

查看驱动的匹配可以通过下面方式,进入设备目录,就能看到driver对应的是自己编写的驱动。
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第22张图片

结束语

今年十一放假还是七天,放七天,上七天,是不是感觉和没休一样,放的都上回来了。我们这时代到底是不是进步,怎么感觉假期一点没多呢,而且感觉就算放假了,也没什么精力去玩了,
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第23张图片
哎,就这样吧,周五了,再不去,食堂的烧饼加鸡蛋就没了
Linux驱动开发(九)---树莓派I2C设备驱动开发(BME280)_第24张图片

你可能感兴趣的:(驱动开发,操作系统,linux知识,驱动开发,linux,BME280,树莓派)