基于STM32的智能心电监测仪

项目概述

一个用于监测患者心电信号的嵌入式系统。该系统旨在采集、处理和显示患者的心电数据,以帮助医生和患者跟踪心脏健康状态。系统将使用嵌入式技术,包括传感器、微控制器和用户界面,以实现这一目标。

1.功能描述

数据采集:系统需要能够连接到心电传感器,采集患者的心电信号数据。传感器能够捕获心电波形并将数据传输到嵌入式系统。

数据处理:采集的心电数据在嵌入式系统中进行实时处理。

数据显示:具有用户界面,可以实时显示心电波形。

用户交互:用户通过下发采样率和采样时长来控制系统开启采集。

2.系统总体设计

  • 3.硬件架构

    3.1硬件选型

需要能够采集心电信号并放大心电信号的传感器。目前业界性价比比较高的是TI公司的AD8232。

芯片如果要正常工作还需要一些外围电路,传感器+外围电路就构成了心电采集模块

 ADC我们选用STM32内置ADC1,使用其11通道来接收采集模块放大后的模拟信号。经过ADC转换后的心电数字信号,需要发给上位机进行显示。考虑到数据并不大,我们采用常见的串口通讯。在本项目中我们选用的是串口2。(串口1用于debug输出)

 心电采集模块AD8232简介

AD8232是一款生物信号放大器,通常用于心电图(ECG)信号的采集和放大。

         AD8232是专门设计用于放大心电信号的放大器。它可以将微弱的生物信号从人体电极捕获并放大,以便进一步处理和分析。AD8232具有低噪声放大器,可确保准确的信号放大,减少了环境噪声的影响。它集成了用于检测皮肤电极状态的电极检测电路,以确保良好的电极贴合和信号质量。可以使用单电源供电,降低了电源电路的复杂性。

        总的来说,AD8232是一款功能强大且灵活的生物信号放大器,适用于各种心电监测和生物传感应用。它的设计使其易于集成到不同类型的电子设备中,以帮助实现生物医疗和生物监测领域的创新。

导联线

在心电监测中,导联线是用于连接心电电极心电监测仪器的电缆线。不同的心电监测系统和标准可能会使用不同类型的导联,但一般来说,有以下几种主要的导联类型:单导联(单极导联)、三导联(三极导联)、十二导联和特殊导联。关于每种导联线的用途大家可以自行查阅资料。

出于成本考虑,我们项目使用的是单导联

人体连接示意图

AD8232引脚接线

GND和3.3V分别接地和电源。Output出来的就是经过放大后的心电模拟信号,需要连接到STM32的ADC转成数字信号才能被处理。我项目中是连接到ADC111通道(PC1)。

  • 4.软件架构

        根据项目功能划分了硬件驱动层和应用层

         心电监测项目各层模块

 硬件驱动层

 根据用到的硬件外设来实现需要的驱动,包括:ADC驱动(ACD1),USART驱动(USART1和USART2),Timer驱动(TIM6)。

ADC1用于把采集到的心电模拟信号转成数字信号,然后供STM32进行处理。

USART1用于Debug,把调试信息发给上位机。

USART2用于把处理后心电处理信号发送给上位机进行心电波形显示。

TIM6定时器驱动。使用定时器的更新中断,来控制采样率和采样时长。

应用层

应用层分为2个模块:通讯模块和心电采集模块。

通讯模块用于实现与上位机之间的通讯逻辑:解析上位机下发的命令,发送数据到上位机。

心电采集模块用于实现心电采集逻辑和数据处理逻辑。

思考:硬件的中断服务函数怎么处理?

硬件的中断服务函数,当中断发生时,我们需要执行一些业务逻辑。中断服务函数理论上属于驱动层,而业务逻辑属于中间层或应用层。这个时候该如何在中断服务函数中执行中间层或应用层的业务逻辑呢?

解决思路1:不把中断服务函数看作驱动层,在需要的时候,可以把中断服务函数放在任意一层。这种其实是破坏了分层架构,但是实现比较简单。

解决思路2:使用weak函数。类似于HAL库实现思路。在中断服务函数中调用weak函数,上层在需要的地方重写weak函数。我采用此种方法,简单,而且不破坏分层思想。

我在项目代码中使用的是2种思路

  • 5. 使用STM32CubeMX创建工程

    5.1 STM32CubeMX设置

    SYS配置

 RCC配置

 TIM6配置

 串口1配置

 ​​​​​​​串口2配置

 ​​​​​​​ADC配置

 ​​​​​​5.2 心电采集模块

用CubeMx生成的代码中,已经包含驱动层的所有代码。我们一般不做改动。

  1. 应用层
    1. APP_HeartCollect.h
    #ifndef __APP_HEARTCOLLECT_H
    #define __APP_HEARTCOLLECT_H
    
    #include "adc.h"
    #include "dma.h"
    #include "tim.h"
    
    void APP_HeartCollect_Start(uint16_t rate, uint16_t duraion);
    void APP_HeartCollect_Stop(void);
    uint16_t APP_HeartCollect_ReadHeartData(void);
    #endif
    

​​​​​​​        ​​​​​​​       2.APP_HeartCollect.c

#include "APP_HeartCollect.h"

/* 采样时长 ms */
static uint16_t g_duration;
/* 多少ms执行1次采样 */
static uint16_t g_collectMs;
/* 保存定时器中断次数 */
static uint16_t g_time6InterruptCount;

/* 是否要读ADC取电压值了 */
uint8_t g_isToReadAdcValue = 0;

/* 存储dma转运过来的电压值 */
uint16_t v = 0;

/**
 * @description: 开始采集心电图
 * @param {uint16_t} rate 采样率 1分钟的采样多少次
 * @param {uint16_t} duraionSecond 采集时长s  用来控制定时器的停止。
 */
void APP_HeartCollect_Start(uint16_t rate, uint16_t duraion)
{
    /* 0. 保存采样周期(ms) 和 多久执行1次采样 */
    g_duration = 1000 * duraion;
    g_collectMs = 60 * 1000 / rate;

    /* 1. 初始化DMA */
    MX_DMA_Init();

    /* 2. 初始化ADC1 */
    MX_ADC1_Init();

    /* 3. 启动ADC开始采集 */
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&v, 1);
    /* 4.初始化定时器6 */
    MX_TIM6_Init();

    /* 5. 启动定时器6 */
    HAL_TIM_Base_Start_IT(&htim6);
}

/**
 * @description: 结束采集心电图
 */
void APP_HeartCollect_Stop(void)
{
    HAL_ADC_Stop_DMA(&hadc1);
}

/* 覆写定时器中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    /* 0. 判断是否是定时器6产生的中断 */
    if (htim->Instance != TIM6)
        return;

    /* 1. 定时器计时超过了采样时长,则停止定时器 */
    g_time6InterruptCount++;
    if (g_time6InterruptCount >= g_duration)
    {
        HAL_TIM_Base_Stop(htim);
        return;
    }
    /* 2. 判断是否到了应该读取心电数据了 */
    if (g_time6InterruptCount % g_collectMs == 0)
    {
        g_isToReadAdcValue = 1;
    }
}

/**
 * @description: 读取心电数据,其实就是ADC的电压值
 * @return {*} 读取到的ADC电压值。范围 [0-4095]
 */
uint16_t APP_HeartCollect_ReadHeartData(void)
{
    /* 1. 等待定时器告诉我们该去读adc值 */
    while (g_isToReadAdcValue == 0)
        ;

    /* 2. 把g_isToReadAdcValue设置为0,用于下次读取 */
    g_isToReadAdcValue = 0;

    /* 3. 返回读取到电压值 */
    return v;
}

           main.c

int main()
{
    /* 1.初始化HAL库 */
    HAL_Init();

    /* 2. 系统时钟配置 */
    SystemClock_Config();

    /* 3. GPIO 初始化 */
    MX_GPIO_Init();

    /* 3. 初始化Debug */
    debug_init();

    debug_printfln("===尚硅谷嵌入式项目之心电监测HAL库版===");

    /* 4.  启动通讯模块*/
    APP_Commucation_Start();

    /* 5. 处理上位机传来的命令, 得到采样率和采样时长 */
    uint16_t rate, duration;
    APP_Commucation_CommandProcess(&rate, &duration);

    debug_printfln("rate=%d, duration=%d", rate, duration);

    /* 6. 开始采集心信号 */
    APP_HeartCollect_Start(rate, duration);
    while (1)
    {
        /* 7. 读取心电数据 */
        uint16_t heartData = APP_HeartCollect_ReadHeartData();
        /* 8. 上传心电数据到上位机 */
        APP_Commucation_SendDataToUpperComputer(heartData);
    }
}
  • 9 章 上位机

上位机负责下发命令和显示心电图。用Python编写,已经打包成exe可以在windows下直接使用。

注意:软件启动后,按照图示的123顺序来设置和启动,否则软件会报错。

总结

把连线按照如图连接起来。然后打开心电监测仪上位机软件。 

  1. 选择表示STM32的串口,设置波特率。然后点击对应设置。
  2. 设置采样率(每分钟采样多少条数据)。然后点击对应的设置。
  3. 设置采样时长(采样多久)。然后点击启动。

这样一个简易的基于单片机的心电监测仪就完成了,此项目是我之前业余时照着网上大佬分享的脉搏显示仪自己琢磨的,欢迎大家一起学习,有什么不对的地方可以指出,好啦,麻烦大家关注一波哦。​​​​​​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值