STM32CubeMX中準備工作:
1、設置AD 通道
2、設置一個定時器中斷,間隔時間2ms,我這里采用的是定時器7
3、代碼優化01
PulseSensor.c文件
#include "main.h"
#include "PulseSensor/PulseSensor.h"/******************外設部分用變量*********************/
//定時器間隔時間:2ms
volatile uint8_t DelayPulseSensor_Output=0; //500ms
//AD采樣值
volatile uint16_t ADPulseSensor; // ADC轉換值,設置為halfword 半字節格式 采樣時間為239.5周期/******************心率算法采集部分*********************/
_Bool ReadHeartRateFlag = 0; //讀取到正確心率標志位uint16_t BPM; //脈搏率==就是心率
uint16_t Signal; //傳入的原始數據。
uint16_t IBI = 600; //節拍間隔,兩次節拍之間的時間(ms)。計算:60/IBI(s)=心率(BPM)
_Bool Pulse = false; //脈沖高低標志。當脈波高時為真,低時為假。
_Bool QS = false; //當發現一個節拍時,就變成了真實
uint16_t rate[10]; //數組保存最后10個IBI值。
uint32_t sampleCounter = 0; //用于確定脈沖定時。
uint32_t lastBeatTime = 0; //用于查找IBI
uint16_t P =512; //用于在脈沖波中尋找峰值
uint16_t T = 512; //用于在脈沖波中尋找波谷
uint16_t thresh = 512; //用來尋找瞬間的心跳
uint16_t amp = 100; //用于保持脈沖波形的振幅
uint16_t Num;
_Bool firstBeat = true; //第一個脈沖節拍
_Bool secondBeat = false;//第二個脈沖節拍,這兩個變量用于確定兩個節拍void PulseSensor_Read(uint16_t PulseSensorValue)
{unsigned char i = 0;unsigned int runningTotal;// 讀取到的值右移2位,12位-->10位Signal = PulseSensorValue/4; //讀取A/D轉換數據
// Signal=Get_Adc_Average(ADC_Channel_0,1)>>2;//讀取A/D轉換數據sampleCounter = HAL_GetTick(); //利用系統滴答時鐘,單位:msNum = sampleCounter - lastBeatTime; //監控最后一次節拍后的時間,以避免噪聲 //發現脈沖波的波峰和波谷if(Signal < thresh && Num > (IBI/5)*3) //為了避免需要等待3/5個IBI的時間{ if (Signal < T) //T是閾值{T = Signal; //跟蹤脈搏波的最低點,改變閾值}}if(Signal > thresh && Signal > P) //采樣值大于閾值并且采樣值大于峰值{P = Signal; //P是峰值,改變峰值} //開始尋找心跳,現在開始尋找心跳節拍//當脈沖來臨的時候,signal的值會上升if (Num > 250) //避免高頻噪聲{ if ( (Signal > thresh) && (Pulse == false) && (Num > (IBI/5)*3) ){ Pulse = true; //當有脈沖的時候就設置脈沖信號
// LED_ON(); //打開LED,表示已經有脈沖了IBI = sampleCounter - lastBeatTime; //測量節拍的ms級的時間lastBeatTime = sampleCounter; //錄下一個脈沖的時間。if(secondBeat) //如果這是第二個節拍,如果secondBeat == TRUE,表示是第二個節拍{ secondBeat = false; //清除secondBeat節拍標志i = 0;for( i=0; i<=9; i++) //在啟動時,種子的運行總數得到一個實現的BPM。{ rate[i] = IBI; }}if(firstBeat) //如果這是第一次發現節拍,如果firstBeat == TRUE。{firstBeat = false; //清除firstBeat標志secondBeat = true; //設置secongBeat標志return; //IBI值是不可靠的,所以放棄它。} //保留最后10個IBI值的運行總數。runningTotal = 0; //清除runningTotal變量 for(i=0; i<=8; i++) //轉換數據到rate數組中{rate[i] = rate[i+1]; // 去掉舊的的IBI值。 runningTotal += rate[i]; //添加9個以前的老的IBI值。}rate[9] = IBI; //將最新的IBI添加到速率數組中。runningTotal += rate[9]; //添加最新的IBI到runningTotal。runningTotal /= 10; //平均最后10個IBI值。BPM = 60000/runningTotal; //一分鐘有多少拍。即心率BPMQS = true; //設置量化自我標志Quantified Self標志} }//脈沖開始下降if (Signal < thresh && Pulse == true) //當值下降時,節拍就結束了。{
// LED0=1; //燈滅Pulse = false; //重設脈沖標記,這樣方便下一次的計數amp = P - T; //得到脈沖波的振幅。thresh = amp/2 + T; //設置thresh為振幅的50%。P = thresh; //重新設置下一個時間T = thresh;}//沒有檢測到脈沖,設置默認值if (Num > 2500) //如果2.5秒過去了還沒有節拍{ thresh = 512; //設置默認閾值P = 512; //設置默認P值T = 512; //設置默認T值lastBeatTime = sampleCounter; //把最后的節拍跟上來。 firstBeat = true; //設置firstBeat為true方便下一次處理secondBeat = false; //設置secondBeat為false方便重新處理QS = false; //清除標志}
}/* */
uint16_t PulseSensor_Output(_Bool SensorFlag)
{uint16_t BPM_Output; //傳遞心率值if (SensorFlag) //讀取到了心率信號{ SensorFlag = 0; //清除標志 等待下一次讀取if(BPM>HEART_MIN_ERROR&&BPM<HEART_MAX_ERROR) //讀取到的值再正常心率區間 40-160內{ReadHeartRateFlag = 1; //標志位置1// printf("心率值:%d \n",BPM);BPM_Output=BPM;BPM=0;}else{ReadHeartRateFlag = 0; //標志位清零BPM_Output=0; //輸出0} }return BPM_Output;
}
PulseSensor.h文件
#ifndef __PulseSensor_H
#define __PulseSensor_H #define true 1
#define false 0#define HEART_MAX_ERROR 160 //心率的不可到值,超過此值表示傳感器出錯
#define HEART_MIN_ERROR 40 //心率的不可到值,低于此值表示傳感器出錯extern volatile uint8_t DelayPulseSensor_Output; //500ms
extern volatile uint16_t ADPulseSensor; // ADC轉換值,設置為半字節格式extern uint16_t IBI; //相鄰節拍時間
extern uint16_t BPM; //心率值
extern uint16_t Signal; //原始信號值
extern _Bool QS; //發現心跳標志void PulseSensor_Read(uint16_t PulseSensorValue);
uint16_t PulseSensor_Output(_Bool SensorFlag);
#endif
main.c中省略了設置,主要就是將取樣AD值放入定時器中斷里計算。
while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */ADPulseSensor=ADC_ConvertedValue[0]; //將AD采樣數據傳遞出去。if(DelayPulseSensor_Output >= 250) //500ms 間隔{DelayPulseSensor_Output=0;printf("心率值:%d\n",PulseSensor_Output(QS));}}/*** 函數功能: 在非阻塞模式下的周期經過的回調* 輸入參數: 無* 返 回 值: 無* 說 明:系統中各定時*/
void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef * htim)
{//TIM7 用于基礎定時計算,時間為:2msif(htim->Instance==TIM7){DelayPulseSensor_Output++; //HAL_TIM_Base_Stop_IT(&htim7); //關閉定時器PulseSensor_Read(ADPulseSensor); //讀取心跳數據HAL_TIM_Base_Start_IT(&htim7); //開啟定時器}
}
?結果如下,手按上去后,需要等個10秒左右,數據才穩定。