嘉立创产业服务站群
发作品签到
专业版

基于ai8051温湿度计+心率血氧仪

工程标签

799
0
0
2

简介

基于ai8051设计了温湿度计+心率血氧仪。主控采用AI8051U-34K64-QFP48,0.91寸IIC接口屏显示环境的温湿度,0.96寸SPI接口屏显示max30102采集的心率和血样数据。

简介:基于ai8051设计了温湿度计+心率血氧仪。主控采用AI8051U-34K64-QFP48,0.91寸IIC接口屏显示环境的温湿度,0.96寸SPI接口屏显示max30102采集的心率和血样数据。
基于Ai8051U开源创意电子设计大赛

开源协议

TAPR Open Hardware License

创建时间:2024-10-24 01:16:40更新时间:2024-12-02 01:10:42

描述

新封面.png

B站演示视频
https://www.bilibili.com/video/BV1xSzpYjEQ8/?vd_source=24f1befd6441a33d7b240715cb07c7b5

前言

参加Ai8051U开源创意电子设计大赛,本来是打算来白嫖个优惠券而言。未曾想一下子入坑了。我只不过来象征性学习一下Ai8051啊。没有想到做这个血氧仪一下子陷进去了,这个心率和血氧算法太难搞了,不管是官方的,还是各个论坛的。能找到的都试了。心率算法,我依然觉得不满意。血氧数据还凑合吧。
算法路上依然艰难啊。
希望各位大佬多指点,提供一些好的算法来验证一下。
个人能力太有限了呀。

设计描述

硬件设计

主控选择

采用了STC最新研究的产品AI8051U-34K64-QFP48,性能比较强劲,个人觉得接近M3了。

具体如下:
image.png

本次使用主控的管脚并不多,原理图如下:

image.png

tpye-c供电输入+下载口

通过type-c接口供电,和程序下载。

micro USB供电+下载口

预留micro USB,也可以下载
image.png

image.png

LDO转3.3V电路

通过LDO转换,为了各个模块供电统一。

image.png

复位电路

预留了复位电路,方便程序下载时,复位后进入下载状态。

image.png

冷启动下载按键

冷启动下载按键。非常必要。
image.png

SPI显示屏幕接口

预留了多种SPI接口屏,本次使用的0.96寸的128X96,其它相同接口的屏幕可以使用。
这也是为了后续进一步学习使用,故而预留了更多的接口。
image.png

IIC显示屏幕接口

适配多数IIC接口的屏幕,本次使用的0.91寸单色屏。128X32.
image.png

温湿度传感器sht40

为了板子不浪费,预留了这个接口,不使用的时候,也可以用来当温湿度计使用。

image.png

心率血氧传感器max30102

IIC接口的传感器模块,淘宝购买。
本次的核心模块哦。
image.png

LED显示

方便调试,使用指示作用

image.png

按键

这里预留学习使用

image.png

其它电路

预留验证和学习使用,本次用不到。多打样的板子,可以用来验证多余的电路。
这是穷人的办法哦。

PCB设计

整体如下:

image.png

从3D上可以看出,这个设计是可以兼容多种接口的屏幕哦。
这也是为了后续学习和验证各个显示模块哦。

image.png

程序设计

废话不多说,直接上干货
这里只贴主程序代码
其它驱动详见附件工程代码

#include "Ai8051U.H"
#include 
#include 
#include "oled.h"
#include "pic.h"

#include "i2c.h"
#include "algorithm.h"
#include "max30102.h"
#include "i2c_oled.h"
#include "sht40.h"


#define  DELAY_TIME   2000
sbit LED  = P5^6;   //


#define MAX_BRIGHTNESS 255
#define BUFFER_LENTH 500

u32 aun_ir_buffer[BUFFER_LENTH]; // IR LED sensor data   
u32 aun_red_buffer[BUFFER_LENTH]; // Red LED sensor data
s32 n_sp02; // SPO2 value
s8 ch_spo2_valid; // indicator to show if the SP02 calculation is valid
s32 n_heart_rate; // heart rate value
s8 ch_hr_valid; // indicator to show if the heart rate calculation is valid

//=====================================================================
// 函数: void  delay_ms(unsigned int ms)
// 描述: 毫秒级延时函数。
// 参数: ms,要延时的ms数,自动适应主时钟.
//=====================================================================
//void  delay_ms(unsigned int ms)
//{ unsigned int i;
//  do{ i = MAIN_Fosc / 6000;
//      while(--i);
//    } while(--ms);
//}

void sys_init()
{
    WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度
		NOP1();
    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
}

/**** 主函数入口 ************************/
void main(void)
{
    //u8 i;
    float dat= -3.145;
	  u32 un_min, un_max, un_prev_data;
    int i;
    s32 n_brightness;
    float f_temp;
    u8 temp[6];
		char ss[32];
    sys_init();                         //系统初始化
    EA = 1;                             //开总中断
		OLED_Init();                        //OLED初始化
		OLED_DisplayTurn(0);
		OLED_LightSet(250);
	
		I2C_OLED_Init();
		I2C_OLED_DisplayTurn(1);
		//I2C_OLED_ShowString(0,0,	"Temperature:",8,1);//6*8 
		I2C_OLED_ShowString(0,0,	"ET:",8,1);//6*8 
		//I2C_OLED_ShowString(64,0,	"Humidity:",8,1);//6*8 
		I2C_OLED_ShowString(73,0,	"RH:",8,1);//6*8 
		I2C_OLED_Refresh();
	
		OLED_BuffClear();                                                       //清除全部缓存
		OLED_BuffShowString(0,0,"HeartRate:",0);      //显示数据
		OLED_BuffShowString(0,4,"BloodOxyg:",0);      //显示数据
		OLED_BuffShow(); 
		
		MAX_IIC_Init();
		max30102_init(); // max30102	
	  n_brightness = 0;
    un_min = 0x3FFFF;
    un_max = 0;

	   // 读取前500个样本,并确定信号范围
    for(i = 0; i < BUFFER_LENTH; i++)
    {
			// 等待中断引脚
        while(MAX30102_INT == 1);   
        max30102_FIFO_ReadBytes(REG_FIFO_DATA, temp); // 读取传感器数据,赋值到temp中
        aun_red_buffer[i] = (long)((long)((long)temp[0] & 0x03) << 16) | (long)temp[1] << 8 | (long)temp[2]; // 将值合并得到实际数字
        aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03) << 16) | (long)temp[4] << 8 | (long)temp[5]; // 将值合并得到实际数字
        if(un_min > aun_red_buffer[i])
            un_min = aun_red_buffer[i]; // 更新信号最小值
        if(un_max < aun_red_buffer[i])
            un_max = aun_red_buffer[i]; // 更新信号最大值
    }
    un_prev_data = aun_red_buffer[i];
    // 计算前500个样本后的心率和SpO2(样本的前5秒)
    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_LENTH, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
		while (1)
    {						
	        // 读取和计算max30102数据,总体用缓存的500组数据分析,实际每读取100组新数据分析一次
        un_min = 0x3FFFF;
        un_max = 0;
        // 将前100组样本转储到内存中(实际没有),并将后400组样本移到顶部,将100-500缓存数据移位到0-400
        for(i = 100; i < 500; i++)
        {
            aun_red_buffer[i - 100] = aun_red_buffer[i]; // 将100-500缓存数据移位到0-400
            aun_ir_buffer[i - 100] = aun_ir_buffer[i]; // 将100-500缓存数据移位到0-400
            // 更新信号的最小值和最大值
            if(un_min > aun_red_buffer[i]) // 寻找移位后0-400中的最小值
                un_min = aun_red_buffer[i];
            if(un_max < aun_red_buffer[i]) // 寻找移位后0-400中的最大值
                un_max = aun_red_buffer[i];
        }	
        // 在计算心率前取100组样本,取的数据放在400-500缓存数组中
        for(i = 400; i < 500; i++)
        {
            un_prev_data = aun_red_buffer[i - 1]; // 临时记录上一次读取数据
					// 等待中断引脚
            while(MAX30102_INT == 1);      
            max30102_FIFO_ReadBytes(REG_FIFO_DATA, temp); // 读取传感器数据,赋值到temp中
            aun_red_buffer[i] = (long)((long)((long)temp[0] & 0x03) << 16) | (long)temp[1] << 8 | (long)temp[2]; // 将值合并得到实际数字,数组400-500为新读取数据
            aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03) << 16) | (long)temp[4] << 8 | (long)temp[5]; // 将值合并得到实际数字,数组400-500为新读取数据
            if(aun_red_buffer[i] > un_prev_data)
            { // 用新获取的一个数值与上一个数值对比
                f_temp = aun_red_buffer[i] - un_prev_data;
                f_temp /= (un_max - un_min);
                f_temp *= MAX_BRIGHTNESS; // 公式(心率曲线)=(新数值-旧数值)/(最大值-最小值)*255
                n_brightness -= (int)f_temp;
                if(n_brightness < 0)
                    n_brightness = 0;
            }
            else
            {
                f_temp = un_prev_data - aun_red_buffer[i];
                f_temp /= (un_max - un_min);
                f_temp *= MAX_BRIGHTNESS; // 公式(心率曲线)=(旧数值-新数值)/(最大值-最小值)*255
                n_brightness += (int)f_temp;
                if(n_brightness > MAX_BRIGHTNESS)
                    n_brightness = MAX_BRIGHTNESS;
            }
        }
        maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_LENTH, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); // 传入500个心率和血氧数据计算传感器检测结论,反馈心率和血氧测试结果

        if((1 == ch_hr_valid) && (1 == ch_spo2_valid) && (n_heart_rate < 120) && (n_sp02 < 101))
        {
            //printf("HeartRate=%i, BloodOxyg=%i\r\n", n_heart_rate, n_sp02);
						OLED_BuffClear();                                                       //清除全部缓存
					
						OLED_BuffShowString(0,0,"HeartRate:",0);      //显示数据
						OLED_BuffShowNum(0,2,n_heart_rate,0);
						OLED_BuffShowString(0,4,"BloodOxyg:",0);      //显示数据
						OLED_BuffShowNum(0,6,n_sp02,0);    
						OLED_BuffShow();                                                        //将缓存写入显示屏显示
						//delay_ms(DELAY_TIME); 					
        }						
				i=SHT40_Cal();
				if(!i)
				{
					sprintf(ss,"%3.1f",SHT40_GetTEM());				
					I2C_OLED_ShowString(0,8,ss,24,1);//6*8 	
					I2C_OLED_ShowString(48,20,"C",8,1);//6*8
					
					sprintf(ss,"%3.1f%",SHT40_GetHUM());
					//I2C_OLED_ShowString(0,0,ss,8,1);//6*8 
					I2C_OLED_ShowString(73,8,ss,24,1);//6*8		
					I2C_OLED_ShowString(121,20,"%",8,1);//6*8
				}else{
					sprintf(ss,"%d",i);
					I2C_OLED_ShowString(0,8,ss,24,1);//6*8					
				}
				I2C_OLED_Refresh();			
						
			LED=!LED;
//			delay_ms(500);
			

    }
}


温湿度sht40输出验证

这个移植过程也花了不少时间,之前模块在其它主控下,ESP,梁山派、stm32等都很正常,但是在这个里面就是无法输出正确的湿度数据。
后来才发现IIC时序有差异,真的是坑死了我。
花了一周多的时间,这个教训太深刻了呀。

image.png

心率血氧输出验证

通过上位机接收,可以看到,数据中心率变化比较大。
目前还没有找到更好算法来验证。
血氧数据比较稳定。
算法优化路漫漫长啊。

image.png

整体调试验证

终于把程序集成在一起验证了,虽然坎坷,数据也不是最令人满意的。
但是这却是让我记忆深刻的,也是让我对这个小作品付出很大心血的。

image.png

距离好的产品,还有很大差距,一点一滴的去积累吧。

2024年11月30日21:46:23完成

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
新封面.png
8
2
温湿度计+心率血氧仪演示.mp4
8
3
心率采集2.jpg
9
4
Ai8051_温湿度计+心率血氧仪.zip
27
克隆工程
添加到专辑
0
0
分享
侵权投诉

评论

全部评论(1)
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航