嵌入式系统是分层级的,分模块的。
使用的硬件资源有:
IMU-IIC
采集-ADC
外置接口-串口
主控部分使用ESP32-IDF进行开发,因为芯片寄存器较多,而且采集对的实时性有要求,所以选用freeRTOS,在满足实时性的要求上程序的设计也会更简单。
FreeRTOS任务设计
MAX30102采集任务:初始化IIC和MAX30102,在一个死循环里面以50Hz的频率读取红外线和红光传感器的数据,进行简单滤波后存入队列。
MPU6050任务:初始化IIC和MPU6050,连续读取Euler角,以20Hz的频率进行更新,数据处理后存入队列。
ADC采集任务:初始化ADC,以适当采样频率(例如100Hz)采集模拟通道电压,发送到队列。
串口发送任务:优先级最低,从队列中读取数据并打包发送。可以设置一定的数据缓存。
空闲任务:优先级最低,MCU睡眠时运行,用于切换低功耗模式。
数据同步
采用FreeRTOS的队列和信号量机制进行任务间同步。信号量可用于指示队列已满或空。
给每个数据包添加采集时间戳,上位机可以根据时间戳重新同步。
也可以仅在串口发送任务中合并时间戳,不在各个采集任务中添加。
低功耗设计
利用调度器suspend/resume接口暂停任务实现睡眠唤醒。
使用内部PERIPH FIFO buffer,减少IIC任务调用。
串口使用DMA传输,CPU仅在发送完一个包后进行复位。
关闭不需要的外设时钟。利用IDLE调度钩子函数实现自动降频。
模块化设计
独立通信模块,内部封装串口通信的复杂度。
采集核心模块只输出统一格式的采集数据。
模块间使用统一的队列/缓存接口进行数据交换。
这里给出采集的样板任务
针对MAX30102的芯片,更多的技术细节是:首先配置传感器工作在FIFO模式下然后周期性读取FIFO,通过1024点的FFT变换得到频域数据,然后选择频带内的最高幅值为心率,通过对比两个幅值的幅度计算出血氧饱和度。通过平均其他频点的差值来标定两个波长数据。
struct compx FFTBUF1[FFT_N + 16]; struct compx FFTBUF2[FFT_N + 16]; uint16_t g_fft_index = 0; BloodData g_blooddata = {0}; void test(float data1, float data2) { static uint8_t str[50]; sprintf((char *)str, "%f,%f ", data1, data2); HAL_UART_Transmit_DMA(&huart1, str, sizeof(str)); } // 血液检测信息更新 void blood_data_update(void) { static DC_FilterData dc1 = {.w = 0, .init = 0, .a = 0.8}; static DC_FilterData dc2 = {.w = 0, .init = 0, .a = 0.8}; static float data1buf[20]; static uint8_t data1cur = 0; static float data2buf[20]; static uint8_t data2cur = 0; uint16_t temp_num = 0; uint16_t fifo_word_buff[1][2]; temp_num = max30100_Bus_Read(INTERRUPT_REG); if (INTERRUPT_REG_A_FULL & temp_num) { max30100_FIFO_Read(0x05, fifo_word_buff, 1); // read the hr and spo2 data form fifo in reg=0x05 float data1 = dc_filter(fifo_word_buff[0][0], &dc1) + 100.0; float data2 = dc_filter(fifo_word_buff[0][1], &dc2) + 100.0; data1buf[data1cur] = data1; data2buf[data2cur] = data2; data1 = 0; data2 = 0; for (int i = 0; i < 20; i++) { data1 += data1buf[i]; data2 += data2buf[i]; } data1 /= 20; data2 /= 20; data1cur = (data1cur < 19) ? data1cur + 1 : 0; data2cur = (data2cur < 19) ? data2cur + 1 : 0; g_blooddata.hb = data1 + 50; g_blooddata.hbo2 = data2 + 50; // 将数据写入fft输入并清除输出 for (int i = 0; i < 1; i++) { if (g_fft_index < FFT_N) { FFTBUF1[g_fft_index].real = fifo_word_buff[i][0]; FFTBUF1[g_fft_index].imag = 0; FFTBUF2[g_fft_index].real = fifo_word_buff[i][1]; FFTBUF2[g_fft_index].imag = 0; g_fft_index++; } } // 信息更新标志位 g_blooddata.update++; } } // 血液信息转换 void blood_data_translate(void) { // 缓冲区写入结束 if (g_fft_index >= FFT_N) { // 快速傅里叶变换 FFT(FFTBUF1); FFT(FFTBUF2); // 解平方 for (int i = 0; i < FFT_N; i++) { FFTBUF1[i].real = sqrtf(FFTBUF1[i].real * FFTBUF1[i].real + FFTBUF1[i].imag * FFTBUF1[i].imag); FFTBUF2[i].real = sqrtf(FFTBUF2[i].real * FFTBUF2[i].real + FFTBUF2[i].imag * FFTBUF2[i].imag); } // 读取峰值点 10-100带通 频率范围30-292次/分钟 uint16_t s1_max_index = find_max_num_index(FFTBUF1, 100); uint16_t s2_max_index = find_max_num_index(FFTBUF2, 100); // 检查HbO2和Hb的变化频率是否一致 if (s1_max_index == s2_max_index) { // 心率计算 uint16_t Heart_Rate = 60 * SAMPLES_PER_SECOND * s2_max_index / FFT_N; g_blooddata.heart = Heart_Rate; // 血氧含量计算 float sp02_num = (FFTBUF1[s1_max_index].real * FFTBUF1[0].real) / (FFTBUF2[s1_max_index].real * FFTBUF2[0].real); sp02_num = sp02_num * SAMPLES_PER_SECOND + CORRECTED_VALUE; g_blooddata.SpO2 = sp02_num; // 状态正常 g_blooddata.state = BLD_NORMAL; } else // 数据发生异常 { g_blooddata.heart = 0; g_blooddata.SpO2 = 0; g_blooddata.state = BLD_ERROR; } g_fft_index = 0; } }
因为PPG的数据处理是难点,以上给出一段处理代码,但是还有优化的空间。
可以创建独立的采集模块和处理模块,采集模块专注获取传感器数据,处理模块实现算法逻辑。两者通过统一的数据结构进行交互。这可以提高代码的模块化和可维护性。
优化数据滤波方式
当前的平均滤波可以考虑改为滚动平均滤波,这样可以加快数据更新的响应速度。同时可以引入一阶IIR滤波来平滑数据。
优化FFT实现
可以考虑使用更优化的FFT库,或者直接调用DSP库的FFT函数,提高运算效率。当前的FFTBUFFER可以改为复数数组,简化运算。
血氧算法可进一步优化
血氧计算中使用了简单的比值法,可以参考更复杂的算法来提高精度,比如考虑LED功率补偿等。
添加参数配置接口
例如采样率、FFT长度、滤波参数等可以设计成可配置的,而不是硬编码的数字。这样可以更灵活地调整参数。
优化数据包发送流程
可以考虑使用FreeRTOS队列来缓存要发送的数据,发送任务从队列中获取数据。这可以避免直接在中断中发送造成的阻塞。
增加状态机管理
可以设计一个状态机来管理整个采集和处理的流程,例如初始化状态,检测状态,发送状态等。这可以使代码流程更清晰。
-
嵌入式
+关注
关注
5068文章
19013浏览量
303074 -
adc
+关注
关注
98文章
6430浏览量
544046 -
采集系统
+关注
关注
0文章
168浏览量
20603 -
PPG
+关注
关注
2文章
62浏览量
18125 -
FreeRTOS
+关注
关注
12文章
483浏览量
61993
原文标题:PPG采集系统-嵌入式软件设计思路
文章出处:【微信号:TT1827652464,微信公众号:云深之无迹】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论