嵌入式系統是分層級的,分模塊的。
使用的硬件資源有:
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隊列來緩存要發送的數據,發送任務從隊列中獲取數據。這可以避免直接在中斷中發送造成的阻塞。
增加狀態機管理
可以設計一個狀態機來管理整個采集和處理的流程,例如初始化狀態,檢測狀態,發送狀態等。這可以使代碼流程更清晰。
-
嵌入式
+關注
關注
5094文章
19183瀏覽量
307792 -
adc
+關注
關注
99文章
6534瀏覽量
545787 -
采集系統
+關注
關注
0文章
171瀏覽量
20706 -
PPG
+關注
關注
2文章
67瀏覽量
18246 -
FreeRTOS
+關注
關注
12文章
484瀏覽量
62403
原文標題:PPG采集系統-嵌入式軟件設計思路
文章出處:【微信號:TT1827652464,微信公眾號:云深之無跡】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論