基于STM32设计的人体健康检测仪

发布时间:2023-08-15  

一、项目介绍

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


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



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

image-20230618132149185

image-20230618132108207

二、项目设计思路

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 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 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 DC signals

     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)

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    体检测仪中测试和校正两者有什么不同;气体检测仪在使用一段时间后,容易受使用环境和仪器本身气体传感器的影响,从而导致测量出来的结果可能会发生较大的偏差。因此,为了保证测量结果的准确性,所以不管是使用何种气体传感器的气体检测仪......
    体检测仪中测试和校正的区别是什么;气体检测仪在使用一段时间后,容易受使用环境和仪器本身气体传感器的影响,从而导致测量出来的结果可能会发生较大的偏差。因此,为了保证测量结果的准确性,所以不管是使用何种气体传感器的气体检测仪......
    进行防护? 最安全有效的方法就是使用硫化氢气体检测仪进行气体监测。电化学硫化氢气体传感器是该检测仪的核心部件,对能否敏锐检测......
    体检测仪的保养方法及注意事项;现在各行各业都安装着气体检测仪,但是大家是否知道气体检测仪的保养工作该如何进行呢?气体检测仪在保养维护的时候需要注意什么呢?接下来给大家简单介绍下气体检测仪......
    有毒有害气体检测仪主要作用于哪些情况?;有毒有害气体检测仪、可燃气体检测仪是专用的安全卫生检测仪,用来检测化学品等危险作业场所或设备内部空气中可能存在的可燃或有毒气体含量超限报警的仪器。 有毒有害气体检测仪......
    体检测仪的使用寿命的长短取决于哪些因素;很多人在使用气体检测仪的时候,都有这样一个疑问:它究竟能用多久?很多用户以为气体检测仪可以一直使用,直到完全损坏无法使用。事实上,这样......
    一般分为以下三类: 1.便携式氢气检测仪 便携式氢气检测仪可连续检测工作环境中的氢气浓度。进口电化学传感器,灵敏度好,重复性好;嵌入式微控制技术,操作简单、功能齐全、可靠性高。 2.在线式氢气检测仪 在线式氢气检测仪由气体检测报警控制器和固定氢气检测仪......
    汉威科技荣获工信部制造业单项冠军,以新质生产力助力行业发展; 3月4日,河南省工信厅公示了入选工信部第八批制造业单项冠军企业名单,汉威科技集团股份有限公司(以下简称“汉威科技”)榜上有名,其凭借在气体检测仪......
    在对探头进行测量时应该特别注意。可燃气探头所能检测的气体主要针对易燃易爆气体。甲烷类、燃气类与工业类气体。 可燃气体传感器相对来说检测可燃范围较广、检测精度较高。有效的测试时长为一年。日常的校准日期为3个月至半年为准。便携式与固定式气体检测仪......
    需要检查气体类型。 气体检测器的关键部件是气体传感器。不同的气体传感器可以检测不同类型的气体。例如,燃烧气体检测器只能使用可燃气体进行检测VOC气体不合适。 2.根据所需功能。 如今,气体检测仪器有许多附加功能,如报......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>