1.实验原理
打开扩展板原理图对照扩展板可以看到扩展板有
1
个心率
/
血氧传感器,如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/578164211621463643587ed3dba4885e.png)
由上图可见可通过 I2C 总线与 MAX30102 通信。
查看原理图可知数据线 I2C1_SDA、I2C1_SCL 和 I2C_INT1 管脚对应关系如下:
查看 Max30102 芯片手册确认设备七位从机地址为:0x57
2.实验步骤
1) 导入交叉编译工具链
linux@ubuntu:$ source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environm ent-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
2) 内核配置
内核中提供了
max30102
的驱动,只要在内核中配置对应选项即可,驱动路径为:
drivers/iio/health/max30102.c
执行
make menuconfig
配置内核对应选项
linux@ubuntu:$ make menuconfigDevice Drivers ---><*> Industrial I/O support --->Health Sensors --->Heart Rate Monitors ---><*> MAX30102 heart rate and pulse oximeter sensor
3) 修改设备树
参考
linux
内核文档:
Documentation/devicetree/bindings/i2c/i2c-stm32.txtDocumentation/devicetree/bindings/iio/health/max30102.txt
修改设备树文件:
arch/arm/boot/dts/stm32mp157a-fsmp1a-extended.dts
由于
i2c1
相关内容上节已经做了添加,本节只需在原有基础添加与硬件对应的相关信息 即可,在文件 stm32mp157a-fsmp1a-extended.dts
的
i2c1
节点添加
max30102
内容,红色字体部分为添加内容:
&i2c1 { …… ap3216c: ap3216c@1e { compatible = "liteon,ap3216c"; reg = <0x1e>; interrupts = <13 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpiof>; }; max30102: max30102@57 { compatible = "maxim,max30102"; reg = <0x57>; maxim,red-led-current-microamp = <7000>; maxim,ir-led-current-microamp = <7000>; interrupt-parent = <&gpiob>; interrupts = <12 IRQ_TYPE_EDGE_FALLING >; }; };
4) 重新编译内核和设备树文件
linux@ubuntu:$ make -j4 uImage dtbs LOADADDR=0xC2000040
5) 更新系统内核和设备树
6) 测试
系统启动后可以查看目录
/sys/bus/iio/devices/
root@fsmp1a:~# ls /sys/bus/iio/devices/iio:device0 iio:device1
如果系统中有多个
iio
设备,这里可能会有很多个
iio
设备目录,确定哪个目录是我们的设备对应目录,可以通过查看/sys/bus/iio/devices/iio:device*/name
信息确认
:
root@fsmp1a:~# cat /sys/bus/iio/devices/iio:device*/name0-0040ap3216cmax30102
由显示信息可以确认
iio:device2
是当前设备对应目录
max30102
在
/sys
目录下的内容如下:
root@fsmp1a:~# ls /sys/bus/iio/devices/iio\:device2/ -R /sys/bus/iio/devices/iio:device2/: buffer dev in_temp_raw in_temp_scale name of_node power scan_elements subsystem uevent /sys/bus/iio/devices/iio:device2/buffer: data_available enable length watermark /sys/bus/iio/devices/iio:device2/power: autosuspend_delay_ms control runtime_active_time runtime_status runtime_suspended_time /sys/bus/iio/devices/iio:device2/scan_elements: in_intensity_ir_en in_intensity_ir_type in_intensity_red_index in_intensity_ir_index in_intensity_red_en in_intensity_red_type
上述文件多为
max30102
驱动提供的属性文件及使能文件,由于心率并不像前文环境光及接近传感器那样数据比较简单,并不能通过读取一个文件获得当前的心率或血氧,而是通过一系列的数据计算出来的,这样 sys
文件系统的接口就不能完全适用了,所以需要结合系统调用以字符设备的方式获取对应数据,然后在计算出心率和血氧。
关键文件说明:
/sys/bus/iio/devices/iio:device2/scan_elements/(in_intensity_ir_en/in_intensity_red_en):通道使能,通过写 1 或 0 使能或关闭通道。/sys/bus/iio/devices/iio:device2/scan_elements/(in_intensity_ir_type/in_intensity_red_type):通道数据类型,读取格式为: be:u18/32>>8 , be 表示数据为大端存储 (big endian) 、 u 表示数据为无符号数, 18/32>>8 表示 32 位数据左移 8 位后有效数据为 18 位。/sys/bus/iio/devices/iio:device2/scan_elements/(in_intensity_ir_index/in_intensity_red_index):读取数据为当前通道的序号, red 为 0 通道, ir 为 1 通道/sys/bus/iio/devices/iio:device2/buffer/length :写入数据用来设置缓存区样本数量。/sys/bus/iio/devices/iio:device2/buffer/enable :写入 1 或 0 用来使能设备采样或停止设备采样。
7) 编写测试程序
max30102_test.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #define DATA_MASK ((1 << 18) - 1) #define DATA_SHIFT 8 #define DATA_BUF_SIZE 400 char device_name[128]; unsigned int aun_red_buf[DATA_BUF_SIZE]; unsigned int aun_ir_buf[DATA_BUF_SIZE]; extern void maxim_heart_rate_and_oxygen_saturation(unsigned int *pun_ir_buffer , int n_ir_buffer_length, unsigned int *pun_red_buffer , int *pn_spo2, int *pch_spo2_valid , int *pn_heart_rate , int *pch_hr_valid); int write_sys_int(char *filename, int data) { int ret = 0; FILE *filp; //printf("filename = %s\n", filename); filp = fopen(filename, "w"); if (!filp) { ret = -errno; fprintf(stderr, "failed to open %s\n", filename); goto error; } ret = fprintf(filp, "%d", data); if (ret < 0) { if (fclose(filp)) perror("_write_sysfs_int(): Failed to close dir"); goto error; } if (fclose(filp)) { ret = -errno; goto error; } return 0; error: return ret; } int enable_disable_all_channels(int enable) { int ret = 0; char red_enable[128] = {0}; char ir_enable[128] = {0}; sprintf(red_enable,"/sys/bus/iio/devices/%s/scan_elements/in_intensity_red_en", device_name); sprintf(ir_enable, "/sys/bus/iio/devices/%s/scan_elements/in_intensity_ir_en", device_name); ret = write_sys_int(red_enable, enable); if (ret < 0) { fprintf(stderr, "Failed to enable/disable %s", red_enable); return ret; } ret = write_sys_int(ir_enable, enable); if (ret < 0) { fprintf(stderr, "Failed to enable/disable %s", ir_enable); return ret; } return 0; } int enable_disable_buffer(int enable) { int ret = 0;