当前位置:   article > 正文

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

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"
  • 1

解决办法就是
在这里插入图片描述

别问为什么,就是这么豪横。
在这里插入图片描述

更新设备树之后,不是在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# 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

设备树创建设备没有问题,咱们继续
在这里插入图片描述

驱动框架

接下来写一个最简单的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");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

编译 加载。
然后就是各种遇到问题了。
在这里插入图片描述

首先是i2c设备没有,执行i2cdetect报错

FATAL: Module i2c-dev not found in directory
  • 1

各种方法之下,还是没有解决。于是!

插播解决内核升级问题

之前遇到的问题就开始显现了,没有i2c设备,加上之前的没有wifi设备,感觉还是不能这么下去了,于是重新编译内核。

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

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

获取当前树莓派的config
已经开机的树莓派上会有这个节点:/proc/config.gz,从这个节点可以获取本树莓派的config。
如果没有这个节点的话则需要先加载模块:

sudo modprobe configs
  • 1

把 config.gz 内容复制到要编译的电脑上:

scp pi@[ip]:/proc/config.gz .
  • 1

解压,保存为.confg文件。

zcat config.gz > .config
  • 1

注:必须在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
  • 1
  • 2
  • 3
  • 4

没问题,已经成功运行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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

不过这里使用了是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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

再次尝试。

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

妥妥的执行到了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); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

我们在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 
  • 1
  • 2
  • 3
  • 4

没毛病。
后续的读取温度及计算就先不搞了,这里主要还是学习IIC驱动的开发,并不是传感器的使用。
在这里插入图片描述

问题总结

查看设备树生成设备

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

find / -name "*mybme280*"
  • 1

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

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

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

i2cdetect -y 1
  • 1

在这里插入图片描述

查看设备匹配的驱动

开始的时候,无法挂载上驱动,不执行probe函数,后来发现设备提前加载了bmp280的驱动,
在这里插入图片描述
手动卸载了相关驱动,再安装自己编译的驱动,也还是加载原有的驱动。
在这里插入图片描述

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

查看驱动的匹配可以通过下面方式,进入设备目录,就能看到driver对应的是自己编写的驱动。
在这里插入图片描述

结束语

今年十一放假还是七天,放七天,上七天,是不是感觉和没休一样,放的都上回来了。我们这时代到底是不是进步,怎么感觉假期一点没多呢,而且感觉就算放假了,也没什么精力去玩了,
在这里插入图片描述
哎,就这样吧,周五了,再不去,食堂的烧饼加鸡蛋就没了
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/647954
推荐阅读
相关标签
  

闽ICP备14008679号