主页 > 互联网 > 内容页

基于STM32设计的人体健康检测仪 环球关注

2023-06-23 13:14:42 来源:电子发烧友网
一、项目介绍

当前文章介绍基于STM32设计的人体健康检测仪。设备采用STM32系列MCU作为主控芯片,配备血氧浓度传感器(使用MAX30102血氧浓度检测传感器)、OLED屏幕和电池供电等外设模块。设备可以广泛应用于医疗、健康等领域。可以帮助医生和病人更好地了解病情变化,提高治疗效果和生活质量。设备也可以用于健康管理、运动监测等场景,帮助用户了解自己的身体状况,保持健康的生活方式。

在项目中,使用了KEIL作为开发平台和工具,通过血氧模块采集人体的心跳和血氧浓度参数,并通过OLED屏幕显示现在的心跳和血氧浓度。同时,通过指标分析,提供采集到的数据与正常指标比对,分析被检测人员的健康状态。采集的数据可通过蓝牙或者WIFI传递给手机APP进行处理,方便用户随时了解自己的身体状况。


(资料图)

本设计采用STM32为主控芯片,搭配血氧浓度传感器和OLED屏幕,实现了人体健康数据的采集和展示,并对采集到的数据进行分析,判断被检测人员的健康状态。同时,设计使用蓝牙或WiFi将采集到的数据传递给手机APP进行处理。

二、项目设计思路2.1 硬件设计

(1)主控芯片:STM32系列MCU,负责驱动其他外设模块;

(2)血氧浓度传感器:使用MAX30102血氧浓度检测传感器,用于采集人体的心跳和血氧浓度参数;

(3)OLED屏:用于显示现在的心跳和血氧浓度;

2.2 软件设计

(1) 通过血氧模块采集人体的心跳和血氧浓度参数;

(2) 通过OLED屏显示现在的心跳和血氧浓度;

(3) 对采集到的数据进行指标分析,将采集到的数据与正常指标比对,分析被检测人员的健康状态;

(4) 采集的数据可通过蓝牙或WiFi传递给手机APP进行处理。

2.3 技术实现

(1)设计采用AD8232心电图(ECG)模块和MAX30102血氧模块采集心跳和血氧浓度参数,并通过I2C接口连接主控芯片STM32。

(2)OLED屏使用I2C接口与主控芯片STM32连接。

(3)采集到的数据通过算法进行指标分析,将采集到的数据与正常指标比对,判断被检测人员的健康状态。

(4)设备通过蓝牙或WiFi将采集到的数据传递给手机APP进行处理。

三、代码设计3.1 MAX30102血氧模块代码

I2C协议代码:

#define MAX30102_I2C_ADDR 0xAE ​ void MAX30102_I2C_Init(void) {     GPIO_InitTypeDef  GPIO_InitStructure;     I2C_InitTypeDef   I2C_InitStructure; ​     /* Enable GPIOB clock */     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     /* Enable I2C1 and I2C2 clock */     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_I2C2, ENABLE); ​     // Configure I2C SCL and SDA pins     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // Open-drain output     GPIO_Init(GPIOB, &GPIO_InitStructure); ​     // Configure I2C parametersI2C_InitStructure.I2C_Mode = I2C_Mode_I2C;     I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;     I2C_InitStructure.I2C_OwnAddress1 = 0x00;     I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;     I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;     I2C_InitStructure.I2C_ClockSpeed = 100000; // 100KHz     I2C_Init(I2C1, &I2C_InitStructure); ​     // Enable I2C     I2C_Cmd(I2C1, ENABLE); } ​ void MAX30102_I2C_WriteReg(uint8_t reg, uint8_t value) {     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, MAX30102_I2C_ADDR, I2C_Direction_Transmitter);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); ​     I2C_SendData(I2C1, reg);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_SendData(I2C1, value);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_GenerateSTOP(I2C1, ENABLE); } ​ uint8_t MAX30102_I2C_ReadReg(uint8_t reg) {     uint8_t value; ​     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, MAX30102_I2C_ADDR, I2C_Direction_Transmitter);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); ​     I2C_SendData(I2C1, reg);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, MAX30102_I2C_ADDR, I2C_Direction_Receiver);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); ​     I2C_AcknowledgeConfig(I2C1, DISABLE);     value = I2C_ReceiveData(I2C1); ​     I2C_GenerateSTOP(I2C1, ENABLE); ​     return value; } ​ void MAX30102_I2C_ReadArray(uint8_t reg, uint8_t* data, uint8_t len) {     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, MAX30102_I2C_ADDR, I2C_Direction_Transmitter);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); ​     I2C_SendData(I2C1, reg);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, MAX30102_I2C_ADDR, I2C_Direction_Receiver);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); ​     while(len > 1)     {         I2C_AcknowledgeConfig(I2C1, ENABLE);         while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));         *data++ = I2C_ReceiveData(I2C1);         len--;     } ​     I2C_AcknowledgeConfig(I2C1, DISABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));     *data++ = I2C_ReceiveData(I2C1); ​     I2C_GenerateSTOP(I2C1, ENABLE); }

MAX30102的初始化函数和数据获取函数:

void MAX30102_Init(void) {     MAX30102_I2C_Init(); ​     // Reset the device     MAX30102_I2C_WriteReg(0x09, 0x40);     HAL_Delay(100);     MAX30102_I2C_WriteReg(0x09, 0x00); ​     // Set FIFO average to 4 samples     MAX30102_I2C_WriteReg(0x08, 0x03); ​     // Set LED pulse amplitude     MAX30102_I2C_WriteReg(0x0C, 0x1F);     MAX30102_I2C_WriteReg(0x0D, 0x1F); ​     // Set sample rate to 100Hz     MAX30102_I2C_WriteReg(0x0F, 0x04); ​     // Enable the red LED only     MAX30102_I2C_WriteReg(0x11, 0x02); ​     // Read the temperature value to start a reading     MAX30102_I2C_ReadReg(0x1F); } ​ uint32_t MAX30102_GetHeartRate(void) {     uint8_t buffer[MAX30102_FIFO_DEPTH*4];     MAX30102_Data sensor_data = {0};     uint16_t ir_value;     uint16_t red_value;     uint8_t byte_count, fifo_overflow; ​     // Check if any data is available in FIFO     byte_count = MAX30102_I2C_ReadReg(0x06) - MAX30102_I2C_ReadReg(0x04);     if(byte_count > 0)     {         fifo_overflow = MAX30102_I2C_ReadReg(0x09) & 0x80; ​         // Read the data from FIFO         MAX30102_I2C_ReadArray(0x07, buffer, byte_count); ​         // Parse the data         for(int i=0; i< byte_count; i+=4)         {             ir_value = ((uint16_t)buffer[i] < < 8) | buffer[i+1];             red_value = ((uint16_t)buffer[i+2] < < 8) | buffer[i+3]; ​             // Update the sensor data             MAX30102_UpdateData(&sensor_data, ir_value, red_value);         } ​         if(!fifo_overflow && MAX30102_CheckForBeat(sensor_data.IR_AC_Signal_Current))         {             return MAX30102_HeartRate(sensor_data.IR_AC_Signal_Previous, 16);         }     } ​     return 0; }

数据处理函数:

void MAX30102_UpdateData(MAX30102_Data* data, uint16_t ir_value, uint16_t red_value) {     int32_t ir_val_diff = ir_value - data- >IR_AC_Signal_Current;     int32_t red_val_diff = red_value - data- >Red_AC_Signal_Current; ​     // Update IR AC and DCsignals     data- >IR_AC_Signal_Current = (ir_val_diff + (7 * data- >IR_AC_Signal_Previous)) / 8;     data- >IR_DC_Signal_Current = (ir_value + data- >IR_AC_Signal_Current + (2 * data- >IR_DC_Signal_Current)) / 4;     data- >IR_AC_Signal_Previous = data- >IR_AC_Signal_Current; ​     // Update Red AC and DC signals     data- >Red_AC_Signal_Current = (red_val_diff + (7 * data- >Red_AC_Signal_Previous)) / 8;     data- >Red_DC_Signal_Current = (red_value + data- >Red_AC_Signal_Current + (2 * data- >Red_DC_Signal_Current)) / 4;     data- >Red_AC_Signal_Previous = data- >Red_AC_Signal_Current; ​     // Update IR and Red AC signal peak-to-peak values     if(data- >IR_AC_Signal_Current > data- >IR_AC_Max)         data- >IR_AC_Max = data- >IR_AC_Signal_Current;     else if(data- >IR_AC_Signal_Current < data- >IR_AC_Min)         data- >IR_AC_Min = data- >IR_AC_Signal_Current; ​     if(data- >Red_AC_Signal_Current > data- >Red_AC_Max)         data- >Red_AC_Max = data- >Red_AC_Signal_Current;     else if(data- >Red_AC_Signal_Current < data- >Red_AC_Min)         data- >Red_AC_Min = data- >Red_AC_Signal_Current; } ​ uint8_t MAX30102_CheckForBeat(int32_t ir_val) {     staticuint8_t beat_detection_enabled = 1;     static uint32_t last_beat_time = 0;     static int32_t threshold = 0x7FFFFF; ​     uint32_t delta_time;     int32_t beat_amplitude; ​     if(beat_detection_enabled)     {         // Increment the beat counter         MAX30102_beat_counter++; ​         // Calculate the threshold value         threshold += (ir_val - threshold) / 8; ​         // Check if a beat has occurred         if(ir_val > threshold && MAX30102_beat_counter > 20)         {             delta_time = micros() - last_beat_time;             last_beat_time = micros();             beat_amplitude = ir_val - threshold;             if(delta_time < 1000 || delta_time > 2000 || beat_amplitude < 20 ||             beat_amplitude > 1000) { return 0; }                    // Reset the beat counter and set the threshold value         MAX30102_beat_counter = 0;         threshold = ir_val; ​         return 1;     } } ​ return 0; } ​ uint32_t MAX30102_HeartRate(int32_t ir_val, uint8_t samples) { int32_t ir_val_sum = 0; // Calculate the sum of IR values for(int i=0; i< samples; i++) {     ir_val_sum += MAX30102_IR_Sample_Buffer[i]; } ​ // Calculate the average IR value ir_val_sum /= samples; ​ // Calculate the heart rate return (uint32_t)(60 * MAX30102_SAMPLING_FREQUENCY / (ir_val - ir_val_sum)); }
3.2 OLED显示屏驱动代码

I2C协议代码:

#define SSD1306_I2C_ADDR 0x78 ​ void SSD1306_I2C_Init(void) {     GPIO_InitTypeDef  GPIO_InitStructure;     I2C_InitTypeDef   I2C_InitStructure; ​     /* Enable GPIOB clock */     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     /* Enable I2C1 and I2C2 clock */     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_I2C2, ENABLE); ​     // Configure I2C SCL and SDA pins     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // Open-drain output     GPIO_Init(GPIOB, &GPIO_InitStructure); ​     // Configure I2C parameters     I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;     I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;     I2C_InitStructure.I2C_OwnAddress1 = 0x00;     I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;     I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;     I2C_InitStructure.I2C_ClockSpeed = 100000; // 100KHz     I2C_Init(I2C1, &I2C_InitStructure); ​     // Enable I2C     I2C_Cmd(I2C1, ENABLE); } ​ void SSD1306_I2C_WriteReg(uint8_t reg, uint8_t value) {     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, SSD1306_I2C_ADDR, I2C_Direction_Transmitter);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); ​     I2C_SendData(I2C1, 0x00);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_SendData(I2C1, reg);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_SendData(I2C1, value);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); ​     I2C_GenerateSTOP(I2C1, ENABLE); } ​ void SSD1306_I2C_WriteArray(uint8_t* data, uint16_t len) {     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); ​     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); ​     I2C_Send7bitAddress(I2C1, SSD1306_I2C_ADDR, I2C_Direction_Transmitter);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); ​     while(len--)     {         I2C_SendData(I2C1, *data++);         while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));     } ​     I2C_GenerateSTOP(I2C1, ENABLE); }

SSD1306的初始化函数和数据更新函数:

#define SSD1306_WIDTH 128 #define SSD1306_HEIGHT 64 #define SSD1306_BUFFER_SIZE (SSD1306_WIDTH*SSD1306_HEIGHT/8) ​ uint8_t SSD1306_Buffer[SSD1306_BUFFER_SIZE]; ​ void SSD1306_Init(void) {     SSD1306_I2C_Init(); ​     // Turn display off     SSD1306_DisplayOff(); ​     // Set the clock to a high value for faster data transfer     SSD1306_I2C_WriteReg(0x0F, 0x80); ​     // Set multiplex ratio to default value (63)     SSD1306_I2C_WriteReg(0xA8, 0x3F); ​     // Set the display offset to 0     SSD1306_I2C_WriteReg(0xD3, 0x00); ​     // Display start line is 0     SSD1306_I2C_WriteReg(0x40, 0x00); ​     // Set segment remap to inverted     SSD1306_I2C_WriteReg(0xA1, 0xC0); ​     // Set COM output scandirection to inverted     SSD1306_I2C_WriteReg(0xC8, 0xC0); ​     // Disable display offset shift     SSD1306_I2C_WriteReg(0xD7, 0x9F); ​     // Set display clock divide ratio/oscillator frequency to default value (8/0xF0)     SSD1306_I2C_WriteReg(0xD5, 0xF0); ​     // Enable charge pump regulator     SSD1306_I2C_WriteReg(0x8D, 0x14); ​     // Set memory addressing mode     // Set the display to normal mode (not inverted) SSD1306_I2C_WriteReg(0xA6, 0xA6); ​ // Set the contrast to a default value of 127 SSD1306_I2C_WriteReg(0x81, 0x7F); ​ // Turn the display back on SSD1306_DisplayOn(); ​ // Clear the display buffer SSD1306_ClearBuffer(); ​ // Update the display with the cleared buffer SSD1306_UpdateDisplay(); } ​ void SSD1306_UpdateDisplay(void) { uint8_t column, page; }for(page=0; page< 8; page++) {     SSD1306_I2C_WriteReg(0xB0+page, 0x00);     SSD1306_I2C_WriteReg(0x10, 0x00);     SSD1306_I2C_WriteReg(0x00, 0x00); ​     for(column=0; column< SSD1306_WIDTH; column++)     {         SSD1306_I2C_WriteArray(&SSD1306_Buffer[column + page*SSD1306_WIDTH], 1);     } } } void SSD1306_ClearBuffer(void) { memset(SSD1306_Buffer, 0x00, sizeof(SSD1306_Buffer)); } ​ void SSD1306_SetPixel(uint8_t x, uint8_t y, uint8_t color) { if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) { return; } }if(color) {     SSD1306_Buffer[x + (y/8)*SSD1306_WIDTH] |= (1 < < (y%8)); } else {     SSD1306_Buffer[x + (y/8)*SSD1306_WIDTH] &= ~(1 < < (y%8)); } }
四、总结

本设计采用STM32为主控芯片,配合血氧浓度传感器和OLED屏幕,实现了人体健康数据的采集和展示,并通过算法对采集到的数据进行分析,判断被检测人员的健康状态。同时,设计使用蓝牙或WiFi将采集到的数据传递给手机APP进行处理。设计基本满足了人体健康检测仪的技术要求和环境要求。

标签:

上一篇:世界观察:美国深海潜水器发生内爆 5名乘员死亡
下一篇:最后一页