“扩展板空气温湿度传感器驱动移植”的版本间的差异
(→实验原理) |
(→实验步骤) |
||
| (未显示同一用户的2个中间版本) | |||
| 第30行: | 第30行: | ||
==实验平台== | ==实验平台== | ||
| + | 华清远见开发环境,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%