MAX32630FTHR板的学习(三):MAX30102的初步使用及心率、血氧的测量原理
本帖最后由 anning865 于 2017-8-6 17:30 编辑在上一篇帖子中,使用MAX32630FTHR板驱动了心率脉搏传感器Pulse sensor,得到了心率数值和脉搏的图像。这篇帖子介绍并研究一款美信公司生产的光学传感器MAX30102。
美信公司的MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。MAX30102采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指、耳垂和手腕等处。标准的I2C兼容的通信接口可以将采集到的数值传输给Arduino、KL25Z等单片机进行心率和血氧计算。此外,该芯片还可通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。正因为其优异的性能,该芯片被大量应用在了三星Galaxy S7手机。与前代产品MAX30100相比(MAX30100目前已经停产淘汰),MAX30102集成了玻璃盖可以有效排除外界和内部光干扰,拥有最优可靠的性能。
与Pulse sensor这种传感器相比,MAX30102的有点在于:
1.集成度高:MAX30102将两个发光LED、光电检测二极管、ADC、环境光抑制电路和光学机械外壳都集成在了一起,形成了一个完整模块。这样的优势就是体积小、能耗小、外界干扰小,易于集成系统。
2.数字输出:MAX30102本身自带18位高精度ADC,使用I2C接口与外接MCU通信。而且自身还有FIFO,可以减轻MCU负担,降低功耗。
3.功能丰富:MAX30102集成了LED驱动电路,可以根据不同情况调节LED电流,采样率也可以根据不同应用进行选择。此外还集成了片上温度传感器,可以随时监测片上温度(对血氧饱和度计算有用)。
正是有以上一些优点,现在的模拟集成前端(AFE)都走向了集成化、小型化、数字化的方向,模拟电路的门槛越来越低了。
首先看一下MAX30102的结构框图:
图1、MAX30102的结构框图
MAX30102的发光部分包括两个LED,一个是红光LED(660nm),另一个是红外光LED(880nm),这个是测量血氧饱和度SPO2最常见的配置。接收部分是一个对可见光和红外光都敏感的光电二极管,其接收的光强度信号转换为电流信号,经过环境光消除电路后,最后被自带的18位ADC进行采样转化,至此模拟部分完成。AD转化后的数字经过数字滤波后储存在数据寄存器中,最后可通过I2C总线被外接MCU读取。在硬件上,LED的电源和其他部分的电源不是同一个,因为LED为了保证足够的出射光强,需要瞬间大电流(最大50ma),这就要求LED的正向电压足够大(要求3.1V以上)。而其余的AD转换和I2C总线部分,为了实现低功耗要求电压足够小(要求1.8V),所以传感器需要两路独立的电源。此外,由于LED电源会产生瞬间大电流,所以电源引脚附近要加一个大电容减轻对电源电压的影响。
图2、MAX30102引脚图
传统的脉搏测量方法主要有三种:一是从心电信号中提取;二是从测量血压时压力传感器测到的波动来计算脉率;三是光电容积法。前两种方法提取信号都会限制病人的活动,如果长时间使用会增加病人生理和心理上的不舒适感。而光电容积法脉搏测量作为监护测量中最普遍的方法之一,其具有方法简单、佩戴方便、可靠性高等特点。
光电容积法的基本原理是利用人体组织在血管搏动时造成透光率不同来进行脉搏和血氧饱和度测量的。其使用的传感器由光源和光电变换器两部分组成,通过绑带或夹子固定在病人的手指、手腕或耳垂上。光源一般采用对动脉血中氧合血红蛋白(HbO2)和去氧血红蛋白(Hb)有选择性的特定波长的发光二极管(一般选用660nm附近的红光和900nm附近的红外光)。当光束透过人体外周血管,由于动脉搏动充血容积变化导致这束光的透光率发生改变,此时由光电变换器接收经人体组织反射的光线,转变为电信号并将其放大和输出。由于脉搏是随心脏的搏动而周期性变化的信号,动脉血管容积也呈现周期性变化,因此光电变换器的电信号变化周期就是脉搏率。同时根据血氧饱和度的定义,其表示为:
图3、血氧饱和度定义式
图4、不同波长下氧合血红蛋白(HbO2)和去氧血红蛋白(Hb)的吸收
MAX30102本身集成了完整的发光LED及驱动部分,光感应和AD转换部分,环境光干扰消除及数字滤波部分,只将数字接口留给用户,极大地减轻了用户的设计负担。用户只需要使用单片机通过硬件I2C或者模拟I2C接口来读取MAX30102本身的FIFO,就可以得到转换后的光强度数值,通过编写相应算法就可以得到心率值和血氧饱和度。
本来为了方便验证MAX30102和MAX32630FTHR板配合使用,本人从淘宝上买了一个现成的MAX30102模块。但是让人意想不到的是,这个模块本身设计存在很大问题,它的I2C总线上拉电压被强制固定在1.8V上了,这导致了该模块与IO口为3.3v,5v电平的单片机无法正常通信!真的是脑残的设计啊!为了找这个原因,浪费了很多时间,后来在GITHUB上看到有外国人抱怨,我才明白!最后只能飞线解决。
图5、X宝上某有问题的模块设计
图6、问题模块及飞线解决方法
这次简单验证一下MAX32630FTHR板可以通过I2C总线对MAX30102进行通信,所以在MBED上写了读取MAX30102片上温度的程序,并通过串口打印输出温度结果。具体程序如下:#include "mbed.h"
#include "max32630fthr.h"
#define false 0
#define true 1
MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);
Serial pc(USBTX, USBRX);//USB TO SERIAL
DigitalIn INT(P3_2);//pin P3.2 connects to the interrupt output pin of the MAX30102
I2C i2cm1(P3_4, P3_5);
#define I2C_WRITE_ADDR 0xAE
#define I2C_READ_ADDR 0xAF
//register addresses
#define REG_INTR_STATUS_1 0x00
#define REG_INTR_STATUS_2 0x01
#define REG_INTR_ENABLE_1 0x02
#define REG_INTR_ENABLE_2 0x03
#define REG_FIFO_WR_PTR 0x04
#define REG_OVF_COUNTER 0x05
#define REG_FIFO_RD_PTR 0x06
#define REG_FIFO_DATA 0x07
#define REG_FIFO_CONFIG 0x08
#define REG_MODE_CONFIG 0x09
#define REG_SPO2_CONFIG 0x0A
#define REG_LED1_PA 0x0C
#define REG_LED2_PA 0x0D
#define REG_PILOT_PA 0x10
#define REG_MULTI_LED_CTRL1 0x11
#define REG_MULTI_LED_CTRL2 0x12
#define REG_TEMP_INTR 0x1F
#define REG_TEMP_FRAC 0x20
#define REG_TEMP_CONFIG 0x21
#define REG_PROX_INT_THRESH 0x30
#define REG_REV_ID 0xFE
#define REG_PART_ID 0xFF
bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
/**
* \brief Write a value to a MAX30102 register
* \par Details
* This function writes a value to a MAX30102 register
*
* \param uch_addr - register address
* \param uch_data - register data
*
* \retval true on success
*/
{
char ach_i2c_data;
ach_i2c_data=uch_addr;
ach_i2c_data=uch_data;
if(i2cm1.write(I2C_WRITE_ADDR, ach_i2c_data, 2, false)==0)
return true;
else
return false;
}
bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
/**
* \brief Read a MAX30102 register
* \par Details
* This function reads a MAX30102 register
*
* \param uch_addr - register address
* \param puch_data - pointer that stores the register data
*
* \retval true on success
*/
{
char ch_i2c_data;
ch_i2c_data=uch_addr;
if(i2cm1.write(I2C_WRITE_ADDR, &ch_i2c_data, 1, true)!=0)
return false;
if(i2cm1.read(I2C_READ_ADDR, &ch_i2c_data, 1, false)==0)
{
*puch_data=(uint8_t) ch_i2c_data;
return true;
}
else
return false;
}
bool maxim_max30102_reset()
/**
* \brief Reset the MAX30102
* \par Details
* This function resets the MAX30102
*
* \param None
*
* \retval true on success
*/
{
if(!maxim_max30102_write_reg(REG_MODE_CONFIG,0x40))
return false;
else
return true;
}
bool maxim_max30102_init()
/**
* \brief Initialize the MAX30102
* \par Details
* This function initializes the MAX30102
*
* \param None
*
* \retval true on success
*/
{
if(!maxim_max30102_write_reg(REG_INTR_ENABLE_1,0xc0)) // INTR setting
return false;
if(!maxim_max30102_write_reg(REG_INTR_ENABLE_2,0x02))
return false;
if(!maxim_max30102_write_reg(REG_FIFO_WR_PTR,0x00))//FIFO_WR_PTR
return false;
if(!maxim_max30102_write_reg(REG_OVF_COUNTER,0x00))//OVF_COUNTER
return false;
if(!maxim_max30102_write_reg(REG_FIFO_RD_PTR,0x00))//FIFO_RD_PTR
return false;
if(!maxim_max30102_write_reg(REG_FIFO_CONFIG,0x0f))//sample avg = 1, fifo rollover=false, fifo almost full = 17
return false;
if(!maxim_max30102_write_reg(REG_MODE_CONFIG,0x03)) //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
return false;
if(!maxim_max30102_write_reg(REG_SPO2_CONFIG,0x27))// SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
return false;
if(!maxim_max30102_write_reg(REG_LED1_PA,0x24)) //Choose value for ~ 7mA for LED1
return false;
if(!maxim_max30102_write_reg(REG_LED2_PA,0x24)) // Choose value for ~ 7mA for LED2
return false;
if(!maxim_max30102_write_reg(REG_PILOT_PA,0x7f)) // Choose value for ~ 25mA for Pilot LED
return false;
if(!maxim_max30102_write_reg(REG_TEMP_CONFIG,0x01)) // En temp
return false;
return true;
}
int main()
{
uint8_t temp_inter,temp_fra;
float temp_value;
maxim_max30102_reset(); //resets the MAX30102
// initialize serial communication at 9600 bits per second:
pc.baud(9600);
pc.format(8,SerialBase::None,1);
pc.printf("ready to go!");
wait(1);
//read and clear status register
maxim_max30102_read_reg(0,&temp_inter);
maxim_max30102_init();
while(1)
{
while(INT.read()==1);
maxim_max30102_read_reg(REG_TEMP_INTR,&temp_inter);
maxim_max30102_read_reg(REG_TEMP_FRAC,&temp_fra);
temp_value=temp_inter+temp_fra*0.0625;
pc.printf("temperature is %6.4f \n\r", temp_value);
}
}
上面的代码在MBED平台记得添加MAX32630FTHR和MBED库文件。
图7、实验图片(MAX30102的LED已经点亮了)
图8、串口输出温度数据
最后KEIL的PACK包已经由论坛发出了,MBED的程序终于可以导出到KEIL调试了,但是我在下载选项中没有找到MAX32630的FLASH选项,请教了几个坛友,希望有成功者指点一二。并附上导出的KEIL整个工程文件
自己总结一下这篇帖子。
总结:
1.MAX30102传感器集成度较高,实现了整个心率和血氧测量的模拟前端功能,功耗,体积上有优势
2.淘宝上目前某些的MAX30102模块有硬件错误,要谨慎购买使用
3.MAX32630FHTR板可以通过I2C成功读取MAX30102内部温度数值
厉害,学习了 感谢楼主的分享,想问一下我按图片所示飞线之后接上电源之后板子温度很高,请问楼主出现这种情况了吗? mse 发表于 2017-8-22 20:42
感谢楼主的分享,想问一下我按图片所示飞线之后接上电源之后板子温度很高,请问楼主出现这种情况了吗?
没有出现你说的情况。飞线后I2C总线上拉电压应该和VIN输入电压相同,不过实际测量后发现电压和VIN电压差一点,大概零点几伏,应该是和上面那个6脚芯片有关,但是不清楚那个芯片具体是什么型号。
如果使用我第四篇帖子里重新改版的传感器,就没有上述问题。 你好,除了飞线之外是不是还要讲472与1.8V的相连的线断掉? 小陆肉肉 发表于 2018-5-7 17:39
你好,除了飞线之外是不是还要讲472与1.8V的相连的线断掉?
肯定啊 anning865 发表于 2018-5-8 19:01
肯定啊
那要怎么断开啊?对板子要做如何操作? SolitaryLGQ 发表于 2018-7-4 16:50
那要怎么断开啊?对板子要做如何操作?
已解决:就是将65k5左边那个和472连接的线划掉就可以了;代码在下面网址下载https://www.maximintegrated.com/cn/design/reference-design-center/system-board/6300.html/tb_tab2
下一步打算修改代码使测量更准确 这个芯片是拿来测血氧和心率的,温度只是用来校准的,你竟然拿来测温度 SolitaryLGQ 发表于 2018-7-4 18:22
已解决:就是将65k5左边那个和472连接的线划掉就可以了;代码在下面网址下载https://www.maximintegrated ...
请问下断线是断的哪里,我的MAX30102使用例程无法正常通讯,是这种原因吗 :victory:能指导一下接近模式使用问题么 727741248 发表于 2019-5-6 12:08
能指导一下接近模式使用问题么
看MAX30102最新版说明书,接近模式已经取消掉了,相应寄存器都不提了。我觉得这个功能就用程序简单写一下就能实现,可能没必要用寄存器了。 <p>读出的值每个部位的都不一样(如手和小臂的值),并且移植的算法始终不能出正确的值,这个是怎么回事呢?</p>
<p> </p>
不!努力 发表于 2019-8-15 14:59
读出的值每个部位的都不一样(如手和小臂的值),并且移植的算法始终不能出正确的值,这个是怎么回事呢?
...
<p>大神您解决这个问题了吗?我的也是始终没正确的值,值的跳变也很大,是算法的问题吗?</p>
<p>感觉这个模块网上的算法等都没得到准确的值,原始数据虽然有,但是一经过算法后始终是错误的值,要是说是环境的干扰我用黑色胶布缠绕还是不行</p>
不!努力 发表于 2019-8-19 13:25
感觉这个模块网上的算法等都没得到准确的值,原始数据虽然有,但是一经过算法后始终是错误的值,要是说是环 ...
<p>可能你的传感器是坏的或者劣质品。目前市面上十元左右的30102基本都是原厂挑剩下的残次品。你只能多买几个挑性能比较好的,或者直接买原厂的芯片(价格三十多)。</p>
<p>美信的料市场确实有仿制品出现,这也正是说明美信原装产品的珍贵,大家一定要搽亮眼睛,一定找懂技术的供应商来合作</p>
不!努力 发表于 2019-8-15 14:59
读出的值每个部位的都不一样(如手和小臂的值),并且移植的算法始终不能出正确的值,这个是怎么回事呢?
...
<p>指肚和耳垂的比较准确,手腕和小臂几乎测不到值的。</p>
不!努力 发表于 2019-8-19 13:25
感觉这个模块网上的算法等都没得到准确的值,原始数据虽然有,但是一经过算法后始终是错误的值,要是说是环 ...
<p>这种的只是做来玩玩,如果想要得到精确数值的话,I2C建议通过电平转换芯片来实现,测量位置推荐指肚和耳垂,手腕和小臂基本没用。</p>
页:
[1]