简介
PulseSensor 是一种光学心率传感器,通过检测心脏跳动时血液流动引起的光吸收变化来测量心率。它由一个 LED (发光二极管) 和一个光敏元件组成。LED 发出光线照射皮肤,光敏元件接收反射回来的光线,并将其转换为电信号。这个电信号经过处理后,可以用来计算心率。
我使用的是STM32F103C8T6
这个传感器测量时找一下感觉,不要用力按压,因为是通过光电信号检测的,轻轻放上即可,然后注意有电阻电容的那面,不要摸!!!!
电压 | 3.3V~5V |
输出信号类型 | 模拟信号 |
输入接口 | ADC1_IN7 |
效果展示
PulseSensor脉搏心率传感器 + u8g2 显示脉搏
cubemx配置
使能ADC_IN7 ,当然大家可以根据需要的引脚进行配置
注意改一下continuous conversion mode
还有时钟配置界面
代码分享
画图函数
#define PULSE_BUFFER_SIZE 128
uint16_t pulse_data[PULSE_BUFFER_SIZE];
uint8_t pulse_index = 0; // 当前数据写入位置
// 缩放比例(调整为适应 1000-3000 的数据范围)
//因为ADC读取到的原始数值在0~3400左右,测量时的数值主要在1000~3000 所以可以缩小比例,看的更清楚。
#define PULSE_DATA_MIN 1000.0f // 数据的最小值
#define PULSE_DATA_MAX 3000.0f // 数据的最大值
#define PULSE_RANGE (PULSE_DATA_MAX - PULSE_DATA_MIN)
#define PULSE_SCALE_FACTOR (63.0f / PULSE_RANGE)
// 绘图偏移量
uint8_t offset = 0;
//把数组清空 不用也可以
void Pulse_Init()
{
for (int i = 0; i < PULSE_BUFFER_SIZE; i++)
{
pulse_data[i] = 0;
}
}
// 绘制脉搏图
//【u8g2_t *u8g2】:在外面调用时输入的结构体
//【pulse】输入的脉搏数值,不过这里没有做好,显示有问题,不可用,大家可以删掉
//【返回值int】可以外接一个flag,意思是数值到达2200~2500就返回一个1,可以配合蜂鸣器,达到蜂鸣器跟随心率“滴滴”的效果
int Pulse_Draw(u8g2_t *u8g2, uint8_t pulse)
{
u8g2_ClearBuffer(u8g2);
// 1. 采集数据 (简化示例,需要替换成你的 ADC 采集代码)
uint16_t pulse_value = 0; // 存储当前的脉搏值
uint16_t flag = 0;
// 读取 ADC 值 (与之前的代码类似,这里简化)
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); // 等待转换完成
pulse_value = HAL_ADC_GetValue(&hadc1); // 获取 ADC 值
if(pulse_value > 2200 &&pulse_value < 2500)
flag = 1;
else
flag = 0;
// 2. 将新数据添加到环形缓冲区
pulse_data[pulse_index] = pulse_value;
pulse_index = (pulse_index + 1) % PULSE_BUFFER_SIZE;
// 3. 绘制脉搏图
for (int i = 0; i < PULSE_BUFFER_SIZE - 1; i++)
{
// 计算数据索引
uint8_t index1 = (pulse_index + i) % PULSE_BUFFER_SIZE;
uint8_t index2 = (pulse_index + i + 1) % PULSE_BUFFER_SIZE;
// 计算屏幕上的 x 和 y 坐标
uint8_t x1 = i;
float scaled_value1 = (float)pulse_data[index1];
// 钳位,确保数据在范围内
if (scaled_value1 < PULSE_DATA_MIN)
{
scaled_value1 = PULSE_DATA_MIN;
}
else if (scaled_value1 > PULSE_DATA_MAX)
{
scaled_value1 = PULSE_DATA_MAX;
}
//减去最小值,再缩放,不然最小值也在底部
uint8_t y1 = 63 - (uint8_t)((scaled_value1 - PULSE_DATA_MIN) * PULSE_SCALE_FACTOR); // 反转Y轴
uint8_t x2 = i + 1;
float scaled_value2 = (float)pulse_data[index2];
// 钳位,确保数据在范围内
if (scaled_value2 < PULSE_DATA_MIN)
{
scaled_value2 = PULSE_DATA_MIN;
}
else if (scaled_value2 > PULSE_DATA_MAX)
{
scaled_value2 = PULSE_DATA_MAX;
}
uint8_t y2 = 63 - (uint8_t)((scaled_value2 - PULSE_DATA_MIN) * PULSE_SCALE_FACTOR);
// 绘制线段
u8g2_DrawLine(u8g2, x1, y1, x2, y2);
}
// 4. 显示数值
char buffer[16]; // 足够存储 "Value: XXXX"
sprintf(buffer, "Value: %u", pulse); // 格式化字符串
u8g2_SetFont(u8g2, u8g2_font_wqy12_t_chinese2); // 设置字体 (根据你的需求调整)
u8g2_DrawStr(u8g2, 0, 10, buffer); // 在 (0, 10) 位置显示数值字符串 (根据你的需求调整)
u8g2_SendBuffer(u8g2);
return flag;
}
heart.c
里面有被注释掉的,配合上位机玩法的代码,可以来下面这个连接看,介绍的很棒,来自:
三、STM32 Pulse sensor 心率脉搏检测_心率监测系统stm32-CSDN博客
/*此代码来自其他博主*/
/*此代码来自其他博主*/
/*此代码来自其他博主*/
/**/
#include "heart.h"
#include "stdint.h"
#include "stdio.h"
uint8_t IBI;
uint16_t usPulse[128];
uint8_t ucPos;
void scaleData(void);
static void calculatePulse(uint8_t *pulse, uint16_t *maxValue);
uint16_t getArrayMax(uint16_t arr[], int size);
uint16_t getArrayMin(uint16_t arr[], int size);
void getPulse(uint8_t *pulse, uint16_t *maxValue)
{
uint16_t usData,SIG;
*pulse = 0;
*maxValue = 0;
usData = HAL_ADC_GetValue(&hadc1);
// printf("S%d\r\n",usData);
usPulse[ucPos++] = usData; //±£´æ£¬¹²¼Æ128¸öµã
if (ucPos>=128) //Íê³ÉÒ»Âֲɼ¯
{
ucPos = 0;
scaleData();
calculatePulse(pulse, maxValue);
}
/////////////////////Processing上位机打开//////////////////////////
SIG = usData - 1500; //使用Processing上位机打开
printf("S%d\r\n",SIG); //使用Processing上位机打开
/////////////////////Processing上位机打开//////////////////////////
}
void scaleData()
{
uint8_t i;
uint16_t usMax, usMin, usDelter;
usMax = getArrayMax(usPulse, 128);
usMin = getArrayMin(usPulse, 128);
usDelter = usMax - usMin;
if(usDelter<200)
{
for(i=0;i<128;i++)
usPulse[i] = usDelter/2;
}
else
{
for(i=0;i<128;i++)
usPulse[i] = usDelter*(usPulse[i]-usMin)/usDelter;
}
}
static void calculatePulse(uint8_t *pulse, uint16_t *maxValue)
{
uint8_t i, firstTime, secondTime;
uint8_t PrePulse, Pulse, IBI;
uint16_t usMax, usMin, usMid;
usMax = getArrayMax(usPulse, 128);
usMin = getArrayMin(usPulse, 128);
usMid = (usMax + usMin)/2;
*maxValue = usMax;
firstTime = secondTime = 0;
for(i=0;i<128;i++)
{
PrePulse = Pulse;
Pulse = (usPulse[i]>usMid)?1:0;
if(PrePulse == 0 && Pulse == 1)
{
if(!firstTime) firstTime = i;
if(firstTime && firstTime <i)
{
secondTime = i;
break;
}
}
}
if((secondTime - firstTime)>0)
{
IBI = (secondTime-firstTime)*HEART_PERIOD; //IBI
*pulse = 60*1000/((secondTime-firstTime)*HEART_PERIOD); //BPM //BPM
}
else
*pulse = 0;
//printf("BPM = %d SIG = %d\r\n\r\n",*pulse, *IBI); //单纯串口调试打开
/////////////////////Processing上位机打开//////////////////////////
printf("B%d\r\n",*pulse); //使用Processing上位机打开
printf("Q%d\r\n",IBI); //使用Processing上位机打开
/////////////////////Processing上位机打开//////////////////////////
}
uint16_t getArrayMax(uint16_t arr[], int size)
{
if (size == 0) return 0; //
uint16_t max = arr[0]; //
for (int i = 1; i < size; i++)
{
if (arr[i] > max)
{
max = arr[i]; //
}
}
return max; //
}
uint16_t getArrayMin(uint16_t arr[], int size)
{
if (size == 0) return 255; //
uint16_t min = arr[0]; //
for (int i = 1; i < size; i++)
{
if (arr[i] < min)
{
min = arr[i]; //
}
}
return min; //
}
heart.h
#ifndef __HEART_H
#define __HEART_H
#include "adc.h"
#include "main.h"
#define HEART_PERIOD 20
extern uint16_t usPulse[128];
extern uint8_t ucPos;
void getPulse(uint8_t *pulse, uint16_t *maxValue);
#endif
调用方法
画图函数
//定义结构体,当然变量也可以,我用的freertos,结构体队列传输会方便一些。
typedef struct
{
uint8_t Pulse;
uint8_t usData;
} Pulse_Data_t;
Pulse_Data_t PulseData;
-----------------------------------------------
Pulse_Draw(&u8g2, PulseData.Pulse);
//20ms调用一次
单纯的测量心率脉搏(主函数调用即可)
HAL_ADC_Start(&hadc1); //开启ADC1
getPulse(&PulseData.Pulse, &usMaxValue);