STM32CubeMX中准备工作:
1、设置AD 通道
2、设置一个定时器中断,间隔时间2ms,我这里采用的是定时器7
3、代码优化01

PulseSensor.c文件
cpp
#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(); //利用系统滴答时钟,单位:ms
Num = 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; //一分钟有多少拍。即心率BPM
QS = 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文件
cpp
#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值放入定时器中断里计算。
cpp
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 用于基础定时计算,时间为:2ms
if(htim->Instance==TIM7)
{
DelayPulseSensor_Output++; //
HAL_TIM_Base_Stop_IT(&htim7); //关闭定时器
PulseSensor_Read(ADPulseSensor); //读取心跳数据
HAL_TIM_Base_Start_IT(&htim7); //开启定时器
}
}
结果如下,手按上去后,需要等个10秒左右,数据才稳定。
