#申请原创#
多年前曾经测试过美信MAX30102血氧脉搏传感器,使用的是STM32F103C8T6最小系统板,显示屏使用LCD5110。当时为了测试方便,我将MAX30102传感器固定在一个瓶盖内,手指正好可以塞进瓶盖内进行测量。下图为当时的测试装置:
当手指塞进瓶盖内,指腹贴在传感器芯片时,稍等几秒钟便可稳定显示出血氧饱和度及脉搏数,同时屏幕上还模拟出脉搏跳动的图形:
本次准备将这个测试移植到PIC18F16Q41核心板,但移植过程非常坎坷。首先在代码移植和分析过程中发现定义了多个下标为500的大数组,由于PIC18F16Q41的资源有限,编译出错,只好将这些数组的下标改为100。然后在测试过程中发现I只要挂接上MAX30102,I2C总线就会读写出错,万般无奈,只好另外启用两个引脚来模拟I2C操作。但模拟I2C也无法得到器件的回应信号,调试了几天都没有结果。下面是调试时用逻辑分析仪抓取的时序图:
这是血氧脉搏传感器在STM32F103C8T6最小系统板上运行时抓取的用于对比分析的时序图:
下面是模拟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也得不到器件的回应,现在仍在继续查找原因中。
|