基于STM32设计的人体健康检测仪 环球关注
当前文章介绍基于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进行处理。设计基本满足了人体健康检测仪的技术要求和环境要求。
标签: