STM32--基于STM32F103的MAX30102心率血氧测量

news2024/11/20 10:32:32

本文介绍基于STM32F103ZET6+MAX30102心率血氧测量+0.96寸OLED(7针)显示(完整程序代码见文末链接)

一、简介

MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。

MAX30102采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指点耳垂和手腕处。

标准的I2C兼容的通信接口可以将采集到的数值传输给Arduino、STM32、STC51等单片机进行心率和血氧计算。此外,该芯片还可以通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。
在这里插入图片描述
模块主要参数:

LED峰值波长器 :660nm/880nm
LED供电电压 : 3.3 ~ 5V
检测信号类型 : 光反射信号(PPG)
输出信号接口 : I2C接口
通信接口电压 : 1.8 ~ 3.3V ~ 5V(可选)

工作原理

光溶积法:利用人体组织在血管搏动时造成透光率不同来进行脉搏和血氧饱和度测量

光源:采用对动脉血中痒合血红蛋白(HbO2)和血红蛋白(Hb)有选择性的特定波长的发光二极管

透光率转化为电信号:动脉搏动充血容积变化导致这束光的透光率发送改变,此时由光电变换接收经人体组织反射光线,转变为电信号并将其放大输出。

在这里插入图片描述
模块原理图

在这里插入图片描述

引脚说明

管脚定义说明
VIN电源输入 1.6V-5.5V,一般3.3V
SDAIIC-SDA
SCLIIC-SCL
GND
INTINT 低电平有效中断(漏极开路)MAX30102 的中断引脚
IRDIR_DRV IR LED阴极和LED驱动器连接点,一般NC
RDR_DRV 红色LED阴极和LED驱动器连接点,一般NC

通讯协议

本模块采用I2C通讯方式,具有I2C/SMBus兼容的2线制由串行数据线(SDA)和串行时钟线(SCL),时钟速率高达400kHz。

设备地址:
在这里插入图片描述

本文主要介绍基于STM32F103ZET6和实现的MAX30102的心率和血氧测量,并显示在0.96寸的OLED(7针)屏幕上

二、接线图

设计中涉及STM32和MAX30102链接以及和OLED的管脚连接,具体连接脚说明如下:

STM32F103ZET6+MAX30102接线

STM32F103ZET6MAX30102
3.3VVin
GNDGND
PC7SCL
PC8SDA
PC9INT

STM32F103ZET6+0.96寸OLED接线,其中OLED选用的是7针OLED-4SPI通信

STM32F103ZET60.96寸OLED(7针)
3.3VVCC
GNDGND
PA5SCL/D0
PA6SDA/D1
PA3RST
PA4DC
PA2CS

在这里插入图片描述

三、代码函数(完整工程文件见文末连接)

主函数,主要实现心率和血氧测量并显示在OLED屏幕,且当超过阈值后会蜂鸣器报警

int main(void)
{ 
	//variables to calculate the on-board LED brightness that reflects the heartbeats
	uint32_t un_min, un_max, un_prev_data;  
	int i;
	int32_t n_brightness;
	float f_temp;
//	u8 temp_num=0;
	u8 temp[6];
	u8 str[100];
	u8 dis_hr=0,dis_spo2=0;

	NVIC_Configuration();
	delay_init();	    	 //延时函数初始化	  
	uart_init(115200);	 	//串口初始化为115200
	LED_Init();
	BEEP_Init();
	
	
	//OLED
	OLED_Init();
	OLED_ShowString(0,0,"  initializing  ",16);
	OLED_Refresh_Gram();//更新显示到OLED	 

	max30102_init();

	printf("\r\n MAX30102  init  \r\n");

	un_min=0x3FFFF;
	un_max=0;
	
	n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
	//read the first 500 samples, and determine the signal range
    for(i=0;i<n_ir_buffer_length;i++)
    {
        while(MAX30102_INT==1);   //wait until the interrupt pin asserts
        
		max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
		aun_red_buffer[i] =  (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2];    // Combine values to get the actual number
		aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5];   // Combine values to get the actual number
            
        if(un_min>aun_red_buffer[i])
            un_min=aun_red_buffer[i];    //update signal min
        if(un_max<aun_red_buffer[i])
            un_max=aun_red_buffer[i];    //update signal max
    }
	un_prev_data=aun_red_buffer[i];
	//calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 
	
	while(1)
	{
		i=0;
        un_min=0x3FFFF;
        un_max=0;
		
		//dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
        for(i=100;i<500;i++)
        {
            aun_red_buffer[i-100]=aun_red_buffer[i];
            aun_ir_buffer[i-100]=aun_ir_buffer[i];
            
            //update the signal min and max
            if(un_min>aun_red_buffer[i])
            un_min=aun_red_buffer[i];
            if(un_max<aun_red_buffer[i])
            un_max=aun_red_buffer[i];
        }
		//take 100 sets of samples before calculating the heart rate.
        for(i=400;i<500;i++)
        {
            un_prev_data=aun_red_buffer[i-1];
            while(MAX30102_INT==1);
            max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
			aun_red_buffer[i] =  (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2];    // Combine values to get the actual number
			aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5];   // Combine values to get the actual number
        
            if(aun_red_buffer[i]>un_prev_data)
            {
                f_temp=aun_red_buffer[i]-un_prev_data;
                f_temp/=(un_max-un_min);
                f_temp*=MAX_BRIGHTNESS;
                n_brightness-=(int)f_temp;
                if(n_brightness<0)
                    n_brightness=0;
            }
            else
            {
                f_temp=un_prev_data-aun_red_buffer[i];
                f_temp/=(un_max-un_min);
                f_temp*=MAX_BRIGHTNESS;
                n_brightness+=(int)f_temp;
                if(n_brightness>MAX_BRIGHTNESS)
                    n_brightness=MAX_BRIGHTNESS;
            }
			//send samples and calculation result to terminal program through UART
			if(ch_hr_valid == 1 && n_heart_rate<120)//**/ ch_hr_valid == 1 && ch_spo2_valid ==1 && n_heart_rate<120 && n_sp02<101
			{
				dis_hr = n_heart_rate;
				dis_spo2 = n_sp02;
			}
			else
			{
				dis_hr = 0;
				dis_spo2 = 0;
			}
				printf("HR=%i, ", n_heart_rate); 
				printf("HRvalid=%i, ", ch_hr_valid);
				printf("SpO2=%i, ", n_sp02);
				printf("SPO2Valid=%i\r\n", ch_spo2_valid);
		}
        maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
		
		//显示刷新
		LED0=0;
		if(dis_hr == 0 && dis_spo2 == 0)  //**dis_hr == 0 && dis_spo2 == 0
		{
			sprintf((char *)str,"HR:--- SpO2:--- ");//**HR:--- SpO2:--- 
		}
		else{
			sprintf((char *)str,"HR:%3d SpO2:%3d ",dis_hr,dis_spo2);//**HR:%3d SpO2:%3d 
		}
		OLED_ShowString(0,0,str,16);
		OLED_Fill(0,23,127,63,0);
		//红光在上,红外在下
		dis_DrawCurve(aun_red_buffer,20);
		dis_DrawCurve(aun_ir_buffer,0);
		OLED_Refresh_Gram();//更新显示到OLED
}
}

四、视频演示

MAX30102心率血氧测量

附完整代码程序资料

链接:https://pan.baidu.com/s/1TaxlDzRbkNnz4guaJPrGnQ?pwd=6arw
提取码:6arw

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

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

相关文章

移动通信原理与关键技术学习之信道编解码(5)

先回顾调制的过程&#xff1a;调制就是对信号源的信息进行处理加到载波上&#xff0c;使其变为适合于信道传输的形式的过程&#xff0c;就是使载波随信号而改变的技术。 1.什么是IQ调制&#xff1f; 答&#xff1a;将数据分为两路&#xff0c;分别进行载波调制&#xff0c;两…

Camunda Sub Process

一&#xff1a;内嵌子流程 repositoryService.createDeployment().name("内嵌子流程").addClasspathResource("bpmn/embed_sub_process.bpmn").deploy(); identityService.setAuthenticatedUserId("huihui"); ProcessInstance processInstance …

教你三招该如何制作家具样本册

​随着生活品质的提高&#xff0c;人们对家居装饰的要求也越来越高。家具作为家居装饰的重要组成部分&#xff0c;其选择和搭配也显得尤为重要。为了更好地展示家具的特点和风格&#xff0c;制作家具样本册成为了许多人的选择。那么&#xff0c;如何制作一份精美的家具样本册呢…

Day31 贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和

贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和 理论基础&#xff08;转载自代码随想录&#xff09; 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 这么说有点抽象&#xff0c;来举一个例子&#xff1a; 例如&#…

CMake入门教程【实战篇】使用Boost库

文章目录 安装 Boost 库CMake中使用Boost库时安装 Boost 库 下载地址 https://www.boost.org/users/download/ 下载版本 选择二进制版本或者源码 二进制版本直接安装即可 源码版本编译->安装 解压到本地目录,如e:/dev/。运行E:\dev\boost_1_83_0\bootstrap.bat,会在同目录下…

激活/注册navicat15

一、获取软件 链接&#xff1a;https://pan.baidu.com/s/1F_tiLuLvVFMEz8pDfIvDjw?pwdjjfj 提取码&#xff1a;jjfj 二、安装 安装的过程我就不放了&#xff0c;重点如下 安装完不要打开软件&#xff01; 安装完不要打开软件&#xff01; 安装完不要打开软件&#xff01;…

Spark SQL进阶

DataFrame详解 清洗相关API 去重API 删除空缺值的API 替换缺失值的API from pyspark import SparkConf, SparkContext import os from pyspark.sql import SparkSession# 绑定指定的Python解释器 os.environ[SPARK_HOME] /export/server/spark os.environ[PYSPARK_PYTHON]…

前端面试题集合六(高频)

1、vue实现双向数据绑定原理是什么&#xff1f; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>…

RT-Thread入门笔记3-线程的创建

线程 RT-Thread 中&#xff0c;线程由三部分组成&#xff1a;线程代码&#xff08;入口函数&#xff09;、线程控制块、线程堆栈. 线程代码: 线程控制块 : 线程控制块是操作系统用于管理线程的一个数据结构&#xff0c; 它会存放线程的一些信息&#xff0c; 例如优先级、 线程…

从学习投研流程的角度学习Qlib

许多同学只是把Qlib当做一个简单的工具来学习。其实Qlib隐含了一套正规的投研流程&#xff0c;从投研流程的视角去学习Qlib,则不仅能加深对Qlib的理解&#xff0c;而且能够掌握正确的投研流程&#xff0c;哪怕以后不使用Qlib而是使用其他系统了&#xff0c;这套流程还是适用的。…

ADOV路由和DSR路由matlab对比仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 ADOV路由&#xff08;Ad hoc On-demand Distance Vector Routing&#xff09; 4.2 DSR路由&#xff08;Dynamic Source Routing&#xff09; 5.完整程序 1.程序功能描述 ADOV路由和DSR…

创建EasyCodeMybatisCodeHelperPro模板文件用于将数据库表生成前端json文件

在intellij idea中&#xff0c;通过插件EasyCodeMybatisCodeHelperPro&#xff0c;从现有的模板文件中选择一个复制粘贴&#xff0c;然后稍为修改&#xff0c;即可得到一个合适的模板文件。 现在的前端&#xff0c;越来越像后端。TypeScript替代了JavaScript&#xff0c;引入了…

基于Flask的高并发部署方案

文章目录 Flask方案简介服务端代码客户端代码 Gevent Flask方案简介安装示例 gunicornFlask 部署服务简介安装示例 在AI部署方案中&#xff0c;Flask是最常用的方案&#xff01;本文列举几种最常用基于Flask的部署方案。 Flask方案 简介 Flask 是一个轻量级的 Python Web 框架…

微信小程序swiper实现层叠轮播图

在微信小程序中,需要实现展示5个&#xff0c;横向层叠的轮播图效果&#xff0c;轮播图由中间到2侧的依次缩小.如下图 使用原生小程序进行开发,没有使用Skyline模式&#xff0c;所以layout-type配置项也无效。所以基于swiper组件进行调整。 主要思路就是设置不同的样式&#xff…

Mysql 分割字符串,一行变多行,@rownum,mysql.help_topic

1 前言 朋友最近遇到一个比较棘手的 sql 问题&#xff0c;让我帮忙看看&#xff1a; 他有两张表 testa 和 testb &#xff0c;一个表存的日期&#xff0c;另一个表存字符串例如 2023-11-01,2023-11-02&#xff0c;如何将这两张表关联起来&#xff0c;只查 testa 表的数据&#…

MES数据采集在制造业的应用

MES设备数据采集的流程包括以下几个步骤&#xff1a; 1. 设备接入&#xff1a;将设备接入MES系统&#xff0c;建立设备与MES系统之间的连接。 2. 数据采集&#xff1a;通过传感器和采集器等设备&#xff0c;采集设备运行数据和状态信息。 3. 数据存储&#xff1a;将采集到的设…

如何给字符串字段添加索引

MySQL是支持前缀索引的&#xff0c;可以定义字符串的一部分作为索引&#xff0c;如果创建索引的语句不指定前缀长度&#xff0c;那么索引就会包含整个字符串。 alter table SUser add index index1(email);alter table SUser add index index2(email(6)); 如上两个创建索引的语…

快速了解VR全景拍摄技术运用在旅游景区的优势

豆腐脑加了糖、烤红薯加了勺&#xff0c;就连索菲亚大教堂前都有了“人造月亮”&#xff0c;在这个冬季&#xff0c;“尔滨”把各地游客宠上了天。面对更多的游客无法实地游玩&#xff0c;哈尔滨冰雪世界再添新玩法&#xff0c;借助VR全景拍摄技术对冬季经典冰雪体验项目进行全…

机器学习_9、朴素贝叶斯

朴素贝叶斯解决鸢尾花数据集问题 #高斯朴素贝叶斯分类 #codingutf-8 from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.naive_bayes import GaussianNB import numpy as np# 导入数据集&#xff0c;并查看数据特征…