嵌入式系統(tǒng)是分層級的,分模塊的。
使用的硬件資源有:
IMU-IIC
采集-ADC
外置接口-串口
主控部分使用ESP32-IDF進(jìn)行開發(fā),因?yàn)樾酒拇嫫鬏^多,而且采集對的實(shí)時(shí)性有要求,所以選用freeRTOS,在滿足實(shí)時(shí)性的要求上程序的設(shè)計(jì)也會更簡單。
FreeRTOS任務(wù)設(shè)計(jì)
MAX30102采集任務(wù):初始化IIC和MAX30102,在一個死循環(huán)里面以50Hz的頻率讀取紅外線和紅光傳感器的數(shù)據(jù),進(jìn)行簡單濾波后存入隊(duì)列。
MPU6050任務(wù):初始化IIC和MPU6050,連續(xù)讀取Euler角,以20Hz的頻率進(jìn)行更新,數(shù)據(jù)處理后存入隊(duì)列。
ADC采集任務(wù):初始化ADC,以適當(dāng)采樣頻率(例如100Hz)采集模擬通道電壓,發(fā)送到隊(duì)列。
串口發(fā)送任務(wù):優(yōu)先級最低,從隊(duì)列中讀取數(shù)據(jù)并打包發(fā)送??梢栽O(shè)置一定的數(shù)據(jù)緩存。
空閑任務(wù):優(yōu)先級最低,MCU睡眠時(shí)運(yùn)行,用于切換低功耗模式。
數(shù)據(jù)同步
采用FreeRTOS的隊(duì)列和信號量機(jī)制進(jìn)行任務(wù)間同步。信號量可用于指示隊(duì)列已滿或空。
給每個數(shù)據(jù)包添加采集時(shí)間戳,上位機(jī)可以根據(jù)時(shí)間戳重新同步。
也可以僅在串口發(fā)送任務(wù)中合并時(shí)間戳,不在各個采集任務(wù)中添加。
低功耗設(shè)計(jì)
利用調(diào)度器suspend/resume接口暫停任務(wù)實(shí)現(xiàn)睡眠喚醒。
使用內(nèi)部PERIPH FIFO buffer,減少IIC任務(wù)調(diào)用。
串口使用DMA傳輸,CPU僅在發(fā)送完一個包后進(jìn)行復(fù)位。
關(guān)閉不需要的外設(shè)時(shí)鐘。利用IDLE調(diào)度鉤子函數(shù)實(shí)現(xiàn)自動降頻。
模塊化設(shè)計(jì)
獨(dú)立通信模塊,內(nèi)部封裝串口通信的復(fù)雜度。
采集核心模塊只輸出統(tǒng)一格式的采集數(shù)據(jù)。
模塊間使用統(tǒng)一的隊(duì)列/緩存接口進(jìn)行數(shù)據(jù)交換。
這里給出采集的樣板任務(wù)
針對MAX30102的芯片,更多的技術(shù)細(xì)節(jié)是:首先配置傳感器工作在FIFO模式下然后周期性讀取FIFO,通過1024點(diǎn)的FFT變換得到頻域數(shù)據(jù),然后選擇頻帶內(nèi)的最高幅值為心率,通過對比兩個幅值的幅度計(jì)算出血氧飽和度。通過平均其他頻點(diǎn)的差值來標(biāo)定兩個波長數(shù)據(jù)。
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; // 將數(shù)據(jù)寫入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++; } } // 信息更新標(biāo)志位 g_blooddata.update++; } } // 血液信息轉(zhuǎn)換 void blood_data_translate(void) { // 緩沖區(qū)寫入結(jié)束 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); } // 讀取峰值點(diǎn) 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) { // 心率計(jì)算 uint16_t Heart_Rate = 60 * SAMPLES_PER_SECOND * s2_max_index / FFT_N; g_blooddata.heart = Heart_Rate; // 血氧含量計(jì)算 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; // 狀態(tài)正常 g_blooddata.state = BLD_NORMAL; } else // 數(shù)據(jù)發(fā)生異常 { g_blooddata.heart = 0; g_blooddata.SpO2 = 0; g_blooddata.state = BLD_ERROR; } g_fft_index = 0; } }
因?yàn)镻PG的數(shù)據(jù)處理是難點(diǎn),以上給出一段處理代碼,但是還有優(yōu)化的空間。
可以創(chuàng)建獨(dú)立的采集模塊和處理模塊,采集模塊專注獲取傳感器數(shù)據(jù),處理模塊實(shí)現(xiàn)算法邏輯。兩者通過統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)進(jìn)行交互。這可以提高代碼的模塊化和可維護(hù)性。
優(yōu)化數(shù)據(jù)濾波方式
當(dāng)前的平均濾波可以考慮改為滾動平均濾波,這樣可以加快數(shù)據(jù)更新的響應(yīng)速度。同時(shí)可以引入一階IIR濾波來平滑數(shù)據(jù)。
優(yōu)化FFT實(shí)現(xiàn)
可以考慮使用更優(yōu)化的FFT庫,或者直接調(diào)用DSP庫的FFT函數(shù),提高運(yùn)算效率。當(dāng)前的FFTBUFFER可以改為復(fù)數(shù)數(shù)組,簡化運(yùn)算。
血氧算法可進(jìn)一步優(yōu)化
血氧計(jì)算中使用了簡單的比值法,可以參考更復(fù)雜的算法來提高精度,比如考慮LED功率補(bǔ)償?shù)取?/p>
添加參數(shù)配置接口
例如采樣率、FFT長度、濾波參數(shù)等可以設(shè)計(jì)成可配置的,而不是硬編碼的數(shù)字。這樣可以更靈活地調(diào)整參數(shù)。
優(yōu)化數(shù)據(jù)包發(fā)送流程
可以考慮使用FreeRTOS隊(duì)列來緩存要發(fā)送的數(shù)據(jù),發(fā)送任務(wù)從隊(duì)列中獲取數(shù)據(jù)。這可以避免直接在中斷中發(fā)送造成的阻塞。
增加狀態(tài)機(jī)管理
可以設(shè)計(jì)一個狀態(tài)機(jī)來管理整個采集和處理的流程,例如初始化狀態(tài),檢測狀態(tài),發(fā)送狀態(tài)等。這可以使代碼流程更清晰。
-
嵌入式
+關(guān)注
關(guān)注
5141文章
19524瀏覽量
314792 -
adc
+關(guān)注
關(guān)注
99文章
6635瀏覽量
548223 -
采集系統(tǒng)
+關(guān)注
關(guān)注
0文章
173瀏覽量
20999 -
PPG
+關(guān)注
關(guān)注
2文章
68瀏覽量
18564 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
488瀏覽量
63728
原文標(biāo)題:PPG采集系統(tǒng)-嵌入式軟件設(shè)計(jì)思路
文章出處:【微信號:TT1827652464,微信公眾號:云深之無跡】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
掌握嵌入式系統(tǒng)軟件設(shè)計(jì)方法
嵌入式系統(tǒng)軟件設(shè)計(jì)的原則是什么
基于ARM的嵌入式系統(tǒng)軟件設(shè)計(jì)

基于嵌入式的腦卒中康復(fù)儀的軟件設(shè)計(jì)
ARM嵌入式系統(tǒng)開發(fā)-軟件設(shè)計(jì)與優(yōu)化

嵌入式USB主機(jī)設(shè)計(jì)(硬件設(shè)計(jì)和軟件設(shè)計(jì))

采用構(gòu)件技術(shù)的嵌入式系統(tǒng)復(fù)用軟件設(shè)計(jì)

嵌入式系統(tǒng)智能鍵盤的軟件設(shè)計(jì)

嵌入式軟件設(shè)計(jì)之設(shè)計(jì)模式

嵌入式軟件設(shè)計(jì)的原則分享

評論