毕业设计 基于stm32的人体健康监护系统 - 单片机 嵌入式 物联网

发布于:2022-11-29 ⋅ 阅读:(549) ⋅ 点赞:(0)


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 简介

基于STM32F103C8T6单片机开发的工人监测系统,该系统可以实时的监测温湿度、烟雾浓度、心率、环卫工人位置,并可以将这些数据在上位机端进行显示,将手指放置在心率检测传感器上,上位机端有心率显示,烟雨雾浓度超过阈值会进行报警并显示工人位置。

2 主要器件

  • STM32F103C8T6
  • 心率传感器
  • 温室度传感器
  • 烟雾浓度传感器

3 实现效果

在这里插入图片描述

4 设计原理

4.1 ATGM332D定位模块

在这里插入图片描述
简介

ATGM332D-5N 系列模块是12X16 尺寸的高性能BDS/GNSS 全星座定位导航模块系列的总称。该系列模块产品都是基于中科微第四代低功耗 GNSS SOC单芯片—AT6558,支持美国的 GPS、中国的 BDS(北斗卫星导航系统)

ATGM332D-5N 系列模块具有高灵敏度、低功耗、低成本等优势,适用车载导航、手持定位、可穿戴设备,可以直接替换 NEO-M8N,封装尺寸和引脚定义完全兼容。

模块默认波特率为9600,所有报文全开,不能直接使用
9600且所有报文全开,会造成数据堵塞,发生时间整秒延迟,大约在1,2,3,4秒整秒跳动。

必须调高波特率到115200;或关闭多余报文,仅留RMC。

经纬数据说明
在这里插入图片描述

4.2 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 ,就可以得到转换后的光强度数值,通过编写相应算法就可以得到心率值和血氧饱和度。
在这里插入图片描述

4.3 DHT11温湿度传感器

简介
在这里插入图片描述
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为 4 针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。
接线
在这里插入图片描述
DHT11编码步骤

  1. 单片机上点后1s内不读取(不重要)
  2. 主机(单片机)发送起始信号:
    • 主机先拉高data
    • 拉低data延迟18ms
    • 拉高data(通过此操作将单片机引脚设置为输入)。
  3. 从机(DHT11)收到起始信号后进行应答:
    • 从机拉低data,主机读取到data线被拉低持续80us后从机拉高data线, 持续80us,直到高电平结束,意味着主机可以开始接受数据。
  4. 主机开始接收数据:
    • 主机先把data线拉高(io设置为输入)
    • 从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)
    • 从机拉高data线后,延迟40us左右(28~70us之间)主机再次读取data线电平,如果为低电平,则为“0”,如果为高电平,则为“1”。
    • 继续重复上述1,2步骤累计40次。

4.4 MQ-2烟雾传感器

简介
MQ-2常用于家庭和工厂的气体泄漏监测装置,适宜于液化气、苯、烷、酒精、氢气、烟雾等的探测。故因此,MQ-2可以准确来说是一个多种气体探测器。
MQ-2的探测范围极其的广泛。它的优点:灵敏度高、响应快、稳定性好、寿命长、驱动电路简单。
在这里插入图片描述
二、MQ-2的工作原理
MQ-2型烟雾传感器属于二氧化锡半导体气敏材料,属于表面离子式N型半导体。处于200~300摄氏度时,二氧化锡吸附空气中的氧,形成氧的负离子吸附,使半导体中的电子密度减少,从而使其电阻值增加。当与烟雾接触时,如果晶粒间界处的势垒收到烟雾的调至而变化,就会引起表面导电率的变化。利用这一点就可以获得这种烟雾存在的信息,烟雾的浓度越大,导电率越大,输出电阻越低,则输出的模拟信号就越大。
在这里插入图片描述

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;
}


//DHT11温湿度传感器部分
#include "reg52.h"
#include "LCD1602.h"
#include "intrins.h"

//typedef unsigned char uchar;
//typedef unsigned int uint;
	
//定义变量
sbit Data=P3^6;
uchar rec_dat[13];//用于保存接收到的数据组

void DHT11_delay_us(uchar n)
{
    while(--n);
}

void DHT11_delay_ms(uint z)
{
   uint i,j;
   for(i=z;i>0;i--)
      for(j=110;j>0;j--);
}

void DHT11_start()
{
   Data=1;
   DHT11_delay_us(2);
   Data=0;
   DHT11_delay_ms(20);   //延时18ms以上
   Data=1;
   DHT11_delay_us(30);
}

uchar DHT11_rec_byte()      //接收一个字节
{
   uchar i,dat=0;
  for(i=0;i<8;i++)    //从高到低依次接收8位数据
   {          
      while(!Data);   //等待50us低电平过去
      DHT11_delay_us(8);     //延时60us,如果还为高则数据为1,否则为0 
      dat<<=1;           //移位使正确接收8位数据,数据为0时直接移位
      if(Data==1)    //数据为1时,使dat加1来接收数据1
         dat+=1;
      while(Data);  //等待数据线拉低    
    }  
    return dat;
}

void DHT11_receive()      //接收40位的数据
{
    uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; 
    DHT11_start();
    if(Data==0)
    {
        while(Data==0);   //等待拉高     
        DHT11_delay_us(40);  //拉高后延时80us
        R_H=DHT11_rec_byte();    //接收湿度高八位  
        R_L=DHT11_rec_byte();    //接收湿度低八位  
        T_H=DHT11_rec_byte();    //接收温度高八位  
        T_L=DHT11_rec_byte();    //接收温度低八位
        revise=DHT11_rec_byte(); //接收校正位

        DHT11_delay_us(25);    //结束

        if((R_H+R_L+T_H+T_L)==revise)      //校正
        {
            RH=R_H;
            RL=R_L;
            TH=T_H;
            TL=T_L;
        } 
		
	
        /*数据处理,方便显示*/
        rec_dat[0]=RH/10+'0';
        rec_dat[1]=(RH%10)+'0';
		rec_dat[2]='%';
        rec_dat[3]='R';
        rec_dat[4]='H';
        rec_dat[5]=' ';
		rec_dat[6]=' ';
        rec_dat[7]=(TH/10)+'0';
        rec_dat[8]=(TH%10)+'0';
		rec_dat[9]='^';
        rec_dat[10]='C';
    }
}

void main()
{
	//使用lcd1602显示数据
	DHT11_receive();
	lcd1602(rec_dat);
}
// MQ-2烟雾传感器

unsigned char GetYanWuValue(void)
{
		unsigned int sum=0;
		unsigned char m,value=0;
		for(m=0;m<20;m++)			//读50次AD值
			sum = adc0832(0)+sum;		//读到的AD值,将读到的数据累加到sum
		value=(unsigned char)(sum/20);				//跳出上面的for循环后,将累加的总数除以50得到平均值value
	
		if(value > ADC_Zero) 
				value = value - ADC_Zero;              //首先减去零点漂移
			else
				value = 0;
	
		return value;

}



5 最后

本文含有隐藏内容,请 开通VIP 后查看