基于STM32的便携式心电监护仪
项目开发背景
随着心血管疾病发病率的逐年上升,心电监护在日常健康管理中的重要性日益凸显。传统心电监测设备往往体积庞大、成本较高,且多限于医院环境使用,无法满足个人日常长期监测的需求。因此,开发一种便携、低成本且功能完善的心电监护仪,能够帮助用户实时了解心脏健康状况,并在异常时及时预警,具有重要的现实意义。
当前,市场上虽有部分便携心电设备,但常存在功能单一、数据传输不便或报警机制不完善等问题。基于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报警组件。这些模块协同工作,确保了系统的可靠性、便携性和高效性,适用于日常健康监护场景。
- 点赞
- 收藏
- 关注作者
评论(0)