
嵌入式系統(tǒng)是分層級的,分模塊的。
使用的硬件資源有:
IMU-IIC
采集-ADC
外置接口-串口
主控部分使用ESP32-IDF進行開發(fā),因為芯片寄存器較多,而且采集對的實時性有要求,所以選用freeRTOS,在滿足實時性的要求上程序的設計也會更簡單。
FreeRTOS任務設計
MAX30102采集任務:初始化IIC和MAX30102,在一個死循環(huán)里面以50Hz的頻率讀取紅外線和紅光傳感器的數(shù)據(jù),進行簡單濾波后存入隊列。
MPU6050任務:初始化IIC和MPU6050,連續(xù)讀取Euler角,以20Hz的頻率進行更新,數(shù)據(jù)處理后存入隊列。
ADC采集任務:初始化ADC,以適當采樣頻率(例如100Hz)采集模擬通道電壓,發(fā)送到隊列。
串口發(fā)送任務:優(yōu)先級最低,從隊列中讀取數(shù)據(jù)并打包發(fā)送。可以設置一定的數(shù)據(jù)緩存。
空閑任務:優(yōu)先級最低,MCU睡眠時運行,用于切換低功耗模式。
數(shù)據(jù)同步
采用FreeRTOS的隊列和信號量機制進行任務間同步。信號量可用于指示隊列已滿或空。
給每個數(shù)據(jù)包添加采集時間戳,上位機可以根據(jù)時間戳重新同步。
也可以僅在串口發(fā)送任務中合并時間戳,不在各個采集任務中添加。
低功耗設計
利用調(diào)度器suspend/resume接口暫停任務實現(xiàn)睡眠喚醒。
使用內(nèi)部PERIPH FIFO buffer,減少IIC任務調(diào)用。
串口使用DMA傳輸,CPU僅在發(fā)送完一個包后進行復位。
關(guān)閉不需要的外設時鐘。利用IDLE調(diào)度鉤子函數(shù)實現(xiàn)自動降頻。
模塊化設計
獨立通信模塊,內(nèi)部封裝串口通信的復雜度。
采集核心模塊只輸出統(tǒng)一格式的采集數(shù)據(jù)。
模塊間使用統(tǒng)一的隊列/緩存接口進行數(shù)據(jù)交換。


這里給出采集的樣板任務
針對MAX30102的芯片,更多的技術(shù)細節(jié)是:首先配置傳感器工作在FIFO模式下然后周期性讀取FIFO,通過1024點的FFT變換得到頻域數(shù)據(jù),然后選擇頻帶內(nèi)的最高幅值為心率,通過對比兩個幅值的幅度計算出血氧飽和度。通過平均其他頻點的差值來標定兩個波長數(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++;
}
}
// 信息更新標志位
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);
}
// 讀取峰值點 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;
// 狀態(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;
}
}
因為PPG的數(shù)據(jù)處理是難點,以上給出一段處理代碼,但是還有優(yōu)化的空間。
可以創(chuàng)建獨立的采集模塊和處理模塊,采集模塊專注獲取傳感器數(shù)據(jù),處理模塊實現(xiàn)算法邏輯。兩者通過統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)進行交互。這可以提高代碼的模塊化和可維護性。
優(yōu)化數(shù)據(jù)濾波方式
當前的平均濾波可以考慮改為滾動平均濾波,這樣可以加快數(shù)據(jù)更新的響應速度。同時可以引入一階IIR濾波來平滑數(shù)據(jù)。
優(yōu)化FFT實現(xiàn)
可以考慮使用更優(yōu)化的FFT庫,或者直接調(diào)用DSP庫的FFT函數(shù),提高運算效率。當前的FFTBUFFER可以改為復數(shù)數(shù)組,簡化運算。
血氧算法可進一步優(yōu)化
血氧計算中使用了簡單的比值法,可以參考更復雜的算法來提高精度,比如考慮LED功率補償?shù)取?/p>
添加參數(shù)配置接口
例如采樣率、FFT長度、濾波參數(shù)等可以設計成可配置的,而不是硬編碼的數(shù)字。這樣可以更靈活地調(diào)整參數(shù)。
優(yōu)化數(shù)據(jù)包發(fā)送流程
可以考慮使用FreeRTOS隊列來緩存要發(fā)送的數(shù)據(jù),發(fā)送任務從隊列中獲取數(shù)據(jù)。這可以避免直接在中斷中發(fā)送造成的阻塞。
增加狀態(tài)機管理
可以設計一個狀態(tài)機來管理整個采集和處理的流程,例如初始化狀態(tài),檢測狀態(tài),發(fā)送狀態(tài)等。這可以使代碼流程更清晰。


-
嵌入式
+關(guān)注
關(guān)注
5198文章
20442瀏覽量
333986 -
adc
+關(guān)注
關(guān)注
100文章
7511瀏覽量
555923 -
采集系統(tǒng)
+關(guān)注
關(guān)注
0文章
180瀏覽量
22001 -
PPG
+關(guān)注
關(guān)注
2文章
70瀏覽量
19120 -
FreeRTOS
+關(guān)注
關(guān)注
14文章
499瀏覽量
66931
原文標題:PPG采集系統(tǒng)-嵌入式軟件設計思路
文章出處:【微信號:TT1827652464,微信公眾號:云深之無跡】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
掌握嵌入式系統(tǒng)軟件設計方法
嵌入式系統(tǒng)軟件設計的原則是什么
基于ARM的嵌入式系統(tǒng)軟件設計
基于嵌入式的腦卒中康復儀的軟件設計
嵌入式USB主機設計(硬件設計和軟件設計)
采用構(gòu)件技術(shù)的嵌入式系統(tǒng)復用軟件設計
嵌入式系統(tǒng)智能鍵盤的軟件設計
嵌入式軟件設計之設計模式
嵌入式軟件設計的原則分享
嵌入式軟件設計之PPG采集系統(tǒng)
評論