嵌入式毕业设计 心率血氧健康监测系统设计与实现 - 物联网

本文主要是介绍嵌入式毕业设计 心率血氧健康监测系统设计与实现 - 物联网,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 0 前言
  • 1 简介
  • 2 主要器件
  • 3 实现效果
  • 4 设计原理
    • 4.1 **硬件准备**
    • 4.2 **传感器和算法**
  • 5 部分核心代码
  • 5 最后


0 前言

🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

🚩 基于单片机的心率血氧健康监测手表

🥇学长这里给一个题目综合评分(每项满分5分)

  • 难度系数:4分
  • 工作量:4分
  • 创新点:3分

1 简介

本项目基于Arduino平台,使用 MAX30102、XIAO 和一个微型显示器来构建血氧和心率计。

2 主要器件

  • Arduino MAX30102脉搏心率传感器
  • Seeeduino XIAO
  • 彩色显示器

3 实现效果

在这里插入图片描述
用 Apple Watch Series 6 交叉检查了读数,MolecularD 算法可以给出与 Apple Watch 相当的非常精确的数字。
在这里插入图片描述

在这里插入图片描述

4 设计原理

4.1 硬件准备

在这里插入图片描述
单片机

脉搏血氧计计算算法需要一些内存。Arduino 经典系列没有足够的内存用于数据缓冲区。而且我希望所有组件都可以安装在一个 400 孔的面包板上,这里选用了一个Seeeduino XIAO。

显示

任何支持 Arduino_GFX 的显示器都应该没问题,刚好手头有一个 1.14" 135x240 彩色显示器。

传感器

Maxim Integrated MAX30102 生物传感器。MAX30102这款传感器:Maxim公司的MAX30102是高灵敏度血氧和心率生物传感器,包括内部的LED,光电检测器,光学元件,以及环境光抑制的低噪音电子学。单个1.8V电源,内部LED电源5.0V,通信通过标准的I2C接口进行,工作温度-40℃ 到 +85℃,微细5.6mm x 3.3mm x 1.55mm 14引脚封装,主要用在可穿戴设备以及健美辅助设备。

面包板连接
在这里插入图片描述

在这里插入图片描述

具体连接如下:
在这里插入图片描述

软件准备

4.2 传感器和算法

传统的脉搏测量方法主要有三种:一是从心电信号中提取;二是从测量血压时压力传感器测到的波动来计算脉率;三是光电容积法。前两种方法提取信号都会限制病人的活动,如果长时间使用会增加病人生理和心理上的不舒适感。而光电容积法脉搏测量作为监护测量中最普遍的方法之一,其具有方法简单、佩戴方便、可靠性高等特点。

工作原理

MAX30102采用PPG光电容积脉搏波描记法(PhotoPlethysmoGraphy)测量数据,主要通过血液及血管的动态改变,反应出透光率的关系进行测算。微控制器对这些数据进行处理运算过后得到心率血氧数值,再通过I2C或UART接口输出,大大降低了传感器的使用难度和对主控的资源占用。同时该传感器还配备有对应的上位机,可通过电脑直接读取数据。

光电容积法的基本原理是利用人体组织在血管搏动时造成透光率不同来进行脉搏和血氧饱和度测量的。其使用的传感器由光源和光电变换器两部分组成,通过绑带或夹子固定在病人的手指、手腕或耳垂上。光源一般采用对动脉血中氧合血红蛋白(HbO2)和去氧血红蛋白(Hb)有选择性的特定波长的发光二极管(一般选用660nm附近的红光和900nm附近的红外光)。当光束透过人体外周血管,由于动脉搏动充血容积变化导致这束光的透光率发生改变,此时由光电变换器接收经人体组织反射的光线,转变为电信号并将其放大和输出。由于脉搏是随心脏的搏动而周期性变化的信号,动脉血管容积也呈现周期性变化,因此光电变换器的电信号变化周期就是脉搏率。同时根据血氧饱和度的定义,其表示为:
在这里插入图片描述
MAX30102本身集成了完整的发光LED及驱动部分,光感应和AD转换部分,环境光干扰消除及数字滤波部分,只将数字接口留给用户,极大地减轻了用户的设计负担。用户只需要使用单片机通过硬件I2C或者模拟I2C接口来读取MAX30102本身的FIFO,就可以得到转换后的光强度数值,通过编写相应算法就可以得到心率值和血氧饱和度。

5 部分核心代码

#include "max30102.h"
#include "myiic.h"
#include "delay.h"u8 max30102_Bus_Write(u8 Register_Address, u8 Word_Data)
{/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 *//* 第1步:发起I2C总线启动信号 */IIC_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_WR);	/* 此处是写指令 *//* 第3步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第4步:发送字节地址 */IIC_Send_Byte(Register_Address);if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第5步:开始写入数据 */IIC_Send_Byte(Word_Data);/* 第6步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 发送I2C总线停止信号 */IIC_Stop();return 1;	/* 执行成功 */cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 *//* 发送I2C总线停止信号 */IIC_Stop();return 0;
}u8 max30102_Bus_Read(u8 Register_Address)
{u8  data;/* 第1步:发起I2C总线启动信号 */IIC_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_WR);	/* 此处是写指令 *//* 第3步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第4步:发送字节地址, */IIC_Send_Byte((uint8_t)Register_Address);if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第6步:重新启动I2C总线。下面开始读取数据 */IIC_Start();/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_RD);	/* 此处是读指令 *//* 第8步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第9步:读取数据 */{data = IIC_Read_Byte(0);	/* 读1个字节 */IIC_NAck();	/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */}/* 发送I2C总线停止信号 */IIC_Stop();return data;	/* 执行成功 返回data值 */cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 *//* 发送I2C总线停止信号 */IIC_Stop();return 0;
}void max30102_FIFO_ReadWords(u8 Register_Address,u16 Word_Data[][2],u8 count)
{u8 i=0;u8 no = count;u8 data1, data2;/* 第1步:发起I2C总线启动信号 */IIC_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_WR);	/* 此处是写指令 *//* 第3步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第4步:发送字节地址, */IIC_Send_Byte((uint8_t)Register_Address);if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第6步:重新启动I2C总线。下面开始读取数据 */IIC_Start();/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_RD);	/* 此处是读指令 *//* 第8步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第9步:读取数据 */while (no){data1 = IIC_Read_Byte(0);	IIC_Ack();data2 = IIC_Read_Byte(0);IIC_Ack();Word_Data[i][0] = (((u16)data1 << 8) | data2);  //data1 = IIC_Read_Byte(0);	IIC_Ack();data2 = IIC_Read_Byte(0);if(1==no)IIC_NAck();	/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */elseIIC_Ack();Word_Data[i][1] = (((u16)data1 << 8) | data2); no--;	i++;}/* 发送I2C总线停止信号 */IIC_Stop();cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 *//* 发送I2C总线停止信号 */IIC_Stop();
}void max30102_FIFO_ReadBytes(u8 Register_Address,u8* Data)
{	max30102_Bus_Read(REG_INTR_STATUS_1);max30102_Bus_Read(REG_INTR_STATUS_2);/* 第1步:发起I2C总线启动信号 */IIC_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_WR);	/* 此处是写指令 *//* 第3步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第4步:发送字节地址, */IIC_Send_Byte((uint8_t)Register_Address);if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第6步:重新启动I2C总线。下面开始读取数据 */IIC_Start();/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */IIC_Send_Byte(max30102_WR_address | I2C_RD);	/* 此处是读指令 *//* 第8步:发送ACK */if (IIC_Wait_Ack() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第9步:读取数据 */Data[0] = IIC_Read_Byte(1);	Data[1] = IIC_Read_Byte(1);	Data[2] = IIC_Read_Byte(1);	Data[3] = IIC_Read_Byte(1);Data[4] = IIC_Read_Byte(1);	Data[5] = IIC_Read_Byte(0);/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) *//* 发送I2C总线停止信号 */IIC_Stop();cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 *//* 发送I2C总线停止信号 */IIC_Stop();//	u8 i;
//	u8 fifo_wr_ptr;
//	u8 firo_rd_ptr;
//	u8 number_tp_read;
//	//Get the FIFO_WR_PTR
//	fifo_wr_ptr = max30102_Bus_Read(REG_FIFO_WR_PTR);
//	//Get the FIFO_RD_PTR
//	firo_rd_ptr = max30102_Bus_Read(REG_FIFO_RD_PTR);
//	
//	number_tp_read = fifo_wr_ptr - firo_rd_ptr;
//	
//	//for(i=0;i<number_tp_read;i++){
//	if(number_tp_read>0){
//		IIC_ReadBytes(max30102_WR_address,REG_FIFO_DATA,Data,6);
//	}//max30102_Bus_Write(REG_FIFO_RD_PTR,fifo_wr_ptr);
}void max30102_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_14;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStructure);IIC_Init();max30102_reset();//	max30102_Bus_Write(REG_MODE_CONFIG, 0x0b);  //mode configuration : temp_en[3]      MODE[2:0]=010 HR only enabled    011 SP02 enabled
//	max30102_Bus_Write(REG_INTR_STATUS_2, 0xF0); //open all of interrupt
//	max30102_Bus_Write(REG_INTR_STATUS_1, 0x00); //all interrupt clear
//	max30102_Bus_Write(REG_INTR_ENABLE_2, 0x02); //DIE_TEMP_RDY_EN
//	max30102_Bus_Write(REG_TEMP_CONFIG, 0x01); //SET   TEMP_EN//	max30102_Bus_Write(REG_SPO2_CONFIG, 0x47); //SPO2_SR[4:2]=001  100 per second    LED_PW[1:0]=11  16BITS//	max30102_Bus_Write(REG_LED1_PA, 0x47); 
//	max30102_Bus_Write(REG_LED2_PA, 0x47); max30102_Bus_Write(REG_INTR_ENABLE_1,0xc0);	// INTR settingmax30102_Bus_Write(REG_INTR_ENABLE_2,0x00);max30102_Bus_Write(REG_FIFO_WR_PTR,0x00);  	//FIFO_WR_PTR[4:0]max30102_Bus_Write(REG_OVF_COUNTER,0x00);  	//OVF_COUNTER[4:0]max30102_Bus_Write(REG_FIFO_RD_PTR,0x00);  	//FIFO_RD_PTR[4:0]max30102_Bus_Write(REG_FIFO_CONFIG,0x0f);  	//sample avg = 1, fifo rollover=false, fifo almost full = 17max30102_Bus_Write(REG_MODE_CONFIG,0x03);  	//0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LEDmax30102_Bus_Write(REG_SPO2_CONFIG,0x27);  	// SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)  max30102_Bus_Write(REG_LED1_PA,0x24);   	//Choose value for ~ 7mA for LED1max30102_Bus_Write(REG_LED2_PA,0x24);   	// Choose value for ~ 7mA for LED2max30102_Bus_Write(REG_PILOT_PA,0x7f);   	// Choose value for ~ 25mA for Pilot LED//	// Interrupt Enable 1 Register. Set PPG_RDY_EN (data available in FIFO)
//	max30102_Bus_Write(0x2, 1<<6);//	// FIFO configuration register
//	// SMP_AVE: 16 samples averaged per FIFO sample
//	// FIFO_ROLLOVER_EN=1
//	//max30102_Bus_Write(0x8,  1<<4);
//	max30102_Bus_Write(0x8, (0<<5) | 1<<4);//	// Mode Configuration Register
//	// SPO2 mode
//	max30102_Bus_Write(0x9, 3);//	// SPO2 Configuration Register
//	max30102_Bus_Write(0xa,
//			(3<<5)  // SPO2_ADC_RGE 2 = full scale 8192 nA (LSB size 31.25pA); 3 = 16384nA
//			| (1<<2) // sample rate: 0 = 50sps; 1 = 100sps; 2 = 200sps
//			| (3<<0) // LED_PW 3 = 411μs, ADC resolution 18 bits
//	);//	// LED1 (red) power (0 = 0mA; 255 = 50mA)
//	max30102_Bus_Write(0xc, 0xb0);//	// LED (IR) power
//	max30102_Bus_Write(0xd, 0xa0);}void max30102_reset(void)
{max30102_Bus_Write(REG_MODE_CONFIG,0x40);max30102_Bus_Write(REG_MODE_CONFIG,0x40);
}void maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
{
//  char ach_i2c_data[2];
//  ach_i2c_data[0]=uch_addr;
//  ach_i2c_data[1]=uch_data;
//	
//  IIC_WriteBytes(I2C_WRITE_ADDR, ach_i2c_data, 2);IIC_Write_One_Byte(I2C_WRITE_ADDR,uch_addr,uch_data);
}void maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
{
//  char ch_i2c_data;
//  ch_i2c_data=uch_addr;
//  IIC_WriteBytes(I2C_WRITE_ADDR, &ch_i2c_data, 1);
//	
//  i2c.read(I2C_READ_ADDR, &ch_i2c_data, 1);
//  
//   *puch_data=(uint8_t) ch_i2c_data;IIC_Read_One_Byte(I2C_WRITE_ADDR,uch_addr,puch_data);
}void maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
{uint32_t un_temp;unsigned char uch_temp;char ach_i2c_data[6];*pun_red_led=0;*pun_ir_led=0;//read and clear status registermaxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);IIC_ReadBytes(I2C_WRITE_ADDR,REG_FIFO_DATA,(u8 *)ach_i2c_data,6);un_temp=(unsigned char) ach_i2c_data[0];un_temp<<=16;*pun_red_led+=un_temp;un_temp=(unsigned char) ach_i2c_data[1];un_temp<<=8;*pun_red_led+=un_temp;un_temp=(unsigned char) ach_i2c_data[2];*pun_red_led+=un_temp;un_temp=(unsigned char) ach_i2c_data[3];un_temp<<=16;*pun_ir_led+=un_temp;un_temp=(unsigned char) ach_i2c_data[4];un_temp<<=8;*pun_ir_led+=un_temp;un_temp=(unsigned char) ach_i2c_data[5];*pun_ir_led+=un_temp;*pun_red_led&=0x03FFFF;  //Mask MSB [23:18]*pun_ir_led&=0x03FFFF;  //Mask MSB [23:18]
}

5 最后

这篇关于嵌入式毕业设计 心率血氧健康监测系统设计与实现 - 物联网的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/371356

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

Linux系统之lvcreate命令使用解读

《Linux系统之lvcreate命令使用解读》lvcreate是LVM中创建逻辑卷的核心命令,支持线性、条带化、RAID、镜像、快照、瘦池和缓存池等多种类型,实现灵活存储资源管理,需注意空间分配、R... 目录lvcreate命令详解一、命令概述二、语法格式三、核心功能四、选项详解五、使用示例1. 创建逻