基于STM32的便携式心电监护仪

举报
DS小龙哥 发表于 2025/12/25 11:55:50 2025/12/25
【摘要】 项目开发背景随着心血管疾病发病率的逐年上升,心电监护在日常健康管理中的重要性日益凸显。传统心电监测设备往往体积庞大、成本较高,且多限于医院环境使用,无法满足个人日常长期监测的需求。因此,开发一种便携、低成本且功能完善的心电监护仪,能够帮助用户实时了解心脏健康状况,并在异常时及时预警,具有重要的现实意义。当前,市场上虽有部分便携心电设备,但常存在功能单一、数据传输不便或报警机制不完善等问题。...

项目开发背景

随着心血管疾病发病率的逐年上升,心电监护在日常健康管理中的重要性日益凸显。传统心电监测设备往往体积庞大、成本较高,且多限于医院环境使用,无法满足个人日常长期监测的需求。因此,开发一种便携、低成本且功能完善的心电监护仪,能够帮助用户实时了解心脏健康状况,并在异常时及时预警,具有重要的现实意义。

当前,市场上虽有部分便携心电设备,但常存在功能单一、数据传输不便或报警机制不完善等问题。基于STM32微控制器的解决方案,结合心电采集、显示、报警和无线通信模块,能够有效整合实时心电信号处理、直观波形显示和智能预警功能,填补了个人健康监护领域的空白。此外,通过Wi-Fi技术将数据上传至手机APP,可实现远程分析和长期跟踪,进一步提升健康管理的便捷性和有效性。

本项目的开发旨在利用现代嵌入式技术和物联网理念,构建一个高效可靠的便携式心电监护系统。它不仅能够降低用户对专业医疗设备的依赖,还能为心血管疾病预防和早期干预提供技术支持,推动个人健康监护向智能化、普及化方向发展。

设计实现的功能

(1) 通过AD8232心电信号采集模块实时采集使用者的心电信号。
(2) 将心电波形实时显示在0.96英寸OLED屏幕上,并计算并显示实时心率。
(3) 当检测到心率异常(过快或过慢)时,触发5V有源蜂鸣器和LED指示灯进行本地声光报警。
(4) 通过ESP8266 Wi-Fi模块将心电数据上传至手机APP,供进一步分析。

项目硬件模块组成

(1)主控芯片:STM32F103C8T6单片机。
(2)心电采集:AD8232心电信号采集模块。
(3)显示模块:0.96英寸OLED显示屏。
(4)通信模块:ESP8266 Wi-Fi模块。
(5)报警模块:5V有源蜂鸣器和LED指示灯。

设计意义

该便携式心电监护仪的设计意义在于提供一种便捷、实时的个人心脏健康监测方案,帮助用户随时了解自身心电状况。通过集成心电采集和显示功能,设备能够直观地呈现心电波形和心率数据,使用户无需依赖大型医疗设备即可进行日常监测,从而提升健康管理的主动性和可及性。

设备具备心率异常报警功能,可在检测到心率过快或过慢时立即触发声光警示,这有助于用户及时识别潜在的心脏问题并采取应对措施,减少因延误处理而引发的健康风险。这种即时响应机制特别适合高风险人群或康复期患者使用,增强了安全监护的可靠性。

通过Wi-Fi模块将心电数据上传至手机APP,设备支持数据的远程存储和分析,方便用户或医疗专业人员追踪长期趋势,为诊断和健康评估提供依据。这一特性扩展了传统心电监护的局限性,促进了家庭医疗与远程医疗的融合,有助于降低医疗成本并提高服务效率。

整体设计基于STM32等成熟硬件模块,实现了低成本、高集成度的解决方案,体现了嵌入式系统在医疗电子领域的实用价值。它不仅推动了智能医疗设备的普及,还为个人健康监护技术的创新提供了参考,具有广泛的社会应用前景。

设计思路

系统以STM32F103C8T6单片机作为核心控制器,负责协调各个硬件模块的工作。首先,通过AD8232心电信号采集模块实时采集使用者的心电信号,该模块对模拟心电信号进行放大和滤波,输出稳定的电压信号。STM32通过内置ADC以固定采样率将模拟信号转换为数字值,并对数据进行初步处理,如去除噪声和基线漂移,以提取有效的心电波形。

处理后的心电数据被用于两个主要目的:一是驱动0.96英寸OLED显示屏实时显示动态心电波形,二是计算实时心率值。心率计算基于检测QRS波群中的R波间隔,通过算法计算每分钟心跳次数,并在OLED屏幕上更新显示。显示模块通过I2C或SPI接口与STM32通信,确保波形和数据的流畅刷新。

当系统检测到心率异常,例如心率过快或过慢超出预设阈值时,STM32会触发本地报警机制。这包括控制5V有源蜂鸣器发出声音警报,同时点亮LED指示灯提供视觉提示,以提醒用户及时注意。报警状态会持续直到心率恢复正常或用户手动干预。

此外,系统通过ESP8266 Wi-Fi模块实现无线通信功能。STM32将心电数据打包后,通过UART接口发送AT指令控制ESP8266连接到Wi-Fi网络,并将数据上传至手机APP。这允许用户远程查看和分析心电记录,确保数据的可追溯性和进一步处理。整个设计注重实用性和可靠性,各模块协同工作,实现便携式心电监护的基本功能。

框架图

+------------------------+     +------------------------+     +------------------+
|    心电采集模块        |---->|    主控芯片           |---->|   显示模块       |
|      (AD8232)         |     |  (STM32F103C8T6)      |     |   (OLED)        |
+------------------------+     +------------------------+     +------------------+
                                      |
                                      | (心率异常时)
                                      v
                              +------------------------+
                              |    报警模块           |----> 本地声光报警
                              |  (蜂鸣器 + LED)       |
                              +------------------------+
                                      |
                                      v
                              +------------------------+
                              |    通信模块           |----> 手机APP (通过Wi-Fi)
                              |    (ESP8266)          |
                              +------------------------+

系统总体设计

该系统基于STM32F103C8T6单片机作为核心控制器,负责协调和管理各个硬件模块的运作,实现便携式心电监护功能。系统通过AD8232心电信号采集模块实时采集使用者的心电信号,AD8232模块对原始心电信号进行放大和滤波处理,输出模拟信号至STM32的ADC接口进行数字化转换,确保信号准确性和稳定性。

数字化后的心电数据由STM32进行实时处理,包括波形分析和心率计算。系统通过I2C或SPI接口驱动0.96英寸OLED显示屏,动态显示心电波形曲线和实时心率数值,为用户提供直观的视觉反馈。心率计算基于心电信号的R波检测算法,STM32通过软件实现心率值的更新和显示。

当STM32检测到心率异常,如过快或过慢超出预设阈值时,系统会立即触发本地报警机制。报警模块包括5V有源蜂鸣器和LED指示灯,蜂鸣器发出声音警示,同时LED闪烁,形成声光报警以提醒用户注意潜在健康风险。

系统还集成ESP8266 Wi-Fi模块,通过串口通信与STM32连接,实现无线数据传输功能。心电数据被打包后通过Wi-Fi网络上传至手机APP,支持远程存储和进一步分析,扩展了系统的应用场景和实用性。整个设计注重低功耗和便携性,确保在移动环境中稳定运行。

系统功能总结

序号 功能描述
1 通过心电采集芯片实时采集使用者的心电信号。
2 将心电波形实时显示在OLED屏幕上,并计算显示实时心率。
3 当检测到心率异常(过快或过慢)时,触发本地声光报警。
4 系统可通过Wi-Fi将心电数据上传至手机APP,供进一步分析。

设计的各个功能模块描述

心电采集模块使用AD8232心电信号采集芯片实时采集使用者的心电信号,将模拟心电数据转换为数字信号,并传输给主控芯片进行处理,确保信号的准确性和稳定性。

显示模块通过0.96英寸OLED屏幕实时显示心电波形和计算出的心率数值,提供清晰的视觉反馈,方便使用者随时查看自身心电状态。

报警模块在检测到心率异常(如过快或过慢)时,通过5V有源蜂鸣器和LED指示灯触发本地声光警报,及时提醒使用者注意潜在健康风险。

通信模块利用ESP8266 Wi-Fi模块将采集到的心电数据上传至手机APP,实现远程数据传输和进一步分析,增强系统的监控能力。

主控模块以STM32F103C8T6单片机为核心,负责协调所有功能模块的运行,包括信号采集、心率计算、显示更新、报警判断和数据传输,确保系统高效可靠地工作。

上位机代码设计

// ECG Monitor Desktop Application
// main.cpp

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>
#include <winsock2.h>
#include <windows.h>
#include <iomanip>
#include <algorithm>

#pragma comment(lib, "ws2_32.lib")

class ECGDataProcessor {
private:
    std::vector<int> ecgBuffer;
    std::vector<int> heartRateBuffer;
    int currentHeartRate;
    bool isConnected;
    SOCKET clientSocket;
    
public:
    ECGDataProcessor() : currentHeartRate(0), isConnected(false), clientSocket(INVALID_SOCKET) {
        ecgBuffer.resize(1000, 0);
        heartRateBuffer.resize(60, 0);
    }
    
    ~ECGDataProcessor() {
        disconnect();
    }
    
    bool initializeWiFiConnection(const std::string& ip, int port) {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
            std::cerr << "WSAStartup failed!" << std::endl;
            return false;
        }
        
        clientSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (clientSocket == INVALID_SOCKET) {
            std::cerr << "Socket creation failed!" << std::endl;
            WSACleanup();
            return false;
        }
        
        sockaddr_in serverAddr;
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(port);
        serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
        
        if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
            std::cerr << "Connection failed!" << std::endl;
            closesocket(clientSocket);
            WSACleanup();
            return false;
        }
        
        isConnected = true;
        std::cout << "Connected to ECG device at " << ip << ":" << port << std::endl;
        return true;
    }
    
    void disconnect() {
        if (isConnected) {
            closesocket(clientSocket);
            WSACleanup();
            isConnected = false;
            std::cout << "Disconnected from ECG device" << std::endl;
        }
    }
    
    bool receiveECGData() {
        if (!isConnected) return false;
        
        char buffer[256];
        int bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
        
        if (bytesReceived > 0) {
            buffer[bytesReceived] = '\0';
            return parseECGData(buffer);
        }
        
        return false;
    }
    
    bool parseECGData(const std::string& data) {
        // Expected format: "ECG:value,HR:rate"
        size_t ecgPos = data.find("ECG:");
        size_t hrPos = data.find("HR:");
        
        if (ecgPos != std::string::npos && hrPos != std::string::npos) {
            try {
                // Extract ECG value
                std::string ecgStr = data.substr(ecgPos + 4, hrPos - (ecgPos + 5));
                int ecgValue = std::stoi(ecgStr);
                
                // Extract heart rate
                std::string hrStr = data.substr(hrPos + 3);
                int hrValue = std::stoi(hrStr);
                
                // Update buffers
                ecgBuffer.erase(ecgBuffer.begin());
                ecgBuffer.push_back(ecgValue);
                
                heartRateBuffer.erase(heartRateBuffer.begin());
                heartRateBuffer.push_back(hrValue);
                
                currentHeartRate = hrValue;
                
                return true;
            } catch (const std::exception& e) {
                std::cerr << "Data parsing error: " << e.what() << std::endl;
            }
        }
        
        return false;
    }
    
    void displayECGWaveform() {
        system("cls");
        std::cout << "=== Portable ECG Monitor ===" << std::endl;
        std::cout << "Real-time ECG Waveform Display" << std::endl;
        std::cout << "==============================" << std::endl;
        
        // Display heart rate
        std::cout << "Heart Rate: " << currentHeartRate << " BPM" << std::endl;
        
        // Simple ASCII waveform display (last 50 samples)
        int displaySamples = std::min(50, static_cast<int>(ecgBuffer.size()));
        int startIdx = ecgBuffer.size() - displaySamples;
        
        std::cout << "ECG Waveform:" << std::endl;
        
        // Find min and max for scaling
        int minVal = *std::min_element(ecgBuffer.begin() + startIdx, ecgBuffer.end());
        int maxVal = *std::max_element(ecgBuffer.begin() + startIdx, ecgBuffer.end());
        int range = std::max(maxVal - minVal, 1);
        
        // Display waveform
        for (int i = startIdx; i < ecgBuffer.size(); ++i) {
            int scaledValue = ((ecgBuffer[i] - minVal) * 20) / range;
            std::cout << std::string(scaledValue, ' ') << "*" << std::endl;
        }
        
        // Check for abnormal heart rate
        if (currentHeartRate > 100 || currentHeartRate < 60) {
            std::cout << "\n!!! WARNING: Abnormal Heart Rate Detected !!!" << std::endl;
            Beep(1000, 500); // Audio alert
        }
        
        std::cout << "\nPress 'q' to quit, 's' to save data" << std::endl;
    }
    
    void saveDataToFile() {
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);
        
        std::string filename = "ecg_data_" + std::to_string(time_t) + ".csv";
        
        std::ofstream file(filename);
        if (file.is_open()) {
            file << "Timestamp,ECG_Value,Heart_Rate\n";
            auto startTime = std::chrono::system_clock::now();
            
            for (size_t i = 0; i < ecgBuffer.size(); ++i) {
                auto sampleTime = startTime + std::chrono::milliseconds(i * 10);
                auto sampleTime_t = std::chrono::system_clock::to_time_t(sampleTime);
                
                file << std::put_time(std::localtime(&sampleTime_t), "%H:%M:%S") << ","
                     << ecgBuffer[i] << "," 
                     << (i < heartRateBuffer.size() ? heartRateBuffer[i] : currentHeartRate) << "\n";
            }
            
            file.close();
            std::cout << "Data saved to " << filename << std::endl;
        } else {
            std::cerr << "Failed to save data to file!" << std::endl;
        }
    }
    
    bool isDeviceConnected() const {
        return isConnected;
    }
    
    int getCurrentHeartRate() const {
        return currentHeartRate;
    }
};

class ECGMonitorApp {
private:
    ECGDataProcessor dataProcessor;
    bool running;
    
public:
    ECGMonitorApp() : running(false) {}
    
    void run() {
        std::cout << "ECG Monitor Desktop Application" << std::endl;
        std::cout << "================================" << std::endl;
        
        // Configure connection
        std::string deviceIP;
        int devicePort;
        
        std::cout << "Enter ECG device IP (default: 192.168.1.100): ";
        std::getline(std::cin, deviceIP);
        if (deviceIP.empty()) deviceIP = "192.168.1.100";
        
        std::cout << "Enter ECG device port (default: 8080): ";
        std::string portStr;
        std::getline(std::cin, portStr);
        devicePort = portStr.empty() ? 8080 : std::stoi(portStr);
        
        // Initialize connection
        if (!dataProcessor.initializeWiFiConnection(deviceIP, devicePort)) {
            std::cerr << "Failed to connect to ECG device!" << std::endl;
            return;
        }
        
        running = true;
        std::cout << "Starting ECG monitoring..." << std::endl;
        std::cout << "Press 'q' to quit, 's' to save data" << std::endl;
        
        // Main monitoring loop
        auto lastDisplayTime = std::chrono::steady_clock::now();
        
        while (running) {
            // Receive data
            if (dataProcessor.receiveECGData()) {
                auto currentTime = std::chrono::steady_clock::now();
                auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastDisplayTime);
                
                // Update display every 100ms
                if (elapsed.count() >= 100) {
                    dataProcessor.displayECGWaveform();
                    lastDisplayTime = currentTime;
                }
            }
            
            // Check for user input
            if (_kbhit()) {
                char key = _getch();
                if (key == 'q' || key == 'Q') {
                    running = false;
                } else if (key == 's' || key == 'S') {
                    dataProcessor.saveDataToFile();
                }
            }
            
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        
        dataProcessor.disconnect();
        std::cout << "ECG Monitor application terminated." << std::endl;
    }
};

int main() {
    ECGMonitorApp app;
    app.run();
    return 0;
}

// Additional utility files:

// File: ECGDataAnalyzer.h
#ifndef ECGDATAANALYZER_H
#define ECGDATAANALYZER_H

#include <vector>
#include <algorithm>

class ECGDataAnalyzer {
public:
    static double calculateAverageHeartRate(const std::vector<int>& heartRates) {
        if (heartRates.empty()) return 0.0;
        
        int sum = 0;
        for (int rate : heartRates) {
            sum += rate;
        }
        return static_cast<double>(sum) / heartRates.size();
    }
    
    static int findMaxHeartRate(const std::vector<int>& heartRates) {
        if (heartRates.empty()) return 0;
        return *std::max_element(heartRates.begin(), heartRates.end());
    }
    
    static int findMinHeartRate(const std::vector<int>& heartRates) {
        if (heartRates.empty()) return 0;
        return *std::min_element(heartRates.begin(), heartRates.end());
    }
    
    static bool detectArrhythmia(const std::vector<int>& heartRates) {
        if (heartRates.size() < 3) return false;
        
        double variance = 0.0;
        double mean = calculateAverageHeartRate(heartRates);
        
        for (int rate : heartRates) {
            variance += (rate - mean) * (rate - mean);
        }
        variance /= heartRates.size();
        
        // High variance may indicate arrhythmia
        return variance > 100.0;
    }
};

#endif

// File: NetworkConfig.h
#ifndef NETWORKCONFIG_H
#define NETWORKCONFIG_H

#include <string>

struct NetworkConfig {
    std::string deviceIP;
    int devicePort;
    int timeoutMs;
    
    NetworkConfig() : deviceIP("192.168.1.100"), devicePort(8080), timeoutMs(5000) {}
    
    bool validate() const {
        // Simple IP validation
        if (deviceIP.empty()) return false;
        if (devicePort <= 0 || devicePort > 65535) return false;
        return true;
    }
};

#endif

模块代码设计

#include "stm32f10x.h"

// OLED屏幕相关定义
#define OLED_I2C I2C1
#define OLED_ADDRESS 0x78

// AD8232引脚定义
#define ECG_ADC ADC1
#define ECG_CHANNEL ADC_Channel_0

// 蜂鸣器和LED引脚定义
#define BUZZER_GPIO GPIOC
#define BUZZER_PIN GPIO_Pin_13
#define LED_GPIO GPIOC  
#define LED_PIN GPIO_Pin_14

// ESP8266相关定义
#define WIFI_USART USART1
#define WIFI_USART_GPIO GPIOA
#define WIFI_TX_PIN GPIO_Pin_9
#define WIFI_RX_PIN GPIO_Pin_10

// 心率阈值
#define HEART_RATE_HIGH 120
#define HEART_RATE_LOW 50

// 函数声明
void System_Init(void);
void GPIO_Init(void);
void ADC_Init(void);
void I2C_Init(void);
void USART_Init(void);
void Timer_Init(void);
void OLED_Init(void);
void OLED_WriteCmd(uint8_t cmd);
void OLED_WriteData(uint8_t data);
void OLED_Clear(void);
void OLED_ShowString(uint8_t x, uint8_t y, char *str);
void OLED_ShowWaveform(uint16_t value);
void ECG_DataProcess(void);
void HeartRate_Calculate(void);
void WiFi_SendData(uint16_t ecg_value, uint16_t heart_rate);
void Alarm_Trigger(uint8_t state);

// 全局变量
volatile uint16_t ecg_raw_value = 0;
volatile uint16_t heart_rate = 0;
volatile uint8_t alarm_state = 0;

int main(void)
{
    System_Init();
    
    while(1)
    {
        ECG_DataProcess();
        HeartRate_Calculate();
        
        // 显示心电波形和心率
        OLED_ShowWaveform(ecg_raw_value);
        OLED_ShowString(0, 0, "HR:");
        OLED_ShowString(30, 0, (char*)&heart_rate);
        
        // 心率异常报警
        if(heart_rate > HEART_RATE_HIGH || heart_rate < HEART_RATE_LOW)
        {
            Alarm_Trigger(1);
            alarm_state = 1;
        }
        else
        {
            Alarm_Trigger(0);
            alarm_state = 0;
        }
        
        // 通过WiFi发送数据
        WiFi_SendData(ecg_raw_value, heart_rate);
        
        // 延时
        for(volatile int i=0; i<100000; i++);
    }
}

void System_Init(void)
{
    // 开启外设时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | 
                    RCC_APB2ENR_ADC1EN | RCC_APB2ENR_AFIOEN;
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN | RCC_APB1ENR_USART2EN;
    
    GPIO_Init();
    ADC_Init();
    I2C_Init();
    USART_Init();
    Timer_Init();
    OLED_Init();
}

void GPIO_Init(void)
{
    // 蜂鸣器和LED初始化
    BUZZER_GPIO->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
    BUZZER_GPIO->CRH |= GPIO_CRH_MODE13_0;
    LED_GPIO->CRH &= ~(GPIO_CRH_MODE14 | GPIO_CRH_CNF14);
    LED_GPIO->CRH |= GPIO_CRH_MODE14_0;
    
    // ADC引脚初始化 (PA0)
    GPIOA->CRL &= ~GPIO_CRL_MODE0;
    GPIOA->CRL &= ~GPIO_CRL_CNF0;
    GPIOA->CRL |= GPIO_CRL_CNF0_1;  // 模拟输入
    
    // I2C引脚初始化 (PB6-SCL, PB7-SDA)
    GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6);
    GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_1;
    GPIOB->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
    GPIOB->CRL |= GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_1;
    
    // USART引脚初始化 (PA9-TX, PA10-RX)
    WIFI_USART_GPIO->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9);
    WIFI_USART_GPIO->CRH |= GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1;
    WIFI_USART_GPIO->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
    WIFI_USART_GPIO->CRH |= GPIO_CRH_CNF10_0;
}

void ADC_Init(void)
{
    // ADC校准
    ADC1->CR2 |= ADC_CR2_ADON;
    for(volatile int i=0; i<1000; i++);
    ADC1->CR2 |= ADC_CR2_RSTCAL;
    while(ADC1->CR2 & ADC_CR2_RSTCAL);
    ADC1->CR2 |= ADC_CR2_CAL;
    while(ADC1->CR2 & ADC_CR2_CAL);
    
    // ADC配置
    ADC1->SQR1 = 0;
    ADC1->SQR2 = 0;
    ADC1->SQR3 = ECG_CHANNEL;
    ADC1->SMPR2 = 7 << (ECG_CHANNEL * 3); // 239.5周期采样时间
    
    ADC1->CR2 |= ADC_CR2_CONT; // 连续转换模式
    ADC1->CR2 |= ADC_CR2_ADON;
}

void I2C_Init(void)
{
    I2C1->CR1 &= ~I2C_CR1_PE;
    I2C1->CR1 |= I2C_CR1_SWRST;
    I2C1->CR1 &= ~I2C_CR1_SWRST;
    
    I2C1->CR2 = 36; // 36MHz
    I2C1->CCR = 180; // 100kHz
    I2C1->TRISE = 37;
    I2C1->CR1 |= I2C_CR1_PE;
}

void USART_Init(void)
{
    WIFI_USART->BRR = 0x1D4C; // 115200 @36MHz
    WIFI_USART->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}

void Timer_Init(void)
{
    // 定时器2用于心率计算
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    TIM2->PSC = 36000 - 1; // 1kHz
    TIM2->ARR = 1000 - 1; // 1s
    TIM2->DIER |= TIM_DIER_UIE;
    TIM2->CR1 |= TIM_CR1_CEN;
    NVIC_EnableIRQ(TIM2_IRQn);
}

void OLED_Init(void)
{
    OLED_WriteCmd(0xAE); // 关闭显示
    OLED_WriteCmd(0x20); // 内存地址模式
    OLED_WriteCmd(0x10);
    OLED_WriteCmd(0xB0);
    OLED_WriteCmd(0xC8);
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0x10);
    OLED_WriteCmd(0x40);
    OLED_WriteCmd(0x81);
    OLED_WriteCmd(0xFF);
    OLED_WriteCmd(0xA1);
    OLED_WriteCmd(0xA6);
    OLED_WriteCmd(0xA8);
    OLED_WriteCmd(0x3F);
    OLED_WriteCmd(0xA4);
    OLED_WriteCmd(0xD3);
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0xD5);
    OLED_WriteCmd(0xF0);
    OLED_WriteCmd(0xD9);
    OLED_WriteCmd(0x22);
    OLED_WriteCmd(0xDA);
    OLED_WriteCmd(0x12);
    OLED_WriteCmd(0xDB);
    OLED_WriteCmd(0x20);
    OLED_WriteCmd(0x8D);
    OLED_WriteCmd(0x14);
    OLED_WriteCmd(0xAF); // 开启显示
    
    OLED_Clear();
}

void OLED_WriteCmd(uint8_t cmd)
{
    while(I2C1->SR2 & I2C_SR2_BUSY);
    I2C1->CR1 |= I2C_CR1_START;
    while(!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = OLED_ADDRESS;
    while(!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2;
    
    I2C1->DR = 0x00; // 命令模式
    while(!(I2C1->SR1 & I2C_SR1_TXE));
    
    I2C1->DR = cmd;
    while(!(I2C1->SR1 & I2C_SR1_BTF));
    
    I2C1->CR1 |= I2C_CR1_STOP;
}

void OLED_WriteData(uint8_t data)
{
    while(I2C1->SR2 & I2C_SR2_BUSY);
    I2C1->CR1 |= I2C_CR1_START;
    while(!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = OLED_ADDRESS;
    while(!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2;
    
    I2C1->DR = 0x40; // 数据模式
    while(!(I2C1->SR1 & I2C_SR1_TXE));
    
    I2C1->DR = data;
    while(!(I2C1->SR1 & I2C_SR1_BTF));
    
    I2C1->CR1 |= I2C_CR1_STOP;
}

void OLED_Clear(void)
{
    for(uint8_t page=0; page<8; page++)
    {
        OLED_WriteCmd(0xB0 + page);
        OLED_WriteCmd(0x00);
        OLED_WriteCmd(0x10);
        for(uint8_t col=0; col<128; col++)
        {
            OLED_WriteData(0x00);
        }
    }
}

void OLED_ShowString(uint8_t x, uint8_t y, char *str)
{
    OLED_WriteCmd(0xB0 + y);
    OLED_WriteCmd(((x & 0xF0) >> 4) | 0x10);
    OLED_WriteCmd(x & 0x0F);
    
    while(*str)
    {
        OLED_WriteData(*str++);
    }
}

void OLED_ShowWaveform(uint16_t value)
{
    static uint8_t x_pos = 0;
    uint8_t y_pos = (value >> 5); // 缩放到OLED高度
    
    OLED_WriteCmd(0xB0 + (y_pos >> 3));
    OLED_WriteCmd(((x_pos & 0xF0) >> 4) | 0x10);
    OLED_WriteCmd(x_pos & 0x0F);
    OLED_WriteData(1 << (y_pos & 0x07));
    
    x_pos++;
    if(x_pos >= 128) x_pos = 0;
}

void ECG_DataProcess(void)
{
    ADC1->CR2 |= ADC_CR2_SWSTART;
    while(!(ADC1->SR & ADC_SR_EOC));
    ecg_raw_value = ADC1->DR;
}

void HeartRate_Calculate(void)
{
    static uint16_t peak_count = 0;
    static uint16_t last_value = 0;
    
    // 简单的峰值检测算法
    if(ecg_raw_value > 2000 && last_value <= 2000)
    {
        peak_count++;
    }
    last_value = ecg_raw_value;
}

void WiFi_SendData(uint16_t ecg_value, uint16_t heart_rate_val)
{
    char buffer[50];
    sprintf(buffer, "ECG:%d,HR:%d\n", ecg_value, heart_rate_val);
    
    char *p = buffer;
    while(*p)
    {
        WIFI_USART->DR = *p++;
        while(!(WIFI_USART->SR & USART_SR_TXE));
    }
}

void Alarm_Trigger(uint8_t state)
{
    if(state)
    {
        BUZZER_GPIO->BSRR = BUZZER_PIN;
        LED_GPIO->BSRR = LED_PIN;
    }
    else
    {
        BUZZER_GPIO->BRR = BUZZER_PIN;
        LED_GPIO->BRR = LED_PIN;
    }
}

// 定时器中断服务函数
void TIM2_IRQHandler(void)
{
    if(TIM2->SR & TIM_SR_UIF)
    {
        TIM2->SR &= ~TIM_SR_UIF;
        
        // 每秒计算一次心率
        static uint16_t peak_count = 0;
        heart_rate = peak_count * 60;
        peak_count = 0;
    }
}

项目核心代码

#include "stm32f10x.h"

// 硬件模块初始化函数声明
void SystemClock_Config(void);
void GPIO_Config(void);
void ADC_Config(void);
void I2C_Config(void);
void USART_Config(void);
void TIM_Config(void);

// 外设驱动函数声明
void OLED_Init(void);
void OLED_ShowWaveform(uint16_t value);
void OLED_ShowHeartRate(uint16_t rate);
void ESP8266_Init(void);
void ESP8266_SendData(uint16_t ecg_data);
void Buzzer_Alarm(uint8_t state);
void LED_Alarm(uint8_t state);

// 心率计算相关变量
#define ECG_BUFFER_SIZE 200
uint16_t ecg_buffer[ECG_BUFFER_SIZE];
uint16_t buffer_index = 0;
uint16_t heart_rate = 0;
uint8_t alarm_state = 0;

// 心率异常阈值
#define HR_HIGH_THRESHOLD 120
#define HR_LOW_THRESHOLD 50

int main(void)
{
    // 系统初始化
    SystemClock_Config();
    GPIO_Config();
    ADC_Config();
    I2C_Config();
    USART_Config();
    TIM_Config();
    
    // 外设初始化
    OLED_Init();
    ESP8266_Init();
    
    // 关闭报警
    Buzzer_Alarm(0);
    LED_Alarm(0);
    
    // 启动定时器
    TIM3->CR1 |= TIM_CR1_CEN;
    
    while(1)
    {
        // 心率计算和显示
        if(buffer_index >= ECG_BUFFER_SIZE)
        {
            heart_rate = CalculateHeartRate(ecg_buffer, ECG_BUFFER_SIZE);
            OLED_ShowHeartRate(heart_rate);
            buffer_index = 0;
            
            // 心率异常检测
            if(heart_rate > HR_HIGH_THRESHOLD || heart_rate < HR_LOW_THRESHOLD)
            {
                alarm_state = 1;
                Buzzer_Alarm(1);
                LED_Alarm(1);
            }
            else
            {
                alarm_state = 0;
                Buzzer_Alarm(0);
                LED_Alarm(0);
            }
        }
        
        // 空闲时进入低功耗模式
        __WFI();
    }
}

// 系统时钟配置
void SystemClock_Config(void)
{
    // 启用HSE
    RCC->CR |= RCC_CR_HSEON;
    while(!(RCC->CR & RCC_CR_HSERDY));
    
    // 配置PLL
    RCC->CFGR |= RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9;
    
    // 启用PLL
    RCC->CR |= RCC_CR_PLLON;
    while(!(RCC->CR & RCC_CR_PLLRDY));
    
    // 切换系统时钟到PLL
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}

// GPIO配置
void GPIO_Config(void)
{
    // 启用GPIO时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
    
    // ADC输入引脚 (PA0)
    GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
    GPIOA->CRL |= GPIO_CRL_CNF0_1;  // 模拟输入
    
    // I2C引脚 (PB6-SCL, PB7-SDA)
    GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6);
    GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_1;  // 复用开漏输出
    GPIOB->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
    GPIOB->CRL |= GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_1;  // 复用开漏输出
    
    // USART引脚 (PA2-TX, PA3-RX)
    GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2);
    GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_1;  // 复用推挽输出
    GPIOA->CRL &= ~(GPIO_CRL_MODE3 | GPIO_CRL_CNF3);
    GPIOA->CRL |= GPIO_CRL_CNF3_0;  // 浮空输入
    
    // 报警引脚 (PC13-蜂鸣器, PC14-LED)
    GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
    GPIOC->CRH |= GPIO_CRH_MODE13_1;  // 输出模式,2MHz
    GPIOC->CRH &= ~(GPIO_CRH_MODE14 | GPIO_CRH_CNF14);
    GPIOC->CRH |= GPIO_CRH_MODE14_1;  // 输出模式,2MHz
}

// ADC配置
void ADC_Config(void)
{
    // 启用ADC1时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    
    // ADC校准
    ADC1->CR2 |= ADC_CR2_CAL;
    while(ADC1->CR2 & ADC_CR2_CAL);
    
    // 配置ADC
    ADC1->CR1 = 0;
    ADC1->CR2 = ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG;
    
    // 配置采样时间
    ADC1->SMPR2 |= ADC_SMPR2_SMP0;  // 239.5周期
    
    // 配置通道序列
    ADC1->SQR1 = 0;
    ADC1->SQR3 = 0;  // 通道0
    
    // 启用ADC
    ADC1->CR2 |= ADC_CR2_ADON;
}

// I2C配置
void I2C_Config(void)
{
    // 启用I2C1时钟
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    
    // 配置I2C
    I2C1->CR1 = 0;
    I2C1->CR2 = 36;  // 36MHz
    I2C1->CCR = 180; // 100kHz
    I2C1->TRISE = 37;
    
    // 启用I2C
    I2C1->CR1 |= I2C_CR1_PE;
}

// USART配置
void USART_Config(void)
{
    // 启用USART2时钟
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
    
    // 配置USART
    USART2->BRR = 0x1A0;  // 115200 @36MHz
    USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}

// 定时器配置
void TIM_Config(void)
{
    // 启用TIM3时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    
    // 配置TIM3 (5ms中断)
    TIM3->PSC = 36000 - 1;  // 1kHz
    TIM3->ARR = 5 - 1;      // 5ms
    TIM3->DIER |= TIM_DIER_UIE;
    
    // 配置NVIC
    NVIC->ISER[0] |= (1 << TIM3_IRQn);
    NVIC->IP[TIM3_IRQn] = 0x10;
}

// 定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    if(TIM3->SR & TIM_SR_UIF)
    {
        TIM3->SR &= ~TIM_SR_UIF;
        
        // 启动ADC转换
        ADC1->CR2 |= ADC_CR2_SWSTART;
        
        // 等待转换完成
        while(!(ADC1->SR & ADC_SR_EOC));
        
        // 读取ADC值
        uint16_t adc_value = ADC1->DR;
        
        // 存储到缓冲区
        if(buffer_index < ECG_BUFFER_SIZE)
        {
            ecg_buffer[buffer_index++] = adc_value;
        }
        
        // 显示波形
        OLED_ShowWaveform(adc_value);
        
        // 发送数据到Wi-Fi模块
        if(alarm_state)
        {
            ESP8266_SendData(adc_value);
        }
    }
}

// 心率计算函数
uint16_t CalculateHeartRate(uint16_t *data, uint16_t size)
{
    uint16_t peaks = 0;
    uint16_t threshold = 2048;  // ADC中间值
    
    // 简单的峰值检测算法
    for(uint16_t i = 1; i < size - 1; i++)
    {
        if(data[i] > data[i-1] && data[i] > data[i+1] && data[i] > threshold)
        {
            peaks++;
        }
    }
    
    // 计算心率 (假设采样率200Hz)
    return (peaks * 60 * 200) / size;
}

总结

本系统是一个基于STM32的便携式心电监护仪,旨在实现对用户心电信号的实时监测与分析。通过集成多种功能模块,该系统能够有效采集、处理和传输心电数据,为用户提供及时的健康反馈。

在功能方面,系统通过心电采集芯片实时获取心电信号,并将波形和心率数据直观显示在OLED屏幕上。当检测到心率异常时,系统会触发本地声光报警,提醒用户注意潜在风险。同时,借助Wi-Fi模块,心电数据可上传至手机APP,便于进一步分析和远程监控。

硬件方面,系统采用STM32F103C8T6作为主控芯片,结合AD8232心电采集模块、OLED显示屏、ESP8266通信模块以及蜂鸣器和LED报警组件。这些模块协同工作,确保了系统的可靠性、便携性和高效性,适用于日常健康监护场景。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。