扩展板空气温湿度传感器驱动移植

来自华清远见研发中心
FarSight讨论 | 贡献2021年3月24日 (三) 08:57的版本 实验步骤

(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转至: 导航搜索

实验原理

打开扩展板原理图对照扩展板可以看到扩展板有1个温湿度传感器SI7006,如下图:

62-1-1.png

由上图可见可通过I2C总线与SI7006通信。

62-1-2.png
扩展板与底板接口对照图

查看原理图可知数据线I2C1_SDA、I2C1_SCL和I2C_INT1管脚对应关系如下:

原理图网络编号 对应管脚 管脚功能 管脚功能码
I2C1_SCL PF14 I2C1_SCL AF5
I2C1_SDA PF15 I2C1_SDA AF5

查看SI7006芯片手册确认设备七位从机地址为:0x40

62-1-3.png

实验平台

华清远见开发环境,FS-MP1A平台

实验步骤

  1. 导入交叉编译工具链
  2. linux@ubuntu:$ source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
    
  3. 内核配置
  4. 内核中没有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
    
  5. 修改设备树
  6. 参考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 */
    		};
    	};
    };
    
    
  7. 重新编译内核和设备树文件
  8. linux@ubuntu:$ make -j4 uImage dtbs LOADADDR=0xC2000040
    
  9. 更新系统内核和设备树
  10. 测试
  11. 系统启动后可以查看目录/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为温度标尺,计算公式如下,公式来自与驱动对应代码:

    62-3-1.png

    Scaletemp=(175.72×1000×4)/65535=10.725097656

    in_ temp_offset为数据偏移,计算公式如下,公式来自于驱动对应代码:

    62-3-2.png

    offsettemp=(-46.85×65536)/(4×175.72)=-4368

    in_ temp_raw为原始数据,计算公式如下,公式来自于驱动对应代码:

    62-3-3.png

    Rawtemp=Codetemp/4

    阅读SI7006芯片手册可以看到温度的计算公式为:

    Temperature(℃)=(175.72×Codetemp)/65535-46.85

    上述公式与驱动返回值看不出直接对应关系,所以我们按照驱动提供的scale、offset及Raw的计算公式对公式进行处理,得到最终公式计算过程如下:

    62-3-4.png

    编写测试程序如下:

    编写测试程序或参考:“光盘资料:华清远见-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%