这次带来一篇使用手机观察心率波形的教程,这可能是最后一篇 关于蓝牙调试器的“硬核广告” 了,这几天一直做这个真的做的有些反感了,感觉时间被浪费,如果有人能从中受益就好了,但看起来不是所有的付出都有收获。不废话了,先看一下效果吧:
这次使用的是MAX30102型的心率检测模块,其为光电投射式的,因为血液对特定波长的光有吸收作用,每次心脏泵血时,该波长都会被大量吸收,以此就可以确定心跳。在使用这种模块时,要注意只能使用手指或其他皮肤比较容易透光的部位进行检测。使用手背或者其它部位基本是检测不到的。估计是因为皮肤中的黑色素会吸收大部分的光,而手指部分皮肤黑色素较少,因此会比较明显。
一、单片机端
1.1硬件连接
MAX30102提供了I2C的接口用于与单片机通信,其作为从设备的地址为0xAE。从图中可以看到其共有八个引脚,其中我们只用了四个,两个用于供电,这个模块可以直接使用3.3V供电。然后SCL和SDA引脚用于I2C通信。
1.2程序编写
完整代码在这里
1.2.1MAX30102驱动
一开始我使用的是STM32的硬件I2C接口,但是搞了半天,发现STM32的硬件I2C一直卡在BUSY标志,没法正常使用,一直听说STM32的硬件I2C不好用,这次真的是吃了教训了。没有办法,又重新写了一个使用IO口来模拟I2C的程序 fake_i2c,fake_i2c的初始化有些不同,下面程序中init_fake_i2c(1,2,MAX30102_ADD,1)的参数 1,2,MAX30102_ADD,1 其中“1”的含义是I2C通信时钟周期系数,该数越小,则通信速率越高,MAX30102最高支持400kHz的通信频率,数据传输还是挺快的。其中的“2”的含义是等待Ack的延时周期,如果从设备在2个时钟周期没有回应,则判定为无Ack。接下来的“MAX30102_ADD”是MAX30102的通信地址。最后的“1”是寄存器地址的宽度,表示宽度为1byte。
//------------------------------------------------------------------------------------------------------
//初始化心率检测模块 MAX30102
//设置模式 : 可设置仅 心率 或 SPO2模式
//设置两种光源的亮度brightness : 配置亮度,范围是00~ff
void initMAX30102(unsigned char mode,unsigned char ir_led_brightness,unsigned char red_led_brightness){
init_fake_i2c(1,2,MAX30102_ADD,1);
fake_i2c_write_reg(0x02,0xf0);
fake_i2c_write_reg(0x03,0x02);
fake_i2c_write_reg(0x04,0x00);
fake_i2c_write_reg(0x05,0x00);
fake_i2c_write_reg(0x06,0x00);
fake_i2c_write_reg(0x08,0x1f);
fake_i2c_write_reg(0x09, mode);
fake_i2c_write_reg(0x0a,0x61);
fake_i2c_write_reg(0x0c, ir_led_brightness);
fake_i2c_write_reg(0x0d, red_led_brightness);
}
然后是数据的读取:
//-------------------------------------------------------------------------------------------------------
//读取采样值
//这些采样值都是经过一定的滤波处理的
//将变量地址传入以读出数据
void MAX30102_ReadValue( float *ir_v,float *red_v){
static unsigned short last_ir,last_red;
static float rate_ir,rate_red;
unsigned char buffer[6];
unsigned char state;
unsigned short raw_value_ir;
unsigned short raw_value_red;
state =fake_i2c_read_reg(0x04);//读取FIFO新数据的数目,如果非0,说明有新的数据
if(state){
fake_i2c_read_buff(0x07,6,buffer);
fake_i2c_write_reg(0x04,0x00);
fake_i2c_write_reg(0x06,0x00);
raw_value_ir = (buffer[1]<<8)+buffer[2];
raw_value_red = (buffer[4]<<8)|buffer[5];
if(raw_value_ir!=last_ir)
rate_ir += ((raw_value_ir-last_ir)-rate_ir)*0.18f;
if(raw_value_red!=last_red)
rate_red += ((raw_value_red-last_red)-rate_red)*0.18f;
last_ir = raw_value_ir;
last_red = raw_value_red;
}
(*red_v)+=rate_red;
(*ir_v)+=rate_ir;
(*ir_v)*=0.8f;
(*red_v)*=0.8f;
if(*ir_v>MAX30102_FILTERED_VALUE_RANGE)
*ir_v = MAX30102_FILTERED_VALUE_RANGE;
if(*ir_v<-MAX30102_FILTERED_VALUE_RANGE)
*ir_v = -MAX30102_FILTERED_VALUE_RANGE;
if(*red_v>MAX30102_FILTERED_VALUE_RANGE)
*red_v = MAX30102_FILTERED_VALUE_RANGE;
if(*red_v<-MAX30102_FILTERED_VALUE_RANGE)
*red_v = -MAX30102_FILTERED_VALUE_RANGE;
}
在这里并没有输出读取的原数据,而是采用一些滤波的算法,先微分,然后确定波动变化率,再对波动变化率积分,并且将积分的数值通过系数向0靠拢,以及限幅,总之就是什么乱七八糟各种手段用上,最终得到一串看得过去的波形。至于心率和血氧等的计算我就偷个懒不写了。
1.2.2通信配置
//根据实际需要的变量,定义数据包中 bool byte short int float 五种类型的数目
#define TX_BOOL_NUM 0
#define TX_BYTE_NUM 0
#define TX_SHORT_NUM 0
#define TX_INT_NUM 0
#define TX_FLOAT_NUM 2
1.2.3主函数
/// ir:红外 red:红光 检测两种光源的反射程度
TxPack txpack;
float ir_value,red_value;
int main(void)
{
initMAX30102(MAX30102_MODE_IR,0x66,0x65);
initValuePack(115200);
while(1)
{
//延时
for(inti=0;i<20000;i++)
{}
MAX30102_ReadValue(&ir_value,&red_value);
txpack.floats[0] = ir_value;
txpack.floats[1] = red_value;
sendValuePack(&txpack);
}
}
二、手机端
详细的手机端应用的介绍见这里,接下来就默认你看过我之前的教程了
2.1通信配置
我们可以从MAX30102读取到两种不同光源的光反射量,分别读取红外光和红色可见光下血液吸收量。
2.2编辑控件
这里为了观察更方便,我们将工程切换为横屏视图
然后编辑控件,添加了一个波形图和一个能量槽:
链接数据,这里我只链接了IR 红外光的检测值。能量槽上下限的设置需要注意,可以设置为-300~300,如下图
至此就可以连接蓝牙模块,运行此工程,你就能看到自己的心跳了。
STM32驱动MAX30102代码在这里
另附MAX30100的代码
应用可以在这里扫码下载
如果本文对你有用的话,来个红包支持一哈
如果觉得《单片机无线调试-看见心跳-手机显示心率波形》对你有帮助,请点赞、收藏,并留下你的观点哦!