【MAX30100】血氧心率检测仪DIY开源【STM32】
kmakise 2021-8-8原创 仪器仪表
关键词
血氧饱和度心率

Github 项目链接

attachment icon MAX30100.zip 27.44MB ZIP 13次下载

发一个之前做的血氧心率检测的小东西,使用的是美信的MAX30100和30102

芯片框图:

upload_downloader_1628401817272_44436819.png

7VU1A@NAMVHFC3GUXN0DWB1.png

携带氧气的红血球能吸收较多红外光(850-1000nm),未携带氧气的红血球则是吸收较多的红光(600-750nm),通过这个特性就可以计算血液的血氧饱和度。

大概效果如下图:

2.jpg

1.jpg


数据处理方式:

首先配置传感器工作在FIFO模式下然后周期性读取FIFO,通过1024点的FFT变换得到频域数据,然后选择频带内的最高幅值为心率,通过对比两个幅值的幅度计算出血氧饱和度。通过平均其他频点的差值来标定两个波长数据。

数据波形图:

血氧1.jpg

QQ截图20210729191204.jpg

部分算法代码:

struct compx FFTBUF1[FFT_N+16];
struct compx FFTBUF2[FFT_N+16];
uint16_t g_fft_index = 0;         	 
BloodData g_blooddata = {0};				


void test(float data1,float data2)
{
		static uint8_t str[50];
		sprintf((char *)str,"%f,%f\r\n",data1,data2);
		HAL_UART_Transmit_DMA(&huart1,str,sizeof(str));
}

//血液检测信息更新
void blood_data_update(void)
{	
	static DC_FilterData dc1 = {.w = 0,.init = 0,.a = 0.8};
	static DC_FilterData dc2 = {.w = 0,.init = 0,.a = 0.8};
	
	static float data1buf[20];
	static uint8_t data1cur = 0;
	static float data2buf[20];
	static uint8_t data2cur = 0;
	
	uint16_t temp_num=0;
	uint16_t fifo_word_buff[1][2];

	temp_num = max30100_Bus_Read(INTERRUPT_REG);
	
	if (INTERRUPT_REG_A_FULL&temp_num)
	{
		max30100_FIFO_Read(0x05,fifo_word_buff,1); //read the hr and spo2 data form fifo in reg=0x05
		
		float data1 = dc_filter(fifo_word_buff[0][0],&dc1)+100.0;
		float data2 = dc_filter(fifo_word_buff[0][1],&dc2)+100.0;
		
		data1buf[data1cur] = data1;
		data2buf[data2cur] = data2;
		
		data1 = 0;
		data2 = 0;
		
		for(int i = 0;i < 20;i++)
		{
			data1 += data1buf[i];
			data2 += data2buf[i];
		}

		data1 /= 20;
		data2 /= 20;
		
		data1cur = (data1cur < 19) ? data1cur + 1 : 0;
		data2cur = (data2cur < 19) ? data2cur + 1 : 0;
		
//		fifo_word_buff[0][0] = data1;
//		fifo_word_buff[0][1] = data2;
		
//		test(data1,data2);
		
		g_blooddata.hb = data1 + 50;
		g_blooddata.hbo2 = data2 + 50;
		
		//将数据写入fft输入并清除输出
		for(int i = 0;i < 1;i++)
		{
			if(g_fft_index < FFT_N)
			{
				FFTBUF1[g_fft_index].real = fifo_word_buff[i][0];
				FFTBUF1[g_fft_index].imag= 0;
				FFTBUF2[g_fft_index].real = fifo_word_buff[i][1];
				FFTBUF2[g_fft_index].imag= 0;
				g_fft_index++;
			}
		}
		
		//信息更新标志位
		g_blooddata.update++;
		
	}
}
//血液信息转换
void blood_data_translate(void)
{	
	//缓冲区写入结束
	if(g_fft_index>=FFT_N)
	{
		//快速傅里叶变换
		FFT(FFTBUF1);
		FFT(FFTBUF2);
		
		//解平方
		for(int i = 0;i < FFT_N;i++) 
		{
			FFTBUF1[i].real = sqrtf(FFTBUF1[i].real * FFTBUF1[i].real + FFTBUF1[i].imag * FFTBUF1[i].imag);
			FFTBUF2[i].real = sqrtf(FFTBUF2[i].real * FFTBUF2[i].real + FFTBUF2[i].imag * FFTBUF2[i].imag);
		}
		
		//读取峰值点 10-100带通 频率范围30-292次/分钟
		uint16_t s1_max_index = find_max_num_index(FFTBUF1, 100);
		uint16_t s2_max_index = find_max_num_index(FFTBUF2, 100);
		
		//检查HbO2和Hb的变化频率是否一致
		if(s1_max_index == s2_max_index)
		{
			//心率计算
			uint16_t Heart_Rate = 60 * SAMPLES_PER_SECOND * 
														s2_max_index / FFT_N;
			
			g_blooddata.heart = Heart_Rate;
			
			//血氧含量计算
			float sp02_num = (FFTBUF1[s1_max_index].real * FFTBUF1[0].real)
											/(FFTBUF2[s1_max_index].real * FFTBUF2[0].real);
			
			sp02_num = sp02_num * SAMPLES_PER_SECOND + CORRECTED_VALUE;
			
			g_blooddata.SpO2 = sp02_num;
			
			//状态正常
			g_blooddata.state = BLD_NORMAL;
			

//			for(int i = 0;i < FFT_N;i++)
//			{
//				static uint8_t str[50];
//				sprintf((char *)str,"%f,%f\r\n\0",FFTBUF1[i].real,FFTBUF2[i].real);
//				HAL_UART_Transmit(&huart1,str,sizeof(str),20);
//			}
//			static uint8_t str[50];
//			sprintf((char *)str,"H:%d,S:%f\r\n",g_blooddata.heart,g_blooddata.SpO2 );
//			HAL_UART_Transmit(&huart1,str,sizeof(str),20);
			
		}
		else //数据发生异常
		{
			g_blooddata.heart = 0;
			g_blooddata.SpO2 	= 0;
			g_blooddata.state = BLD_ERROR;
		}
		
		g_fft_index = 0;
	}
}




来自:仪器与装备 / 仪器仪表
 
4
虎哥
2个月15天前
1楼

代码挺漂亮

回复
评论
1
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
zRed
2个月14天前
2楼
引用虎哥发表于1楼的内容
代码挺漂亮

sticker 猫子虎哥,突然回复这么多贴

回复
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
kmakise作者
2个月14天前
3楼
引用虎哥发表于1楼的内容
代码挺漂亮

感谢大佬夸奖

回复
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
kmakise
进士 机友 笔友
文章
3
回复
10
学术分
0
2021/08/07注册,2 个月前活动

bilibili:XXXXXXXXXXXXXXXXXXXXXXXXXX/22908638 github:XXXXXXXXXXXXXXXXXX/kmakise

%7B%22isDisplay%22%3Atrue%7D
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
插入资源
全部
图片
视频
音频
附件
全部
未使用
已使用
正在上传
空空如也~
上传中..{{f.progress}}%
处理中..
上传失败,点击重试
等待中...
{{f.name}}
空空如也~
(视频){{r.oname}}
{{selectedResourcesId.indexOf(r.rid) + 1}}
处理中..
处理失败
插入表情
我的表情
共享表情
Emoji
上传
注意事项
最大尺寸100px,超过会被压缩。为保证效果,建议上传前自行处理。
建议上传自己DIY的表情,严禁上传侵权内容。
点击重试等待上传{{s.progress}}%处理中...已上传
空空如也~
草稿箱
加载中...
此处只插入正文,如果要使用草稿中的其余内容,请点击继续创作。
{{fromNow(d.toc)}}
{{getDraftInfo(d)}}
标题:{{d.t}}
内容:{{d.c}}
继续创作
删除插入插入
{{forum.displayName}}
{{forum.countThreads}}
篇文章,
{{forum.countPosts}}
条回复
{{forum.description || "暂无简介"}}
ID: {{user.uid}}
学术分隐藏
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

支持的图片格式:jpg, jpeg, png
插入公式
分享回复:{{shareId}}
加载中...
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
加入关注取消关注
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
建议修改
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

下载资料
{{fileName}}
大小:{{size}}
下载当前附件将花费 {{costMessage}}
{{description}}
你当前剩余 {{holdMessage}}
{{fileName}}
大小:{{size}}
当前附件免费。
你已购买过此附件,下载当前附件不需要花费积分。
加载中...
{{errorInfo}}
附件已丢失
当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}