MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块(芯片)。它集成了一个660nm红光LED、880nm红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。可通过软件关断模块,待机电流为零,实现电源始终维持供电状态,可运用于低功耗产品中。
MAX30102采用一个1.8V电源和一个独立的3.3V用于内部LED的电源,标准的I2C兼容的通信接口。市面很多都将MAX30102芯片集成在一个PCB模块上,内部增加一个1.8V和3.3V LDO稳压电路,可对模块单独供5.0V电源,方便开发者进行开发。
Max30102芯片及模块
从框图看,芯片可分为两部分,一部分为模拟信号采集电路,通过RED和IR灯发出特定波长的光,采集人体反射回来的光,经过PD管将光信号转化为电信号,最终通过18bit ADC转换器转化为数字信号。
第二部分为数字处理电路,将ADC转换出来的原始数据进行滤波处理后放置于缓冲区内;单片机通过IIC接口读写芯片内部寄存器,读取出相应的数据;
芯片内部有3.3V-5.0V的LED电源和1.8V的逻辑电源,所以模块带有两路稳压电路,将5V电源分别转化为3.3V和1.8V;由于LED驱动电源的供电范围为3.3V-5.0V,3.3V稳压电路可省去。
由于MAX30102的逻辑电路的IIC通信电平为1.8V,这与我们常用的51单片机和STM32单片机的引脚电平不匹配。
这里有个解决方法,因为MAX30102的SDA、SCL、INT引脚为开漏,
可以将模块上的R1、R2、R3电阻去掉,对于51单片机来说,在SDA、SCL、INT引脚上分别加一个4.7-10k电阻上拉至5V;对于STM32单片机,只需要将相应的控制引脚配置为上拉模式即可。
模块只需要接上5V电源,SDA、SCL、INT引脚与单片机连接即可;IRD、RD一般不接,以STM32单片机接线为例(单片机采用模拟IIC的控制方式),如图:
MAX30102内部集成了一整套完整信号采集电路,包括光信号发射及接收、AD转换、环境光干扰消除及数字滤波部分,只将数字接口留给用户。用户只需通过单片机的IIC接口(可用模拟IIC),对MAX30102内部的寄存器进行读写操作,就可以得到转换后的光强度数值。最后需要通过相应的处理算法计算出心率值和血氧饱和度。
对于MAX30102的驱动程序,将其拆分出来,可分为标准IIC程序和MAX30102寄存器的读写操作,实现这两部分的编程,便完成MAX30102的驱动;由于IIC程序比较常用,这里就不例举出来。在完成IIC的通信程序后,实现以下程序;
u8 max30102_Bus_Write(u8 Register_Address, u8 Word_Data)
{
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
i2c_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送字节地址 */
i2c_SendByte(Register_Address);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第5步:开始写入数据 */
i2c_SendByte(Word_Data);
/* 第6步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 发送I2C总线停止信号 */
i2c_Stop();
return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();
return 0;
}
u8 max30102_Bus_Read(u8 Register_Address)
{
u8 data;
/* 第1步:发起I2C总线启动信号 */
i2c_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送字节地址, */
i2c_SendByte((uint8_t)Register_Address);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。下面开始读取数据 */
i2c_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第9步:读取数据 */
{
data = i2c_ReadByte(); /* 读1个字节 */
i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
}
/* 发送I2C总线停止信号 */
i2c_Stop();
return data; /* 执行成功 返回data值 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();
return 0;
}
void max30102_FIFO_Read(u8 Register_Address,u16 Word_Data[][2],u8 count)
{
u8 i=0;
u8 no = count;
u8 data1, data2;
/* 第1步:发起I2C总线启动信号 */
i2c_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送字节地址, */
i2c_SendByte((uint8_t)Register_Address);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。下面开始读取数据 */
i2c_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第9步:读取数据 */
while (no)
{
data1 = i2c_ReadByte();
i2c_Ack();
data2 = i2c_ReadByte();
i2c_Ack();
Word_Data[i][0] = (((u16)data1 << 8) | data2); //
data1 = i2c_ReadByte();
i2c_Ack();
data2 = i2c_ReadByte();
if(1==no)
i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
else
i2c_Ack();
Word_Data[i][1] = (((u16)data1 << 8) | data2);
no--;
i++;
}
/* 发送I2C总线停止信号 */
i2c_Stop();
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();
}
void max30102_init()
{
max30102_Bus_Write(0x09, 0x0b); //mode configuration : temp_en[3] MODE[2:0]=010 HR only enabled 011 SP02 enabled
max30102_Bus_Write(0x01, 0xF0); //open all of interrupt
max30102_Bus_Write(INTERRUPT_REG, 0x00); //all interrupt clear
max30102_Bus_Write(0x03, 0x02); //DIE_TEMP_RDY_EN
max30102_Bus_Write(0x21, 0x01); //SET TEMP_EN
max30102_Bus_Write(0x0a, 0x47); //SPO2_SR[4:2]=001 100 per second LED_PW[1:0]=11 16BITS
max30102_Bus_Write(0x0c, 0x47);
max30102_Bus_Write(0x0d, 0x47);
}
此程序只演示读取出温度,读取血氧数据需要通过max30102_FIFO_Read函数读出;
int main(void)
{
u8 temp_num=0;
main_init();
max30102_init();
printf("\r\n MAX30102 init \r\n");
while(1)
{
delay_ms(1000);
max30102_init();
temp_num = max30102_Bus_Read(0x1f);
printf("当前温度 = %d\r\n",temp_num);
}
}
如需MAX30102相关资料及完整例程, 请关注公众号,首页回复MAX30102获取资料
文章浏览阅读1.3w次,点赞103次,收藏97次。Windows UAC权限详解以及因为权限不对等引发的若干问题分享。_uac权限
文章浏览阅读1.1k次,点赞16次,收藏20次。目标检测的数据集划分以及XML格式转为YOLO的Label格式
文章浏览阅读1.7k次。安装nb_condaconda install nb_conda安装完成后,jupyter notebook中多了Conad选项卡,但此时还不能用,还需要在每一个虚拟环境中安装jupyter.在虚拟环境中安装jupyter进入虚拟环境,Linux&mac环境:source activate your_env_name,在windows下执行conda activate your_env_name在虚拟环境中安装jupyter:pip install ipykernel,这样重新执行j_jupyter conda
文章浏览阅读74次。1.要求在一组数中,插入一个新数,并维护原来的排序方式不变<?php//1.要求在一组数中,插入一个新数,并维护原来的排序方式不变function insertArr($arr,$val){ $pos=0; if (sizeof($arr)==0) return array($val); //传入数组没有值 if (sizeof(..._php 要求在一组数中,插入一个新数,并维护原来的排序方式不变
文章浏览阅读57次。掌握Class.forName方法及获取其对象的属性方法等
文章浏览阅读888次。1.首先全选或者选择你要保存的元素【Ctrl+A】2.Edit---->Export Image3. 导出-->保存over!_pdm 导出图片
文章浏览阅读62次。本来打算一切都尘埃落定再去写东西。可是如果这么一直等下去,真不知道还有没有写的可能啦。 写技术不是件容易的事情突然发现记录生活和写技术日志需要特殊的热情和持久的耐性。生活本身就不容易,做技术也很不容易,我们还要协调,思考,沟通。。。那么最后托着疲惫的身体,麻木的灵魂还能写出什么来呢。。。所以我对那些一只孜孜不倦的写技术日志的人还是充满着敬意和羡慕。羡慕他们在兴趣的驱动下..._最终难得迎来一只菜鸟
文章浏览阅读3.6k次,点赞8次,收藏59次。本文是本着了解物联网原理及如何架构软件到具体案例的应用而梳理的一篇文章,学习了多位前辈的成果,有不足的地方请及时指正。_iot物模型 如何对接
文章浏览阅读5.4k次,点赞2次,收藏3次。在python中,1,星号(*)运算符可以用在两个位置,函数定义和展开集合def func1(*args): #用星号定义可变参数列表 for arg in args: print 'arg=',argif __name__ == '__main__': func1(1,2,3,4)# args={'a':1,'b':2,'c':3,'d':4}..._用双星号可变参数最大值,最小值怎么算
文章浏览阅读1.2w次,点赞2次,收藏8次。一般出现这个原因就要去Springmvc.xml下去查看自己的配置文件有没有出错注意<mvc:annotation-driven />是把映射器和适配器给打开还有注意@Controller注释把ExceptionController封装成对象,要不然也会报错注意<url-pattern>/</url-pattern>拦截”/*”,这是一个错误的..._org.springframework.web.servlet.dispatcherservlet.nohandlerfound no mapping
文章浏览阅读109次。千万不要再回答简单的ArrayList查询快,LinkedList插入快了,这样回答一看就是背题的…先上结论ArrayList基于数组,需要分配连续内存随机访问快(根据下表访问)尾部插入、删除性能可能,其他部分插入、删除会移动数据,性能会降低可以利用cpu缓存,局部性原理LinkedList基于双向链表,无需连续内存随机访问慢(需要沿着链表遍历)头尾插入删除性能高占用内存多原理首先呢,感慨一句,真的随着年限的增长,越来越觉得世界是相通的,技术也是相通的,如果你明白计算机._arraylist vs linkedlist
文章浏览阅读8.2k次,点赞28次,收藏64次。网络编程_什么是网络编程