STM32 PulseSensor心跳传感器驱动代码

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秒左右,数据才稳定。

相关推荐
学习噢学个屁12 分钟前
基于51单片机的红外人体感应报警器
c语言·单片机·嵌入式硬件·51单片机
技术干货贩卖机2 小时前
0基础 | STM32 | STM32F103C8T6开发板 | 项目开发
stm32·单片机·嵌入式硬件·源代码·项目开发·0基础
Leon_George3 小时前
GPIO引脚的上拉下拉以及转换速度到底怎么选
单片机·嵌入式硬件·引脚配置·上拉下拉·引脚速度
2401_888859713 小时前
STM32 USART串口
stm32·嵌入式硬件
zhugedz4 小时前
开关电源原理
单片机·嵌入式硬件
学生小羊5 小时前
[C++] 小游戏 决战苍穹
c++·stm32·单片机
mftang5 小时前
zephyr OS架构下构建Nordic MCU boot
单片机·嵌入式硬件
百里东风5 小时前
STM32外设-GPIO输出(不含复用)
c语言·stm32·单片机·嵌入式硬件
菜只因C5 小时前
深入探索 51 单片机:从入门到实践的全面指南
单片机·嵌入式硬件