[PIC®/AVR®/dsPIC®产品]

【CuriosityNano测评报告】06.驱动MAX30102血氧传感器的尝试

[复制链接]
43897|49
手机看帖
扫描二维码
随时随地手机跟帖
hu9jj|  楼主 | 2021-9-14 15:28 | 显示全部楼层 |阅读模式
#申请原创#

    多年前曾经测试过美信MAX30102血氧脉搏传感器,使用的是STM32F103C8T6最小系统板,显示屏使用LCD5110。当时为了测试方便,我将MAX30102传感器固定在一个瓶盖内,手指正好可以塞进瓶盖内进行测量。下图为当时的测试装置:
Max_1.jpg

    当手指塞进瓶盖内,指腹贴在传感器芯片时,稍等几秒钟便可稳定显示出血氧饱和度及脉搏数,同时屏幕上还模拟出脉搏跳动的图形:
Max_2.jpg

    本次准备将这个测试移植到PIC18F16Q41核心板,但移植过程非常坎坷。首先在代码移植和分析过程中发现定义了多个下标为500的大数组,由于PIC18F16Q41的资源有限,编译出错,只好将这些数组的下标改为100。然后在测试过程中发现I只要挂接上MAX30102,I2C总线就会读写出错,万般无奈,只好另外启用两个引脚来模拟I2C操作。但模拟I2C也无法得到器件的回应信号,调试了几天都没有结果。下面是调试时用逻辑分析仪抓取的时序图:
Max_3.jpg

    这是血氧脉搏传感器在STM32F103C8T6最小系统板上运行时抓取的用于对比分析的时序图:
Max_4.jpg

    下面是模拟I2C的代码,这个代码是原来驱动DS1307日历模块的,应该不会有太大的问题:

#include "i2c.h"


/******************************************************************************************************************************************
* 函数名称: I2C_Start()
* 功能说明:        产生I2C传输的Start信号
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Start(void)
{
    SDA_OUT;       //SDA输出
    SDA_1;
    SCL_1;            //scl = 1;
    DELAY_microseconds(2);
    SDA_0;           //sda = 0;        scl为高时sda的下降沿表示“起始”
    DELAY_microseconds(1);
    SCL_0;           //scl = 0; 钳住I2C总线,准备发送或接收数据
}

/******************************************************************************************************************************************
* 函数名称:        I2C_Stop()
* 功能说明:        产生I2C传输的Stop信号
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Stop(void)
{
    SDA_OUT;
    SCL_0;           // scl = 0;
    SDA_0;          // STOP:when CLK is high DATA change form low to high
    DELAY_microseconds(2);
    SCL_1;           // scl = 1;
    DELAY_microseconds(1);
    SDA_1;          // sda = 1;        sclk为高时sdat的上升沿表示“停止”
}


/******************************************************************************************************************************************
* 函数名称: I2C_Send()
* 功能说明:        向IIC总线发送一个字节的数据
* 输    入: byte dat         要发送的数据
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Send(uint8_t dat)
{
    uint8_t i;
    SDA_OUT;
    SCL_0;                     //拉低时钟开始数据传输
    for(i=0; i<8; i++)
    {
        if((dat&0x80)>>7)//准备好SDA数据
            SDA_1;
        else
            SDA_0;
        dat <<= 1;         //dat <<= 1;
        SCL_1;               //拉高时钟等待从设备读取数据
        DELAY_microseconds(3);
        SCL_0;               //拉低时钟准备下一位数据
        DELAY_microseconds(2);
    }
}


/******************************************************************************************************************************************
* 函数名称:        I2C_Receive()
* 功能说明:        从IIC总线接收一个字节的数据
* 输    入: 无
* 输    出: byte                从IIC总线上接收到得数据
* 注意事项: 无
******************************************************************************************************************************************/
uint8_t SI2C_Receive(void)
{
    uint8_t i,dat;
    SDA_IN;        //设置为输入       
    for(i=0;i<8;i++)
    {
        SCL_0;
        DELAY_microseconds(1);
        dat<<=1;
        if(1 == SDA_X)
            dat|=0x01;
    }
    return dat;
}

/******************************************************************************************************************************************
* 函数名称: I2CDoAck()
* 功能说明:        在应答位位置产生应答,从而继续连续传输
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2CDoAck(void)
{
    SCL_0;
    SDA_OUT;
    SDA_0;              //sda = 0;        /拉低数据线,即给于应答
    DELAY_microseconds(1);
    SCL_1;              //scl = 1;
    DELAY_microseconds(1);
    SCL_0;              //scl = 0;
}

/******************************************************************************************************************************************
* 函数名称: I2CNoAck()
* 功能说明:        在应答位位置不产生应答,从而终止连续传输
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2CNoAck(void)
{
    SCL_0;
    SDA_OUT;
    SDA_1;                  // sda = 1;        不拉低数据线,即不给于应答
    DELAY_microseconds(1);
    SCL_1;                   // scl = 1;
    DELAY_microseconds(1);
    SCL_0;              // scl = 0;
}

/******************************************************************************************************************************************
* 函数名称: I2CIsAck()
* 功能说明:        检测从机应答位
* 输    入: 无
* 输    出: uint8_t        0=ACK_OK 从机产生了应答;1=ACK_NO 从机没有产生应答
******************************************************************************************************************************************/
uint8_t SI2CIsAck(void)
{
    uint8_t i;
    SDA_OUT;
    SDA_1;                     // sda = 1; 释放数据线
    DELAY_microseconds(1);
    SDA_IN;
    SCL_1;                     // scl = 1;
    DELAY_microseconds(1);
    while(SDA_X){
        i++;
        if(i>250){
            SI2C_Stop();    //数据线未被拉低,即未收到应答
            return 1;
        }
    }
    SCL_0;
    return 0;
}


/******************************************************************************************
* 函数名称: I2C_8bitByteRead()
* 功能说明: 从指定地址addr开始获取1个字节的数据
* 输    入: uint8_t I2Caddr 器件地址
*           uint8_t addr        数据地址
* 输    出: uint8_t 返回读取的数值
* 备    注:
******************************************************************************************/
uint8_t I2C_8bitByteRead(uint8_t I2Caddr,uint8_t addr)
{
    uint8_t dat;

    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr);       //发送器件地址及读写位,0表示写
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();               //产生停止信号
        return 2;
    }
    SI2C_Send(addr);           //发送读取数据的起始地址
    if(SI2CIsAck())               //检测器件是否有响应
    {
        SI2C_Stop();              //产生停止信号
        return 3;
    }

    SI2C_Start();                  //产生Repeated Start
    SI2C_Send(I2Caddr|1);   //发送器件芯片地址及读写位,1表示读
    if(SI2CIsAck())                //检测器件是否有响应
    {
        SI2C_Stop();              //产生停止信号
        return 4;
    }

    dat = SI2C_Receive();
    SI2CDoAck();

    SI2CNoAck();                //器件要求必须使用NOAck来结束数据读取

    SI2C_Stop();                 //产生停止信号

    return dat;
}

/******************************************************************************************
* 函数名称: I2C_8bitBuffRead()
* 功能说明: 从指定地址addr开始获取size个字节的数据,获取的数据存储在全局变量数组I2C_Buff中
* 输    入: uint8_t I2Caddr 器件地址
*           uint8_t addr        获取数据从addr开始
*                        uint8_t size        要获取的数据个数(1~8)
* 输    出: uint8_t 操作状态
* 备    注:长度不得超过16个字节
******************************************************************************************/
uint8_t I2C_8bitBuffRead(uint8_t I2Caddr,uint8_t addr,uint8_t size,uint8_t *buff)
{
    uint8_t i;

    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr);             //发送器件地址及读写位,0表示写
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 2;
    }
    SI2C_Send(addr);                //发送读取数据的起始地址
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                    //产生停止信号
        return 3;
    }

    SI2C_Start();                   //产生Repeated Start
    SI2C_Send(I2Caddr|1);           //发送器件芯片地址及读写位,1表示读
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 4;
    }

    for(i=0;i<size;i++)             //从addr处读取size个字节的数据
    {
        buff[i] = SI2C_Receive();
        SI2CDoAck();
    }
    SI2CNoAck();                    //器件要求必须使用NOAck来结束数据读取

    SI2C_Stop();                    //产生停止信号
   
    return *buff;
}


/******************************************************************************************
* 函数名称: I2C_8bitByteWrite()
* 功能说明: 从指定地址addr开始写入1个字节的数据
* 输    入: uint8_t I2Caddr 器件地址
*           uint8_t addr        数据地址
* 输    出: uint8_t 返回读取的数值
* 备    注:
******************************************************************************************/
uint8_t I2C_8bitByteWrite(uint8_t I2Caddr,uint8_t addr,uint8_t dat)
{
    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr|0);           //发送从设备芯片地址及读写位,0表示写
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 1;
    }

    SI2C_Send(addr);                //发送数据要写入的地址
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 2;
    }

    SI2C_Send(dat);
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }

    SI2C_Stop();                    //产生停止信号
   
     return 0;

}


/**********************************************************************************************
* 函数名称:        I2C_8bitBuffWrite()
* 功能说明: 向I2C器件的地址addr开始写入size个字节的数据,将要写入的数据存储在全局变量数组I2C_Buff中
* 输    入: uint8_t addr        数据被写入从addr开始的地址处
*                        uint8_t size        要设置的数据个数(1~8)
* 输    出: uint8_t                0= 成功  1=出现错误
**********************************************************************************************/
uint8_t I2C_8bitBuffWrite(uint8_t I2Caddr,uint8_t addr,uint8_t size,uint8_t *buff)
{
    uint8_t i = 0;
       
    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr|0);           //发送器件芯片地址及读写位,0表示写
     if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 1;
    }

    SI2C_Send(addr);                //发送数据要写入的地址
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 2;
    }

    for(i=0;i<size;i++)
    {
        SI2C_Send(buff[i]);
        if(SI2CIsAck())             //检测器件是否有响应
        {
            SI2C_Stop();            //产生停止信号
            return 3;
        }
    }

    SI2C_Stop();                    //产生停止信号

    return 0;
}


/******************************************************************************************
* 函数名称: I2C_16bitByteRead()
* 功能说明: 从指定地址addr开始获取1个字节的数据
* 输    入: uint8_t I2Caddr 器件地址
*           uint16_t addr        双字节数据地址
* 输    出: uint8_t 返回读取的数值
* 备    注:
******************************************************************************************/
uint8_t I2C_16bitByteRead(uint8_t I2Caddr,uint16_t addr)
{
    uint8_t dat;

    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr);             //发送器件地址及读写位,0表示写
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 2;
    }
    SI2C_Send(addr>>8);             //发送读取数据的起始地址
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }
    SI2C_Send(addr|0x0F);           //发送读取数据的起始地址低8位
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }
   
    SI2C_Start();                   //产生Repeated Start
    SI2C_Send(I2Caddr|1);           //发送从设备芯片地址及读写位,1表示读
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 4;
    }

    dat = SI2C_Receive();
    SI2CDoAck();

    SI2CNoAck();                    //从设备要求必须使用NOAck来结束数据读取

    SI2C_Stop();                    //产生停止信号

    return dat;
}

/******************************************************************************************
* 函数名称: I2C_16bitBuffRead()
* 功能说明: 从指定地址addr开始获取size个字节的数据,获取的数据存储在全局变量数组I2C_Buff中
* 输    入: uint8_t I2Caddr 器件地址
*           uint16_t addr        获取数据从addr开始(16位地址)
*                        uint8_t size        要获取的数据个数(1~8)
* 输    出: uint8_t 操作状态
* 备    注:长度不得超过16个字节
******************************************************************************************/
uint8_t I2C_16bitBuffRead(uint8_t I2Caddr,uint16_t addr,uint8_t size,uint8_t *buff)
{
    uint8_t i;

    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr);             //发送器件地址及读写位,0表示写
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 2;
    }
   
    SI2C_Send(addr>>8);             //发送读取数据的起始地址
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
       SI2C_Stop();                //产生停止信号
       return 3;
    }
    SI2C_Send(addr|0x0F);           //发送读取数据的起始地址低8位
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }

    SI2C_Start();                   //产生Repeated Start
    SI2C_Send(I2Caddr|1);           //发送从设备芯片地址及读写位,1表示读
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 4;
    }

    for(i=0;i<size;i++)             //从addr处读取size个字节的数据
    {
        buff[i] = SI2C_Receive();
        SI2CDoAck();
    }
    SI2CNoAck();                    //从设备要求必须使用NOAck来结束数据读取

    SI2C_Stop();                    //产生停止信号
   
    return *buff;
}


/******************************************************************************************
* 函数名称: I2C_16bitByteWrite()
* 功能说明: 从指定地址addr开始写入1个字节的数据
* 输    入: uint8_t I2Caddr 器件地址
*           uint16_t addr        双字节数据地址
* 输    出: uint8_t 返回读取的数值
* 备    注:
******************************************************************************************/
uint8_t I2C_16bitByteWrite(uint8_t I2Caddr,uint16_t addr,uint8_t dat)
{
    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr|0);           //发送从设备芯片地址及读写位,0表示写
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 1;
    }
    SI2C_Send(addr>>8);             //发送读取数据的起始地址
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }
    SI2C_Send(addr|0x0F);           //发送读取数据的起始地址低8位
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }

    SI2C_Send(dat);
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }

    SI2C_Stop();                    //产生停止信号
   
    return 0;

}


/**********************************************************************************************
* 函数名称:        I2C_16bitBuffWrite()
* 功能说明: 向I2C器件的地址addr开始写入size个字节的数据,将要写入的数据存储在全局变量数组I2C_Buff中
* 输    入: uint16_t addr        数据被写入从addr开始的16位地址处
*                        uint8_t size        要设置的数据个数(1~8)
* 输    出: uint8_t                0= 成功  1=出现错误
**********************************************************************************************/
uint8_t I2C_16bitBuffWrite(uint8_t I2Caddr,uint16_t addr,uint8_t size,uint8_t *buff)
{
    uint8_t i = 0;
      
    SI2C_Start();                   //产生起始信号
    SI2C_Send(I2Caddr|0);           //发送从设备芯片地址及读写位,0表示写
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 1;
    }

    SI2C_Send(addr>>8);             //发送读取数据的起始地址
    if(SI2CIsAck())                 //检测从设备是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }
    SI2C_Send(addr|0x0F);           //发送读取数据的起始地址低8位
    if(SI2CIsAck())                 //检测器件是否有响应
    {
        SI2C_Stop();                //产生停止信号
        return 3;
    }

    for(i=0;i<size;i++)
    {
        SI2C_Send(buff[i]);
        if(SI2CIsAck())             //检测从设备是否有响应
        {
            SI2C_Stop();            //产生停止信号
            return 3;
        }
    }

    SI2C_Stop();                    //产生停止信号

    return 0;
}



    先后折腾了十多天了,代码移植尚未完成,模拟I2C也得不到器件的回应,现在仍在继续查找原因中。

使用特权

评论回复
598330983| | 2021-9-14 22:03 | 显示全部楼层
你这传感器给力。

使用特权

评论回复
daichaodai| | 2021-9-15 07:47 | 显示全部楼层
这个传感器有点简陋啊!

使用特权

评论回复
hu9jj|  楼主 | 2021-9-15 08:31 | 显示全部楼层
本帖最后由 hu9jj 于 2021-9-17 09:01 编辑

    此传感器只有指甲盖大小,见下面的照片:
a_006.jpg

    为了使用方便,我将其固定在一个瓶盖里面,使用时只要将手指塞进就行:
b_004.jpg

使用特权

评论回复
cyclefly| | 2021-9-16 19:40 | 显示全部楼层
这传感器可以啊~~有意思

使用特权

评论回复
yangxiaor520| | 2021-9-17 07:39 | 显示全部楼层
这个传感器有点大

使用特权

评论回复
海洋无限| | 2021-9-24 14:07 | 显示全部楼层
你这小瓶盖真给力,点赞,说实话模拟I2C确实不行

使用特权

评论回复
kkzz| | 2021-10-2 12:44 | 显示全部楼层
max30102有没有放大滤波的功能?  

使用特权

评论回复
hudi008| | 2021-10-2 12:45 | 显示全部楼层
有计算心率的功能吗   

使用特权

评论回复
lzmm| | 2021-10-2 12:45 | 显示全部楼层
max30102模块内部的LED满足什么条件会亮

使用特权

评论回复
minzisc| | 2021-10-2 12:45 | 显示全部楼层
美信官方提供的两个算法PBA和SKA

使用特权

评论回复
selongli| | 2021-10-2 12:46 | 显示全部楼层
采集PPG,包括信号处理及心率血氧算法

使用特权

评论回复
fentianyou| | 2021-10-2 12:46 | 显示全部楼层
MAX30102心率血氧显示例程   

使用特权

评论回复
xiaoyaodz| | 2021-10-2 12:46 | 显示全部楼层
有工程文件吗      

使用特权

评论回复
febgxu| | 2021-10-2 12:46 | 显示全部楼层
做滤波算法了吗     

使用特权

评论回复
sdlls| | 2021-10-2 12:47 | 显示全部楼层
有max30102或者30100的算法详解吗   

使用特权

评论回复
pixhw| | 2021-10-2 12:47 | 显示全部楼层
max30102实现脉搏检测吗   

使用特权

评论回复
febgxu| | 2021-10-2 12:47 | 显示全部楼层

使用特权

评论回复
uiint| | 2021-10-3 20:13 | 显示全部楼层
楼主使用的是什么算法?     

使用特权

评论回复
hellosdc| | 2021-10-3 20:14 | 显示全部楼层
用过这个测量心率,太跳了。   

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:Microchip
简介:让我们来为您提供帮助。我们可提供各种资源来帮助您解决一切问题。是否需要与我们的客户支持团队联系?您可以通过电话、在线聊天功能或电子邮件与他们联系。

144

主题

1031

帖子

11

粉丝