“扩展板空气温湿度传感器驱动移植”的版本间的差异
(创建页面,内容为“==实验原理== 打开扩展板原理图对照扩展板可以看到扩展板有1个温湿度传感器SI7006,如下图: ==实验平台== ==实验步骤==”) |
(→实验步骤) |
||
(未显示同一用户的4个中间版本) | |||
第1行: | 第1行: | ||
==实验原理== | ==实验原理== | ||
打开扩展板原理图对照扩展板可以看到扩展板有1个温湿度传感器SI7006,如下图: | 打开扩展板原理图对照扩展板可以看到扩展板有1个温湿度传感器SI7006,如下图: | ||
+ | |||
+ | <center>[[Image:62-1-1.png]]</center> | ||
+ | |||
+ | 由上图可见可通过I2C总线与SI7006通信。 | ||
+ | |||
+ | <center>[[Image:62-1-2.png]]</center> | ||
+ | |||
+ | <center>扩展板与底板接口对照图</center> | ||
+ | |||
+ | 查看原理图可知数据线I2C1_SDA、I2C1_SCL和I2C_INT1管脚对应关系如下: | ||
+ | |||
+ | {|align=center border=1 | ||
+ | |- | ||
+ | ! 原理图网络编号 !! 对应管脚 !! 管脚功能 !! 管脚功能码 | ||
+ | |- | ||
+ | | I2C1_SCL | ||
+ | | PF14 | ||
+ | | I2C1_SCL | ||
+ | | AF5 | ||
+ | |- | ||
+ | | I2C1_SDA | ||
+ | | PF15 | ||
+ | | I2C1_SDA | ||
+ | | AF5 | ||
+ | |} | ||
+ | 查看SI7006芯片手册确认设备七位从机地址为:0x40 | ||
+ | <center>[[Image:62-1-3.png]]</center> | ||
+ | |||
==实验平台== | ==实验平台== | ||
+ | 华清远见开发环境,FS-MP1A平台 | ||
+ | |||
==实验步骤== | ==实验步骤== | ||
+ | <ol> | ||
+ | <li>导入交叉编译工具链</li> | ||
+ | linux@ubuntu:$ source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi | ||
+ | |||
+ | <li>内核配置</li> | ||
+ | 内核中没有SI7006的驱动,但是可以找到SI7020和SI7005的驱动,通过查阅资料及芯片手册对比,可以发现SI7020和SI7006兼容,所以使用SI7020的驱动即可。 | ||
+ | drivers/iio/humidity/si7020.c | ||
+ | 执行make menuconfig配置内核对应选项 | ||
+ | |||
+ | <pre> | ||
+ | linux@ubuntu:$ make menuconfig | ||
+ | Device Drivers ---> | ||
+ | <*> Industrial I/O support ---> | ||
+ | Humidity sensors ---> | ||
+ | <*> Si7013/20/21 Relative Humidity and Temperature Sensors | ||
+ | </pre> | ||
+ | <li>修改设备树</li> | ||
+ | 参考linux内核文档: | ||
+ | Documentation/devicetree/bindings/i2c/i2c-stm32.txt | ||
+ | 修改设备树文件: | ||
+ | arch/arm/boot/dts/stm32mp157a-fsmp1a-extended.dts | ||
+ | 由于i2c1在stm32mp151.dtsi中已完成定义,这里需要在原有基础添加与硬件对应的相关信息,在文件stm32mp157a-fsmp1a-extended.dts末尾集成并添加i2c1相关内容: | ||
+ | |||
+ | <pre> | ||
+ | &i2c1 { | ||
+ | pinctrl-names = "default", "sleep"; | ||
+ | pinctrl-0 = <&i2c1_pins_b>; | ||
+ | pinctrl-1 = <&i2c1_pins_sleep_b>; | ||
+ | i2c-scl-rising-time-ns = <100>; | ||
+ | i2c-scl-falling-time-ns = <7>; | ||
+ | status = "okay"; | ||
+ | /delete-property/dmas; | ||
+ | /delete-property/dma-names; | ||
+ | |||
+ | si7020: si7020@40 { | ||
+ | compatible = "silabs,si7020"; | ||
+ | reg = <0x40>; | ||
+ | }; | ||
+ | }; | ||
+ | </pre> | ||
+ | 仿照设备树对于i2c管脚的配置添加i2c1管脚配置: | ||
+ | |||
+ | <pre> | ||
+ | &pinctrl { | ||
+ | i2c1_pins_b: i2c1-0 { | ||
+ | pins { | ||
+ | pinmux = <STM32_PINMUX('F', 14, AF5)>, /* I2C1_SCL */ | ||
+ | <STM32_PINMUX('F', 15, AF5)>; /* I2C1_SDA */ | ||
+ | bias-disable; | ||
+ | drive-open-drain; | ||
+ | slew-rate = <0>; | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | i2c1_pins_sleep_b: i2c1-1 { | ||
+ | pins { | ||
+ | pinmux = <STM32_PINMUX('F', 14, ANALOG)>, /* I2C1_SCL */ | ||
+ | <STM32_PINMUX('F', 15, ANALOG)>; /* I2C1_SDA */ | ||
+ | }; | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | </pre> | ||
+ | <li>重新编译内核和设备树文件</li> | ||
+ | linux@ubuntu:$ make -j4 uImage dtbs LOADADDR=0xC2000040 | ||
+ | <li>更新系统内核和设备树</li> | ||
+ | <li>测试</li> | ||
+ | 系统启动后可以查看目录/sys/bus/iio/devices/ | ||
+ | <pre> | ||
+ | root@fsmp1a:~# ls /sys/bus/iio/devices/ | ||
+ | iio:device0 | ||
+ | </pre> | ||
+ | 如果系统中有多个iio设备,这里可能会有很多个iio目录,确定哪个目录是我们的设备对应目录,可以通过查看/sys/bus/iio/devices/iio\:device0/name信息确认: | ||
+ | <pre> | ||
+ | root@fsmp1a:~# cat /sys/bus/iio/devices/iio\:device0/name | ||
+ | 0-0040 | ||
+ | </pre> | ||
+ | |||
+ | 由显示信息每个驱动对应设备可能有所不同,当前显示内容为设备的物理地址,与设备树中地址一致,可以确认iio:device0是当前设备对应目录 | ||
+ | 查看当目录下内容: | ||
+ | |||
+ | <pre> | ||
+ | root@fsmp1a:~# ls -l /sys/bus/iio/devices/iio\:device0/ | ||
+ | total 0 | ||
+ | -r--r--r-- 1 root root 4096 Feb 7 15:51 dev | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_offset | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_raw | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_scale | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_offset | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_raw | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_scale | ||
+ | -r--r--r-- 1 root root 4096 Feb 7 15:51 name | ||
+ | drwxr-xr-x 2 root root 0 Feb 7 15:51 power | ||
+ | lrwxrwxrwx 1 root root 0 Feb 7 15:50 subsystem -> ../../../../../../../bus/iio | ||
+ | -rw-r--r-- 1 root root 4096 Feb 7 15:50 uevent | ||
+ | </pre> | ||
+ | 文件说明:<br> | ||
+ | 文件in_ temp_scale为温度标尺,计算公式如下,公式来自与驱动对应代码: | ||
+ | |||
+ | <center>[[Image:62-3-1.png]]</center><br> | ||
+ | |||
+ | <center>Scale<sub>temp</sub>=(175.72×1000×4)/65535=10.725097656</center> | ||
+ | |||
+ | in_ temp_offset为数据偏移,计算公式如下,公式来自于驱动对应代码: | ||
+ | |||
+ | <center>[[Image:62-3-2.png]]</center><br> | ||
+ | |||
+ | <center>offset<sub>temp</sub>=(-46.85×65536)/(4×175.72)=-4368</center> | ||
+ | |||
+ | in_ temp_raw为原始数据,计算公式如下,公式来自于驱动对应代码: | ||
+ | |||
+ | <center>[[Image:62-3-3.png]]</center><br> | ||
+ | |||
+ | <center>Raw<sub>temp</sub>=Code<sub>temp</sub>/4 </center> | ||
+ | |||
+ | 阅读SI7006芯片手册可以看到温度的计算公式为: | ||
+ | |||
+ | <center>Temperature(℃)=(175.72×Code<sub>temp</sub>)/65535-46.85</center> | ||
+ | |||
+ | 上述公式与驱动返回值看不出直接对应关系,所以我们按照驱动提供的scale、offset及Raw的计算公式对公式进行处理,得到最终公式计算过程如下: | ||
+ | |||
+ | <center>[[Image:62-3-4.png]]</center><br> | ||
+ | |||
+ | 编写测试程序如下: | ||
+ | |||
+ | 编写测试程序或参考:“光盘资料:华清远见-FS-MP1A开发资料\02-程序源码\06-资源扩展板测试程序\03-si7006_test” | ||
+ | |||
+ | Si7006_test.c | ||
+ | <pre> | ||
+ | #include <stdint.h> | ||
+ | #include <string.h> | ||
+ | #include <fcntl.h> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <errno.h> | ||
+ | |||
+ | int read_sysfs_float(const char *device, const char *filename, float *val) | ||
+ | { | ||
+ | int ret = 0; | ||
+ | FILE *sysfsfp; | ||
+ | char temp[128]; | ||
+ | |||
+ | memset(temp, '0', 128); | ||
+ | |||
+ | ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename); | ||
+ | if (ret < 0) | ||
+ | goto error; | ||
+ | |||
+ | sysfsfp = fopen(temp, "r"); | ||
+ | if (!sysfsfp) { | ||
+ | ret = -errno; | ||
+ | goto error; | ||
+ | } | ||
+ | |||
+ | errno = 0; | ||
+ | if (fscanf(sysfsfp, "%f\n", val) != 1) { | ||
+ | ret = errno ? -errno : -ENODATA; | ||
+ | if (fclose(sysfsfp)) | ||
+ | perror("read_sysfs_float(): Failed to close dir"); | ||
+ | |||
+ | goto error; | ||
+ | } | ||
+ | |||
+ | if (fclose(sysfsfp)) | ||
+ | ret = -errno; | ||
+ | |||
+ | error: | ||
+ | |||
+ | return ret; | ||
+ | } | ||
+ | |||
+ | int read_sysfs_int(const char *device, const char *filename, int *val) | ||
+ | { | ||
+ | int ret = 0; | ||
+ | FILE *sysfsfp; | ||
+ | char temp[128]; | ||
+ | |||
+ | memset(temp, '0', 128); | ||
+ | |||
+ | ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename); | ||
+ | if (ret < 0) | ||
+ | goto error; | ||
+ | |||
+ | sysfsfp = fopen(temp, "r"); | ||
+ | if (!sysfsfp) { | ||
+ | ret = -errno; | ||
+ | goto error; | ||
+ | } | ||
+ | |||
+ | errno = 0; | ||
+ | if (fscanf(sysfsfp, "%d\n", val) != 1) { | ||
+ | ret = errno ? -errno : -ENODATA; | ||
+ | if (fclose(sysfsfp)) | ||
+ | perror("read_sysfs_float(): Failed to close dir"); | ||
+ | |||
+ | goto error; | ||
+ | } | ||
+ | |||
+ | if (fclose(sysfsfp)) | ||
+ | ret = -errno; | ||
+ | |||
+ | error: | ||
+ | |||
+ | return ret; | ||
+ | } | ||
+ | |||
+ | |||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | int temp_raw = 0; | ||
+ | int temp_offset = 0; | ||
+ | float temp_scale = 0; | ||
+ | |||
+ | int hum_raw = 0; | ||
+ | int hum_offset = 0; | ||
+ | float hum_scale = 0; | ||
+ | |||
+ | while(1) { | ||
+ | /*read temp data*/ | ||
+ | read_sysfs_int(argv[1], "in_temp_raw", &temp_raw); | ||
+ | read_sysfs_int(argv[1], "in_temp_offset", &temp_offset); | ||
+ | read_sysfs_float(argv[1], "in_temp_scale", &temp_scale); | ||
+ | |||
+ | printf("temperature = %.2f\n", (temp_raw + temp_offset) * temp_scale / 1000); | ||
+ | |||
+ | read_sysfs_int(argv[1], "in_humidityrelative_raw", &hum_raw); | ||
+ | read_sysfs_int(argv[1], "in_humidityrelative_offset", &hum_offset); | ||
+ | read_sysfs_float(argv[1], "in_humidityrelative_scale", &hum_scale); | ||
+ | |||
+ | printf("humidity = %.2f%%\n", (hum_raw + hum_offset) * hum_scale / 1000); | ||
+ | |||
+ | sleep(1); | ||
+ | } | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | 编译测试程序拷贝背到系统中并执行,效果如下: | ||
+ | |||
+ | <pre> | ||
+ | root@fsmp1a:~# ./si7006_test iio:device0 | ||
+ | temperature = 29.11 | ||
+ | humidity = 32.54% | ||
+ | temperature = 29.13 | ||
+ | humidity = 32.55% | ||
+ | temperature = 29.12 | ||
+ | humidity = 32.58% | ||
+ | temperature = 29.11 | ||
+ | humidity = 32.59% | ||
+ | temperature = 29.13 | ||
+ | humidity = 32.59% | ||
+ | |||
+ | </pre> | ||
+ | </ol> |
2021年3月24日 (三) 08:57的最新版本
实验原理
打开扩展板原理图对照扩展板可以看到扩展板有1个温湿度传感器SI7006,如下图:
由上图可见可通过I2C总线与SI7006通信。
查看原理图可知数据线I2C1_SDA、I2C1_SCL和I2C_INT1管脚对应关系如下:
原理图网络编号 | 对应管脚 | 管脚功能 | 管脚功能码 |
---|---|---|---|
I2C1_SCL | PF14 | I2C1_SCL | AF5 |
I2C1_SDA | PF15 | I2C1_SDA | AF5 |
查看SI7006芯片手册确认设备七位从机地址为:0x40
实验平台
华清远见开发环境,FS-MP1A平台
实验步骤
- 导入交叉编译工具链
- 内核配置
- 修改设备树
- 重新编译内核和设备树文件
- 更新系统内核和设备树
- 测试
linux@ubuntu:$ source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
内核中没有SI7006的驱动,但是可以找到SI7020和SI7005的驱动,通过查阅资料及芯片手册对比,可以发现SI7020和SI7006兼容,所以使用SI7020的驱动即可。
drivers/iio/humidity/si7020.c
执行make menuconfig配置内核对应选项
linux@ubuntu:$ make menuconfig Device Drivers ---> <*> Industrial I/O support ---> Humidity sensors ---> <*> Si7013/20/21 Relative Humidity and Temperature Sensors
参考linux内核文档:
Documentation/devicetree/bindings/i2c/i2c-stm32.txt
修改设备树文件:
arch/arm/boot/dts/stm32mp157a-fsmp1a-extended.dts
由于i2c1在stm32mp151.dtsi中已完成定义,这里需要在原有基础添加与硬件对应的相关信息,在文件stm32mp157a-fsmp1a-extended.dts末尾集成并添加i2c1相关内容:
&i2c1 { pinctrl-names = "default", "sleep"; pinctrl-0 = <&i2c1_pins_b>; pinctrl-1 = <&i2c1_pins_sleep_b>; i2c-scl-rising-time-ns = <100>; i2c-scl-falling-time-ns = <7>; status = "okay"; /delete-property/dmas; /delete-property/dma-names; si7020: si7020@40 { compatible = "silabs,si7020"; reg = <0x40>; }; };
仿照设备树对于i2c管脚的配置添加i2c1管脚配置:
&pinctrl { i2c1_pins_b: i2c1-0 { pins { pinmux = <STM32_PINMUX('F', 14, AF5)>, /* I2C1_SCL */ <STM32_PINMUX('F', 15, AF5)>; /* I2C1_SDA */ bias-disable; drive-open-drain; slew-rate = <0>; }; }; i2c1_pins_sleep_b: i2c1-1 { pins { pinmux = <STM32_PINMUX('F', 14, ANALOG)>, /* I2C1_SCL */ <STM32_PINMUX('F', 15, ANALOG)>; /* I2C1_SDA */ }; }; };
linux@ubuntu:$ make -j4 uImage dtbs LOADADDR=0xC2000040
系统启动后可以查看目录/sys/bus/iio/devices/
root@fsmp1a:~# ls /sys/bus/iio/devices/ iio:device0
如果系统中有多个iio设备,这里可能会有很多个iio目录,确定哪个目录是我们的设备对应目录,可以通过查看/sys/bus/iio/devices/iio\:device0/name信息确认:
root@fsmp1a:~# cat /sys/bus/iio/devices/iio\:device0/name 0-0040
由显示信息每个驱动对应设备可能有所不同,当前显示内容为设备的物理地址,与设备树中地址一致,可以确认iio:device0是当前设备对应目录 查看当目录下内容:
root@fsmp1a:~# ls -l /sys/bus/iio/devices/iio\:device0/ total 0 -r--r--r-- 1 root root 4096 Feb 7 15:51 dev -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_offset -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_raw -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_scale -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_offset -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_raw -rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_scale -r--r--r-- 1 root root 4096 Feb 7 15:51 name drwxr-xr-x 2 root root 0 Feb 7 15:51 power lrwxrwxrwx 1 root root 0 Feb 7 15:50 subsystem -> ../../../../../../../bus/iio -rw-r--r-- 1 root root 4096 Feb 7 15:50 uevent
文件说明:
文件in_ temp_scale为温度标尺,计算公式如下,公式来自与驱动对应代码:
in_ temp_offset为数据偏移,计算公式如下,公式来自于驱动对应代码:
in_ temp_raw为原始数据,计算公式如下,公式来自于驱动对应代码:
阅读SI7006芯片手册可以看到温度的计算公式为:
上述公式与驱动返回值看不出直接对应关系,所以我们按照驱动提供的scale、offset及Raw的计算公式对公式进行处理,得到最终公式计算过程如下:
编写测试程序如下:
编写测试程序或参考:“光盘资料:华清远见-FS-MP1A开发资料\02-程序源码\06-资源扩展板测试程序\03-si7006_test”
Si7006_test.c
#include <stdint.h> #include <string.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int read_sysfs_float(const char *device, const char *filename, float *val) { int ret = 0; FILE *sysfsfp; char temp[128]; memset(temp, '0', 128); ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename); if (ret < 0) goto error; sysfsfp = fopen(temp, "r"); if (!sysfsfp) { ret = -errno; goto error; } errno = 0; if (fscanf(sysfsfp, "%f\n", val) != 1) { ret = errno ? -errno : -ENODATA; if (fclose(sysfsfp)) perror("read_sysfs_float(): Failed to close dir"); goto error; } if (fclose(sysfsfp)) ret = -errno; error: return ret; } int read_sysfs_int(const char *device, const char *filename, int *val) { int ret = 0; FILE *sysfsfp; char temp[128]; memset(temp, '0', 128); ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename); if (ret < 0) goto error; sysfsfp = fopen(temp, "r"); if (!sysfsfp) { ret = -errno; goto error; } errno = 0; if (fscanf(sysfsfp, "%d\n", val) != 1) { ret = errno ? -errno : -ENODATA; if (fclose(sysfsfp)) perror("read_sysfs_float(): Failed to close dir"); goto error; } if (fclose(sysfsfp)) ret = -errno; error: return ret; } int main(int argc, char *argv[]) { int temp_raw = 0; int temp_offset = 0; float temp_scale = 0; int hum_raw = 0; int hum_offset = 0; float hum_scale = 0; while(1) { /*read temp data*/ read_sysfs_int(argv[1], "in_temp_raw", &temp_raw); read_sysfs_int(argv[1], "in_temp_offset", &temp_offset); read_sysfs_float(argv[1], "in_temp_scale", &temp_scale); printf("temperature = %.2f\n", (temp_raw + temp_offset) * temp_scale / 1000); read_sysfs_int(argv[1], "in_humidityrelative_raw", &hum_raw); read_sysfs_int(argv[1], "in_humidityrelative_offset", &hum_offset); read_sysfs_float(argv[1], "in_humidityrelative_scale", &hum_scale); printf("humidity = %.2f%%\n", (hum_raw + hum_offset) * hum_scale / 1000); sleep(1); } return 0; }
编译测试程序拷贝背到系统中并执行,效果如下:
root@fsmp1a:~# ./si7006_test iio:device0 temperature = 29.11 humidity = 32.54% temperature = 29.13 humidity = 32.55% temperature = 29.12 humidity = 32.58% temperature = 29.11 humidity = 32.59% temperature = 29.13 humidity = 32.59%