毕业设计| STM32家庭健康监测系统

2023-04-24  

系统框架

系统可以实时监测被测者的心率、体温以及周遭环境的温度,也同时可以通过姿态解算来判断被测者是否摔倒。该系统可以将被测者的心率、体温等数据既在本地显示,也可以通过WI-FI传输至云平台以实现远程显示。当被测者摔倒时会发出蜂鸣声,以便引起周围人向被测者施以援手;当被测者吸烟时则会发出警报直至香烟熄灭,可以让被测者远离不健康的生活习惯。


功能简介

该设计主要功能如下:

1) 实时的采集心率、温度、烟雾浓度等信息;

2) 实时的显示心电图以及温度数值信息;

3) 实现跌倒的判断,并且在跌倒时发出报警;

4) 实现吸烟警告,在吸烟时发出报警;

5) 实现将温度、心率、姿态解算数据、烟雾浓度等发送至云平台;

6) 通过登录云平台查看心率、温度、烟雾浓度的折线图。


Image



系统硬件组成

1)控制核心

STM32C8T6(最小核心板),当然,用其他型号的STM32,如STM32ZET6也是可以的。


2)外围模块

心率模块:ADS1292R

温度模块:LMT70

姿态解算模块:MPU6050

WIFi模块:ATK-ESP8266

液晶显示模块:OLED12864

Image

3)上位机

OneNET云平台

Image


相关设计及框图

1)系统总统设计

基于本系统的需求,本设计提出了分层的设计思想,将系统分为:硬件采集层、网络传输层、数据展示层,提高了软硬件之间的耦合性,便于分工与维护。其中,硬件采集层负责收集心率、温度、烟雾浓度、姿态解算数据,网络传输层负责将前一层采集到的数据通过WI-FI传输到数据展示层,而数据展示层分为本地数据展示和云端数据展示。

Image

2)数据采集层设计框图

Image

3)数据传输层设计框图

Image

4)数据展示层设计框图

Image

各功能模块详细设计

1)心率监测功能

在检测的时候,会有噪音对检测的结果带来干扰,比如人体自身以及电路的干扰都会对检测结果产生影响,当许多干扰信号和心电信号混在一起的时候就可能会使有用信号发生变形甚至被淹没,因此滤波是非常必要的,而滤波可以分为代码实现的软件滤波和芯片自带的硬件滤波,ADS1292R是一款医用级的前端芯片。该模块按照以下的工作时序与STM32进行全双工的SPI通讯。

Image

该模块在系统中的工作流程图:

Image

当ADS1292R上电后至少需要等待1s,当寄存器稳定后再进入ADS1292R的引脚初始化,其中包含了CS、START、RESET、DRYDY控制引脚,当引脚初始化超时则会重新初始化,其次是SPI通讯端口的初始化,然后继续对ADS1292R进行采集操作的一系列配置就可以获得心率的原始数据。

该段是对ADS1292R初始化的配置函数:


/* 初始化ads1292r,超时时间为timeout*100ms,返回0表示初始化失败 */

uint8_t ads1292r_init( uint8_t timeout)

{

    uint8_t id ;

    

    /* gpio接口初始化,针对非hal库版本需要再此添加代码 */

    port_gpio_init() ;

    

    /* spi接口初始化 */

    port_spi_init() ;

    

    ADS1292_CS_SET() ;

    

    ADS1292_REST_RESET() ;

    ADS1292_START_RESET() ;

    port_delay_ms(1000) ;

    ADS1292_REST_SET() ;

    port_delay_ms(100) ;    /* 硬件复位 */

    

  ads1292r_send_cmd(ADS1292R_COMMAND_SDATAC) ;    /* 软件复位,并停止连续读状态 */

    port_delay_ms(100) ;

    ads1292r_send_cmd(ADS1292R_COMMAND_RESET) ;

    port_delay_ms(1000) ;

    ads1292r_send_cmd(ADS1292R_COMMAND_SDATAC) ;

    port_delay_ms(100) ;

    

    while( ( id != ads1292r_reg.id) && ( timeout > 0))       /* 识别芯片型号,1292r为0x73 */

  {        

        id = ads1292r_rw_reg(ADS1292R_COMMAND_RREG|ADS1292R_REG_ID, 0) ;

        

        timeout-- ;

        port_delay_ms(100) ;

  }

    

    /* 500sps采样率 */

    ads1292r_reg.cfg1 = ADS1292R_SET_BITS(ads1292r_reg.cfg1, ADS1292R_DR, ADS1292R_OVERSAMPLING_500SPS) ;

    

    /* 导联脱落比较器开,内部2.42v参考电压 */

    ads1292r_reg.cfg2 = ADS1292R_SET_BITS(ads1292r_reg.cfg2, ADS1292R_PDB_LOFF_COMP, ADS1292R_PDB_LOFF_COMP_ON) ;  

    ads1292r_reg.cfg2 = ADS1292R_SET_BITS(ads1292r_reg.cfg2, ADS1292R_PDB_REFBUF, ADS1292R_PDB_REFBUF_ON) ;       

    ads1292r_reg.cfg2 = ADS1292R_SET_BITS(ads1292r_reg.cfg2, ADS1292R_VREF_4V, ADS1292R_VREF_2420MV) ;      

    

    /* 通道二导联脱落检测功能开 */

    ads1292r_reg.loff_sens = ADS1292R_SET_BITS(ads1292r_reg.loff_sens, ADS1292R_LOFF2N, ADS1292R_LOFF2N_ON) ;

    ads1292r_reg.loff_sens = ADS1292R_SET_BITS(ads1292r_reg.loff_sens, ADS1292R_LOFF2P, ADS1292R_LOFF2P_ON) ;

    

    /* pga斩波频率4分频,右腿驱动电源开,开启通道二的右腿驱动输出 */

    ads1292r_reg.rld_sens = ADS1292R_SET_BITS(ads1292r_reg.rld_sens, ADS1292R_CHOP, ADS1292R_CHOP_FREQ_DIV4) ;

    ads1292r_reg.rld_sens = ADS1292R_SET_BITS(ads1292r_reg.rld_sens, ADS1292R_PDB_RLD, ADS1292R_PDB_RLD_ON) ;

    ads1292r_reg.rld_sens = ADS1292R_SET_BITS(ads1292r_reg.rld_sens, ADS1292R_RLD2N, ADS1292R_RLD2N_ON) ;

    ads1292r_reg.rld_sens = ADS1292R_SET_BITS(ads1292r_reg.rld_sens, ADS1292R_RLD2P, ADS1292R_RLD2P_ON) ;

    

    /* 右腿驱动参考电压选择内部信号 */

    ads1292r_reg.resp2 = ADS1292R_SET_BITS(ads1292r_reg.resp2, ADS1292R_RLDREF_INT, ADS1292R_RLDREF_INT) ;

    

    return timeout ;

}

其他的关于芯片识别和采样频率设置、开启导联脱落比较器、开启二联导脱落检测功能、开启通道二右腿驱动输出并选择内部信号的代码这里就不放了,这些可以通过官方给的芯片资料是可以移植的。




然后来说一下软件滤波的操作:


初始化函数返回的timeout的是初始化失败次数,如果超过则会一直卡在这里不能继续往下。流程图的原始数据是仅仅只是硬件滤波后的ADC原始数据,为了能够得到符合正常范围的心电信号,还需要对数据进行软件滤波,处于过于偏大或者偏小的波形,最后才能将滤波后的心率值存到数组中。以下为软件滤波的部分代码:


ads1292r_get_value(ad_b);

        ecg_data = i24toi32(ad_b+6);  /* 转换原始数据 */

        ecg_sum = 0;

        for(i = 0; i < 8; i++)

        {

            ecg_data_s[i] = ecg_data_s[i+1];

            ecg_sum += ecg_data_s[i];

        }

        ecg_data_s[8] = ecg_data;

        ecg = (ecg_sum + ecg_data) / 9;


        ecg_min = 5000000;

        ecg_max = -5000000;

        for(i=0; i < 29; i++)

        {

            ecg_buf[i]=ecg_buf[i+1];

            ecg_max = ecg_max < ecg_buf[i] ? ecg_buf[i] : ecg_max;

            ecg_min = ecg_min > ecg_buf[i] ? ecg_buf[i] : ecg_min;

        }

        ecg_buf[29]=ecg;

        ecg_max = ecg_max < ecg ? ecg : ecg_max;

        ecg_min = ecg_min > ecg ? ecg : ecg_min;


        if((ecg_max - ecg_min > 30000)&&(tim3_tick > 200))

        {

            if((60000/tim3_tick < 200) && (60000/tim3_tick > 40))

            {

                Heart_buff[0] = Heart_buff[1];

                Heart_buff[1] = Heart_buff[2];

                Heart_buff[2] = Heart_buff[3];

                Heart_buff[3] = Heart_buff[4];

                Heart_buff[4] = Heart_buff[5];

                Heart_buff[5] = Heart_buff[6];

                Heart_buff[6] = Heart_buff[7];

                Heart_buff[7] = Heart_buff[8];

                Heart_buff[8] = Heart_buff[9];

                Heart_buff[9] = 60000/tim3_tick;


                Heart_rate=(Heart_buff[0]+Heart_buff[1]+Heart_buff[2]+Heart_buff[3]+Heart_buff[4]

                            +Heart_buff[5]+Heart_buff[6]+Heart_buff[7]+Heart_buff[8]+Heart_buff[9])/10;

            }


            tim3_tick = 0;

        }

从流程图中可以看到在定时器中断的作用下,每隔100ms就会将获得滤波后的心电信号以串口的方式传输到主控的STM32芯片再对心率数据进行下一步的处理。




2)温度采集功能


LMT70是一款高精度的医用级温度传感器,其仅需3.3V的电压就可以驱动,且功耗极低,可以检测的温度范围是-55摄氏度到150摄氏度。温度模块与主控STM32采用的是IIC通信,其具体的引脚连接图如下:

Image

以下为该模块在系统中的流程图:

Image

由于在硬件电路中,ADS115和LMT70是合在一起的,共同算作温度模块,因此,在流程图中的ADS115初始化是指整个温度模块的初始化,以下为ADS115初始化函数:

从流程图中可以看到在定时器中断的作用下,每隔100ms就会将获得滤波后的心电信号以串口的方式传输到主控的STM32芯片再对心率数据进行下一步的处理。




2)温度采集功能


LMT70是一款高精度的医用级温度传感器,其仅需3.3V的电压就可以驱动,且功耗极低,可以检测的温度范围是-55摄氏度到150摄氏度。温度模块与主控STM32采用的是IIC通信,其具体的引脚连接图如下:


Image

以下为该模块在系统中的流程图:


Image

由于在硬件电路中,ADS115和LMT70是合在一起的,共同算作温度模块,因此,在流程图中的ADS115初始化是指整个温度模块的初始化,以下为ADS115初始化函数:

该初始化函数完成了IIC引脚的初始化以及寄存器的配置。通过对convert寄存器的处理,可以获得原始的ADC数据,最后再通过对该数据进行才得到最终温度数值,再把该数据通过IIC传给STM32进行进一步的处理。




具体的式子如下:


//采集ADC数 (温度)

 adcx=GetAds1115Values();//采集ADC数据

 temp=(float)adcx*0.125f;

 tem = (-0.0000084515f)*temp*temp+(-0.176928f)*temp+203.393f;

3)跌倒监测功能(姿态解算)


MPU6050是整合性六轴运动处理组件,其中组合了三轴陀螺仪和三轴加速器。加速度传感器是用来检查空集中的6个面(前后左右上下)中的哪些面受到了力的作用,陀螺仪是检测3个方向的欧拉角,以水平摊开的手掌为例,判断手掌上下摆动幅度的角度叫俯仰角用pitch表示,判断手掌左右水平移动的角度叫偏航角用yaw表示,判断手掌左右翻滚的角度叫滚转角用roll表示。本模块所需要实现简单的跌倒检测的功能,首先得知道什么算跌倒或者如何表示跌倒的状态。跌倒是指突发、不自主的、非故意的体位改变,倒在地上或更低的平面上[4]。人体在跌倒瞬间,身体的加速度和角速度在水平和垂直方向都会发生巨大的变化[5]。因此,通过判断三个方向的欧拉角以及三个方向的综合加速度综合考虑来判断是否摔倒。

Image

MPU6050的初始化代码是可以根据官方资料移植的,所以这里就不贴初始化了,这里介绍一下跌倒监测的实现。


首先知道DMP功能移植DMP是MPU6050内部的运动引擎,由Inven Sence公司自主提供,用于从内部传感器中直接解算出四元数,大幅降低运算复杂度。由于DMP可直接输出四元数,从而可以减轻外围微处理器的工作负担,且能避免繁琐的滤波和数据融合处理,能降低系统运算的复杂度。以为为四元组转换公式的转换公式,其中四元单位数的平方和为1。

Image

然后通过四元组转化得到三个方向的欧拉角,代码实现如下:

if(sensors&INV_WXYZ_QUAT) 

  {

    q0 = quat[0] / q30;  //q30格式转换为浮点数

    q1 = quat[1] / q30;

    q2 = quat[2] / q30;

    q3 = quat[3] / q30; 

    //计算得到俯仰角/横滚角/航向角

    *pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;  // pitch

    *roll  = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3;  // roll

    *yaw   = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;  //yaw

得到三个方向的欧拉角之后,再结合人体加速度向量幅值SVM来综合判断是否跌倒。


其中SVM是三个方向的综合加速度值,式子如下:

以下为跌倒监测的代码实现:


void fall(){

      MPU_Get_Accelerometer(&aacx,&aacy,&aacz);  //得到加速度传感器数据    

        SVM = sqrt(pow(aacx,2)+  pow(aacy,2) + pow(aacz,2));  

        //printf("pitch:%0.1f   roll:%0.1f   yaw:%0.1f   SVM:%urn",fabs(pitch),fabs(roll),fabs(yaw),SVM);

        

        //分析x、y、z角度的异常判断

        if( fabs(pitch)>40 || fabs(roll)>40 || fabs(yaw)>40 )//倾斜角度的“绝对值”大于70°,SVM大于设定的阈值时,即认为摔倒

          mpu_1_flag = 1;  

        else 

          mpu_1_flag = 0;

        

        //分析加速度SVM的异常判断

        if( SVM>22000 || SVM<12000 ){i = 0;}

        i++;

        

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。