基于STM32F103的MAX86150驱动移植与多模式数据采集实战
1. MAX86150芯片与STM32F103开发基础MAX86150这颗芯片确实是个好东西它把ECG心电图、PPG光电容积图也就是血氧检测用到的技术、心率监测这些功能都集成到了一起。我最早是在一个智能手环项目上用到它当时对比了好几款传感器最终选择MAX86150就是看中了它的高集成度和低功耗特性。先说几个关键参数工作电压1.8VLED驱动有独立供电支持-40℃到85℃的工作温度内置环境光抑制电路采样率最高可达1600HzPPG模式在实际项目中我发现STM32F103系列和MAX86150是绝配。F103的72MHz主频完全够用而且它的GPIO操作速度足够快可以很好地模拟I2C时序。这里有个小技巧如果你用的是C8T6这类Flash容量较小的型号记得在Keil里把优化等级调到-O2可以节省不少空间。2. 驱动移植的关键要点2.1 硬件接口设计先说说硬件连接这个搞错了后面全白搭。MAX86150的典型电路其实很简单SDA接PB7SCL接PB6INT接任意GPIO我用的是PA0电源记得加0.1μF去耦电容这里有个坑我踩过STM32的I2C引脚是5V容忍的但MAX86150是1.8V器件所以中间最好加个电平转换芯片。如果为了省成本不加至少要在SDA/SCL上拉电阻取值大些我用的是4.7kΩ。2.2 I2C驱动适配原始代码用的是IO模拟I2C这个方案最大的好处就是移植性强。我在不同型号的F103上都试过C8T6、R8T6、ZET6只需要改下GPIO定义就能用。下面是关键函数改造示例// 修改后的GPIO初始化 void I2C_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD; // 开漏输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); I2C_SCL_H; I2C_SDA_H; }实测下来这种实现方式在400kHz的Fast Mode下也能稳定工作。如果遇到通信失败建议先用逻辑分析仪抓波形重点看起始信号和ACK时序。3. 多模式数据采集实战3.1 ECG单模式配置ECG配置要注意三个关键寄存器ECG_CONFIG10x3C设置采样率和带宽ECG_CONFIG20x3E配置增益FIFO_DATA_CTRL0x09/0x0A定义数据格式这是我的推荐配置void MAX86150_ECG_Init(void) { // 1600Hz采样率带宽最宽 MAX86150_WriteReg(MAX86150_ECG_CONFIG1, MAX86150_ECG_ADC_CLK_OSR_1600); // PGA增益设为1仪表放大器增益50 MAX86150_WriteReg(MAX86150_ECG_CONFIG2, MAX86150_ECG_PGA_GAIN_1 | MAX86150_ECG_IA_GAIN_50); // 配置FIFO只存储ECG数据 MAX86150_WriteReg(MAX86150_REG_FIFO_DATA_CTRL_1, MAX86150_FIFO_DATA_CTRLX_FD1_ECG); }采集到的数据是18位有符号数需要做转换int32_t ProcessECGData(uint32_t raw) { // 扩展符号位 if(raw 0x20000) { raw | 0xFFFC0000; } return (int32_t)raw; }3.2 PPG血氧模式配置PPG模式要复杂些因为涉及LED驱动配置。关键步骤设置LED电流REG_LED1_PA/REG_LED2_PA配置PPG采样参数PPG_CONFIG1/2定义FIFO数据格式典型配置代码void MAX86150_PPG_Init(void) { // LED1红光设为10mALED2红外设为8mA MAX86150_WriteReg(MAX86150_REG_LED1_PA, MAX86150_LEDX_RANGE_100_10mA); MAX86150_WriteReg(MAX86150_REG_LED2_PA, MAX86150_LEDX_RANGE_100_8mA); // 400Hz采样率50us脉冲宽度 MAX86150_WriteReg(MAX86150_PPG_CONFIG1, MAX86150_PPG_ADC_RGE_4096 | MAX86150_PPG_SR_400HZ | MAX86150_PPG_LED_PW_50US); // 配置FIFO存储两个LED的数据 MAX86150_WriteReg(MAX86150_REG_FIFO_DATA_CTRL_1, MAX86150_FIFO_DATA_CTRLX_FD2_PPG_LED1 | MAX86150_FIFO_DATA_CTRLX_FD1_PPG_LED2); }读取PPG数据时要注意两个LED的数据是交替存储的void ReadPPGData(uint32_t *led1, uint32_t *led2) { uint8_t buf[6]; MAX86150_ReadData(MAX86150_REG_FIFO_DATA, buf, 6); *led1 ((buf[0]16) | (buf[1]8) | buf[2]) 0x7FFFF; *led2 ((buf[3]16) | (buf[4]8) | buf[5]) 0x7FFFF; }3.3 同步采集模式实现同步采集ECG和PPG是最实用的模式但配置要更精细。关键点在于采样率要匹配建议ECG 1600HzPPG 400HzFIFO配置要正确数据解析要考虑时间对齐配置示例void MAX86150_MultiMode_Init(void) { // 初始化ECG部分 MAX86150_WriteReg(MAX86150_ECG_CONFIG1, MAX86150_ECG_ADC_CLK_OSR_1600); MAX86150_WriteReg(MAX86150_ECG_CONFIG2, MAX86150_ECG_PGA_GAIN_1); // 初始化PPG部分 MAX86150_WriteReg(MAX86150_REG_LED1_PA, MAX86150_LEDX_RANGE_100_10mA); MAX86150_WriteReg(MAX86150_PPG_CONFIG1, MAX86150_PPG_ADC_RGE_4096 | MAX86150_PPG_SR_400HZ); // 配置FIFO MAX86150_WriteReg(MAX86150_REG_FIFO_DATA_CTRL_1, MAX86150_FIFO_DATA_CTRLX_FD2_PPG_LED1 | MAX86150_FIFO_DATA_CTRLX_FD1_PPG_LED2); MAX86150_WriteReg(MAX86150_REG_FIFO_DATA_CTRL_2, MAX86150_FIFO_DATA_CTRLX_FD1_ECG); }读取数据时要按顺序解析typedef struct { uint32_t ppg_ir; uint32_t ppg_red; int32_t ecg; } SampleData; void ReadMultiModeData(SampleData *sample) { uint8_t buf[9]; MAX86150_ReadData(MAX86150_REG_FIFO_DATA, buf, 9); sample-ppg_red ((buf[0]16) | (buf[1]8) | buf[2]) 0x7FFFF; sample-ppg_ir ((buf[3]16) | (buf[4]8) | buf[5]) 0x7FFFF; // ECG数据处理要考虑符号位 uint32_t ecg_raw ((buf[6]16) | (buf[7]8) | buf[8]) 0x3FFFF; sample-ecg (ecg_raw 0x20000) ? (ecg_raw | 0xFFFC0000) : ecg_raw; }4. 数据采集优化技巧4.1 FIFO使用策略MAX86150的FIFO深度是32个样本但每个样本的组成取决于配置。在同步模式下一个样本包含3字节PPGLED13字节PPGLED23字节ECG所以实际能存储的样本数会少很多。我的经验是设置FIFO几乎满中断阈值A_FULL为8-12这样既能保证数据连续性又不会频繁中断。配置示例// 设置FIFO几乎满阈值为10 MAX86150_WriteReg(MAX86150_REG_FIFO_CFG, 0x0A);4.2 数据同步处理由于ECG和PPG采样率不同比如4:1需要在软件层做数据对齐。我通常的做法为ECG数据维护一个4样本的缓存每次读取PPG数据时取ECG缓存的平均值使用硬件定时器精确控制采样间隔示例代码#define ECG_BUFFER_SIZE 4 int32_t ecg_buffer[ECG_BUFFER_SIZE]; uint8_t ecg_index 0; void ProcessSamples(void) { SampleData raw; ReadMultiModeData(raw); // 存储ECG数据 ecg_buffer[ecg_index] raw.ecg; if(ecg_index ECG_BUFFER_SIZE) { ecg_index 0; } // 计算平均ECG值 int32_t avg_ecg 0; for(int i0; iECG_BUFFER_SIZE; i) { avg_ecg ecg_buffer[i]; } avg_ecg / ECG_BUFFER_SIZE; // 此时avg_ecg和raw.ppg_ir/raw.ppg_red就是同步的数据 }4.3 功耗优化在电池供电的设备中功耗是关键。MAX86150有几个省电技巧动态调整LED电流根据信号质量自适应使用SHUTDOWN模式0x0D寄存器降低采样率但要注意信号质量实测下来在PPG 100Hz LED 50mA配置下整机电流可以控制在1mA以内。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2601421.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!