• 【毕业设计】老人心率脉搏血压体征监测手表 - stm32 单片机 嵌入式 物联网



    0 前言

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

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

    🚩 基于stm32的老人心率脉搏血压体征监测手表

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

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

    🧿 选题指导, 项目分享:

    https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md


    1 简介

    随着社会的不断发展,生活水平越来越好,大家也越来越关注健康问题。计步器是一种便携式反映人体状况的仪器,通过计算行走的步数和所消耗的能量,很有效反映人体健康的状态,所以受到了很多人的喜爱和欢迎。

    2 主要器件

    • STM32F103C8T6
    • MAX30102测量传感器
    • ADXL345三轴传感器
    • HC05蓝牙模块
    • OLED显示屏模块
    • DS1302时钟
    • DS18B20温度采集

    3 实现效果

    传感器监测到数据之后,在屏幕上以曲线加数字的方式显示出检测者的血氧和心率数据。同时LED指示灯和蜂鸣器根据心率数据闪烁和发出声音。如果未监测到数据,蜂鸣器常响警报。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    功能介绍

    1. OLED液晶显示当前时间,心率血氧,体温,步数
    2. 通过DS1302显示当前时间,井且具有掉电行是功能
    3. MAX30102模块检测当前心率和血氧
    4. DS18B20检测当前温度或者是体温
    5. ADXL3轴传感器采集当前行走步数,并且具有掉电保存功能
    6. 通过HC05蓝牙模块把当前数据无线传输到手机端进行显示
    7. 第一个按键进行 设置,第二个按键加,第三个按键减
    8. 第四个一键清除当前行走步数

    框图

    在这里插入图片描述

    4 设计原理

    MAX30102
    MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。MAX30102采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指、耳垂和手腕等处。标准的2C兼容的通信接口可以将采集到的数值传输给Arduino、KL25Z等单片机进行心率和血氧计算。此外,该芯片还可通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。正因为其优异的性能,该芯片被大量应用在了三星 Galaxy S7 手机。与前代产品 MAX30100 相比 (MAX30100 目前已经停产淘汰 ) , MAX30102 集成了玻璃盖可以有效排除外界和内部光干扰,拥有最优可靠的性能。
    传统的脉搏测量方法主要有三种:

    • 一是从心电信号中提取;
    • 二是从测量血压时压力传感器测到的波动来计算脉率;
    • 三是光电容积法。

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

    5 部分核心代码

    /** \file max30102.cpp ******************************************************
    *
    * Project: MAXREFDES117#
    * Filename: max30102.cpp
    * Description: This module is an embedded controller driver for the MAX30102
    *
    * Revision History:
    *\n 1-18-2016 Rev 01.00 GL Initial release.
    *\n
    */
    
    #include "max30102.h"
    #include "myiic.h"
    
    #define max30102_WR_address 0xAE
    bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
    /**
    * \brief        Write a value to a MAX30102 register
    * \par          Details
    *               This function writes a value to a MAX30102 register
    *
    * \param[in]    uch_addr    - register address
    * \param[in]    uch_data    - register data
    *
    * \retval       true on success
    */
    {
        /* 第1步:发起I2C总线启动信号 */
        i2c_Start();
    
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(max30102_WR_address | I2C_WR);	/* 此处是写指令 */
    
        /* 第3步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        /* 第4步:发送字节地址 */
        i2c_SendByte(uch_addr);
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        /* 第5步:开始写入数据 */
        i2c_SendByte(uch_data);
    
        /* 第6步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        /* 发送I2C总线停止信号 */
        i2c_Stop();
        return true;	/* 执行成功 */
    
    cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
        /* 发送I2C总线停止信号 */
        i2c_Stop();
        return false;
    }
    
    bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
    /**
    * \brief        Read a MAX30102 register
    * \par          Details
    *               This function reads a MAX30102 register
    *
    * \param[in]    uch_addr    - register address
    * \param[out]   puch_data    - pointer that stores the register data
    *
    * \retval       true on success
    */
    {
        /* 第1步:发起I2C总线启动信号 */
        i2c_Start();
    
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(max30102_WR_address | I2C_WR);	/* 此处是写指令 */
    
        /* 第3步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        /* 第4步:发送字节地址, */
        i2c_SendByte((uint8_t)uch_addr);
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
    
        /* 第6步:重新启动I2C总线。下面开始读取数据 */
        i2c_Start();
    
        /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(max30102_WR_address | I2C_RD);	/* 此处是读指令 */
    
        /* 第8步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        /* 第9步:读取数据 */
        {
            *puch_data = i2c_ReadByte();	/* 读1个字节 */
    
            i2c_NAck();	/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
        /* 发送I2C总线停止信号 */
        i2c_Stop();
        return true;	/* 执行成功 返回data值 */
    
    cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
        /* 发送I2C总线停止信号 */
        i2c_Stop();
        return false;
    }
    
    bool maxim_max30102_init(void)
    /**
    * \brief        Initialize the MAX30102
    * \par          Details
    *               This function initializes the MAX30102
    *
    * \param        None
    *
    * \retval       true on success
    */
    {
        if(!maxim_max30102_write_reg(REG_INTR_ENABLE_1, 0xc0)) // INTR setting
            return false;
        if(!maxim_max30102_write_reg(REG_INTR_ENABLE_2, 0x00))
            return false;
        if(!maxim_max30102_write_reg(REG_FIFO_WR_PTR, 0x00)) //FIFO_WR_PTR[4:0]
            return false;
        if(!maxim_max30102_write_reg(REG_OVF_COUNTER, 0x00)) //OVF_COUNTER[4:0]
            return false;
        if(!maxim_max30102_write_reg(REG_FIFO_RD_PTR, 0x00)) //FIFO_RD_PTR[4:0]
            return false;
        if(!maxim_max30102_write_reg(REG_FIFO_CONFIG, 0x6f)) //sample avg = 8, fifo rollover=false, fifo almost full = 17
            return false;
        if(!maxim_max30102_write_reg(REG_MODE_CONFIG, 0x03))  //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
            return false;
        if(!maxim_max30102_write_reg(REG_SPO2_CONFIG, 0x2F)) // SPO2_ADC range = 4096nA, SPO2 sample rate (400 Hz), LED pulseWidth (411uS)
            return false;
    
        if(!maxim_max30102_write_reg(REG_LED1_PA, 0x17))  //Choose value for ~ 4.5mA for LED1
            return false;
        if(!maxim_max30102_write_reg(REG_LED2_PA, 0x17))  // Choose value for ~ 4.5mA for LED2
            return false;
        if(!maxim_max30102_write_reg(REG_PILOT_PA, 0x7f))  // Choose value for ~ 25mA for Pilot LED
            return false;
        return true;
    }
    
    bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
    
    /**
    * \brief        Read a set of samples from the MAX30102 FIFO register
    * \par          Details
    *               This function reads a set of samples from the MAX30102 FIFO register
    *
    * \param[out]   *pun_red_led   - pointer that stores the red LED reading data
    * \param[out]   *pun_ir_led    - pointer that stores the IR LED reading data
    *
    * \retval       true on success
    */
    {
        uint32_t un_temp;
        uint8_t uch_temp;
        *pun_ir_led = 0;
        *pun_red_led = 0;
        maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
        maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);
    
    
    
        /* 第1步:发起I2C总线启动信号 */
        i2c_Start();
    
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(max30102_WR_address | I2C_WR);	/* 此处是写指令 */
    
        /* 第3步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            printf("read fifo failed");
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        /* 第4步:发送字节地址, */
        i2c_SendByte((uint8_t)REG_FIFO_DATA);
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
    
        /* 第6步:重新启动I2C总线。下面开始读取数据 */
        i2c_Start();
    
        /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(max30102_WR_address | I2C_RD);	/* 此处是读指令 */
    
        /* 第8步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;	/* EEPROM器件无应答 */
        }
    
        un_temp = i2c_ReadByte();
        i2c_Ack();
        un_temp <<= 16;
        *pun_red_led += un_temp;
        un_temp = i2c_ReadByte();
        i2c_Ack();
        un_temp <<= 8;
        *pun_red_led += un_temp;
        un_temp = i2c_ReadByte();
        i2c_Ack();
        *pun_red_led += un_temp;
    
        un_temp = i2c_ReadByte();
        i2c_Ack();
        un_temp <<= 16;
        *pun_ir_led += un_temp;
        un_temp = i2c_ReadByte();
        i2c_Ack();
        un_temp <<= 8;
        *pun_ir_led += un_temp;
        un_temp = i2c_ReadByte();
        i2c_Ack();
        *pun_ir_led += un_temp;
        *pun_red_led &= 0x03FFFF; //Mask MSB [23:18]
        *pun_ir_led &= 0x03FFFF; //Mask MSB [23:18]
    
        /* 发送I2C总线停止信号 */
        i2c_Stop();
        return true;
    cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
        /* 发送I2C总线停止信号 */
        i2c_Stop();
        return false;
    }
    
    bool maxim_max30102_reset()
    /**
    * \brief        Reset the MAX30102
    * \par          Details
    *               This function resets the MAX30102
    *
    * \param        None
    *
    * \retval       true on success
    */
    {
        if(!maxim_max30102_write_reg(REG_MODE_CONFIG, 0x40))
            return false;
        else
            return true;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270

    🧿 选题指导, 项目分享:

    https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md


    5 最后

  • 相关阅读:
    MySQL中的Join 的算法(NLJ、BNL、BKA)
    万字详解 | Java 流式编程
    力扣每日一题73:矩阵置零
    智汇云舟总裁周舟受邀出席“2022数字生态大会” 分享视频孪生的应用与价值
    知识蒸馏和知识图谱相结合的大模型微调方案
    java spring引用外部jar包并使用
    Qt多线程之QThreadData::current()理解
    layui增删改查
    C++笔记-auto_ptr&unique_ptr&shared_ptr&shared_ptr基本用法
    Kotlin进阶指南 - Parcelable序列化
  • 原文地址:https://blog.csdn.net/m0_71572576/article/details/126278438