【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
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}}