STM32 PulseSensor心跳传感器驱动代码

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79561.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++项目容易犯错的点

1. 矩阵q要先定义大小&#xff0c;再赋值。不可以直接赋值。下面这种方式是错误的Eigen::MatrixXd q&#xff1b;q<<1,2&#xff1b;正确的这样的&#xff1a; Eigen::MatrixXd q(2,1); q<<1.4, 1.5; 2. 不要重复加载variables.h头文件&#xff0c;这样变量会被…

在阿里云 Ubuntu 24.04 上部署 RabbitMQ:一篇实战指南

前言 RabbitMQ 是业界常用的开源消息中间件,支持 AMQP 协议,易于部署、高可用、插件丰富。本文以阿里云 ECS 上运行的 Ubuntu 24.04 LTS 为例,手把手带你完成 RabbitMQ 从仓库配置到运行的全流程,并分享在国内环境下常见的坑与对应解决方案。 环境概况 操作系统:Ubuntu …

【论文笔记】SOTR: Segmenting Objects with Transformers

【题目】&#xff1a;SOTR: Segmenting Objects with Transformers 【引用格式】&#xff1a;Guo R, Niu D, Qu L, et al. Sotr: Segmenting objects with transformers[C]//Proceedings of the IEEE/CVF international conference on computer vision. 2021: 7157-7166. 【网…

MinIO实现https访问

Windows下实现MinIO的https访问. 首先需要自己解决证书问题, 这里可以是个人证书 也可以是花钱买的证书. 现在使用个人开发者证书举例子。 将证书数据解压到你知道的目录之下 然后直接使用命令启动MinIO start minio.exe server --certs-dir D:\xxxxx\tools\certs …

基于 jQuery 实现灵活可配置的输入框验证功能

在 Web 表单开发中&#xff0c;输入框验证是保障数据准确性和安全性的关键环节。无论是用户注册、信息提交还是数据录入场景&#xff0c;都需要对用户输入内容进行合法性检查。本文将介绍如何使用 HTML、CSS 和 jQuery 构建一个可灵活配置的输入框验证系统&#xff0c;轻松应对…

Kotlin 04Flow stateIn 和 shareIn的区别

一 Kotlin Flow 中的 stateIn 和 shareIn 一、简单比喻理解 想象一个水龙头&#xff08;数据源&#xff09;和几个水杯&#xff08;数据接收者&#xff09;&#xff1a; 普通 Flow&#xff08;冷流&#xff09;&#xff1a;每个水杯来接水时&#xff0c;都要重新打开水龙头从…

WebRTC 服务器之SRS服务器概述和环境搭建

1.概述 SRS&#xff08;Simple Realtime Server&#xff09;是一款高性能、跨平台的流媒体服务器&#xff0c;支持多种协议&#xff0c;包括 RTMP、WebRTC、HLS、HTTP-FLV、SRT、MPEG-DASH 和 GB28181。本文介绍了 SRS&#xff0c;包括其用途、关键功能、架构和支持协议。SRS 旨…

Dify - Embedding Rerank

注意&#xff1a;v100显卡会出现不适配&#xff0c;不推荐使用 1. 安装 Docker ubuntu 22.04 docker 安装&使用_ubuntu22.04 安装docker-CSDN博客 2. 安装vllm pip install -U xformers torch torchvision torchaudio triton --index-url https://download.pytorch.org/w…

LeetCode:链表的中间结点

1、题目描述 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点&#xff…

LabVIEW温控系统热敏电阻滞后问题

在 LabVIEW 构建的温控系统中&#xff0c;热敏电阻因热时间常数大&#xff08;2 秒左右&#xff09;产生的滞后效应&#xff0c;致使控温出现超调与波动。在不更换传感器的前提下&#xff0c;可从算法优化、硬件调整和系统设计等维度着手解决。 ​ 一、算法优化​ 1. 改进 PI…

技术犯规计入个人犯规吗·棒球1号位

在棒球运动中&#xff0c;虽然没有“技术犯规”这一特定术语&#xff0c;但存在多种违规行为或违反规则的情况&#xff0c;通常会导致判罚或处罚。以下是常见的违规行为及相关规则&#xff1a; 1. 投手违规&#xff08;Balk&#xff09; 定义&#xff1a;投手在垒上有跑垒员时…

Python核心技巧 类与实例:面向对象编程的基石

、核心概念图解 &#x1f3af; 类 vs 实例 类&#xff1a;对象的蓝图&#xff08;如"汽车设计图"&#xff09; 实例&#xff1a;类的具体实现&#xff08;如"你的特斯拉Model 3"&#xff09; class MyClass: # 类声明 count 0 # 类…

协程补充---viewModelScope 相关知识点

viewModelScope.launch 默认在 Dispatchers.Default 线程池执行Dispatchers.Default 是一个后台线程池&#xff0c;专门用于 CPU 密集型任务如果需要在主线程执行&#xff0c;必须显式指定 Dispatchers.Main remember 是 Compose 的状态管理函数(queueMenus) 是依赖项&#xff…

linux stm32mp157 GIC-V2 中断处理过程分析

/* ** 中断触发时&#xff0c;调用的 handle_arch_irq 入口地址。 ** 因为此时&#xff0c;挂接的就是 gic_handle_irq 函数&#xff01;gic_handle_irq 是个全局函数指针&#xff0c; ** static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) ** 它是Lin…

动态指令参数:根据组件状态调整指令行为

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

直方图比较

目录 1、直方图比较的概念 2、直方图比较的主要原因 3、典型应用场景 4、基础直方图比较 5、多通道直方图比较 6、实时直方图检测 1、直方图比较的概念 直方图比较是通过数学方法计算两个直方图之间的相似度或差异度的技术。在计算机视觉中&#xff0c;直方图是对图像特征…

Windows11 VS code 安装 Cline 调用 Github MCP 配置过程坑点汇总

背景 为了调研 MCP 在 windows 上如何使用本地的命令执行一些操作而实现自动化的过程&#xff0c;在 B 站视频的指导下&#xff0c;进行相应填坑过程&#xff0c;最终运行起来&#xff0c;并实现 github 自动化编程并提交代码的过程。 B 站 Cline 视频演示 Cline Cline 是一…

kdump详解

kdump 是 Linux 系统中的一种内核崩溃转储机制&#xff0c;用于在系统崩溃时将内存中的数据保存到磁盘上&#xff0c;以便后续分析系统崩溃的原因。以下是对 kdump 的详细介绍&#xff1a; 1、工作原理 kdump 利用了 Linux 系统中的双启动机制。当系统启动时&#xff0c;它会…

RGB三原色

本文来源 &#xff1a; 腾讯元宝 ​​RGB三原色&#xff08;红绿蓝&#xff09;详解​​ RGB&#xff08;Red, Green, Blue&#xff09;是光学的三原色&#xff0c;通过不同比例的混合可以产生人眼可见的绝大多数颜色。它是现代显示技术&#xff08;如屏幕、投影仪&#xff09…

CSS兼容性:挑战与策略

CSS兼容性&#xff1a;挑战与策略 引言 在前端开发的广阔领域中&#xff0c;跨浏览器兼容性无疑是最棘手且难以预测的挑战之一。当我们精心设计的网页在Chrome中完美呈现&#xff0c;却在Safari中布局崩溃&#xff0c;或在Firefox中交互失效时&#xff0c;这种挫折感是每位前…