一.调试过程
执行两句代码
I2C_MAX30102_Single_Write(MAX30102_CONFIG_MODE,0X40); //复位
I2C_MAX30102_Single_Write(MAX30102_CONFIG_MODE,0X03); //开启SPO2模式
整体效果图:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLkBDOxMWNyYzYzUmMwAjZiF2Y5QDM5gjZ5MWOilTZ4czLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
高电平宽度为12微秒
低电平宽度为86微秒
其中的I2C_MAX30102_Single_Write函数代码:
/*---------------------------------------------------------------------------
* 函数:I2C_MAX30102_Single_Write(uint8_t Register_Address,uint8_t Word_Data)
* 描述:把单个数据写入到相应的寄存器中
* 参数:Register_Address 寄存器地址
Word_Data 写入字节
* 返回:1:写入成功; 0:写入失败
*---------------------------------------------------------------------------*/
static uint8_t I2C_MAX30102_Single_Write(uint8_t Register_Address,uint8_t Word_Data)
{
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
I2C_MAX30102_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_MAX30102_SendByte( MAX30102_WR_address | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
if (I2C_MAX30102_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送字节地址 */
I2C_MAX30102_SendByte(Register_Address);
if (I2C_MAX30102_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第5步:开始写入数据 */
I2C_MAX30102_SendByte(Word_Data);
/* 第6步:发送ACK */
if (I2C_MAX30102_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 发送I2C总线停止信号 */
I2C_MAX30102_Stop();
return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
I2C_MAX30102_Stop();
return 0;
}
其中的I2C_MAX30102_Start()函数:
/*---------------------------------------------------------------------------
* 函数:I2C_MAX30102_Start(void)
* 描述:IIC主机发出开始条件
* 参数:无
* 返回:无
*---------------------------------------------------------------------------*/
static void I2C_MAX30102_Start(void)
{
I2C_MAX30102_Sda_Mode(GPIO_Mode_OUT); //sda线输出
MAX30102_SDA_OUT=1; //SDA置为高电平
MAX30102_SCL=1; //SCL置为高电平
delay_us(5);
MAX30102_SDA_OUT=0;
delay_us(5);
MAX30102_SCL=0;//钳住I2C总线,准备发送或接收数据
}
所以先不管其他部分的时序正确与否,首先这个I2C_MAX30102_Start的起始信号就与程序设计中的不符合,尤其是SCL的时钟线信号,实际测试时一直处于低电平状态。
I2C_MAX30102_Sda_Mode函数:
/*---------------------------------------------------------------------------
* 函数:I2C_MAX30102_Sda_Mode(GPIOMode_TypeDef mode)
* 描述:IIC主机改变SDA的IO状态
* 参数:GPIO_Mode_IN GPIO_Mode_OUT
* 返回:无
*---------------------------------------------------------------------------*/
static void I2C_MAX30102_Sda_Mode(GPIOMode_TypeDef mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //第9号引脚
GPIO_InitStructure.GPIO_Mode = mode; //输入/输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出,增强驱动能力,引脚的输出电流更大
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //引脚的速度最大为100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //没有使用内部上拉电阻
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
首先推测是GPIO引脚配置的问题,所以两个IIC信号不对。
mpu6050也是采用IIC,引脚初始化如下:
void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
max30100引脚初始化如下:
/*---------------------------------------------------------------------------
* 函数:I2C_MAX30102_Config(void)
* 描述:MAX30102引脚初始化
* 参数:无
* 返回:无
*---------------------------------------------------------------------------*/
void I2C_MAX30102_Config(void)
{
//Delay_Init();//初始化SysTick时钟延时
GPIO_InitTypeDef GPIO_InitStruct;
//打开GPIOE组时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; //引脚 7
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度
GPIO_Init(GPIOE, &GPIO_InitStruct);
PEout(7) = 1;
//空闲状态
MAX30102_SCL = 1;
MAX30102_SDA_OUT = 1;
}
区别点在于 推挽输出 与上拉
现在主循环测试两个GPIO引脚的设置是否正确
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_1);
GPIO_SetBits(GPIOB,GPIO_Pin_2);
delay_ms(15);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_2);
delay_ms(15);
}
GPIO8和GPIO9也可正常使用
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_8);
GPIO_SetBits(GPIOB,GPIO_Pin_9);
delay_ms(20);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
GPIO_ResetBits(GPIOB,GPIO_Pin_9);
delay_ms(10);
}
引脚配置如下:
/*---------------------------------------------------------------------------
* 函数:I2C_MAX30102_Config(void)
* 描述:MAX30102引脚初始化
* 参数:无
* 返回:无
*---------------------------------------------------------------------------*/
void I2C_MAX30102_Config(void)
{
//Delay_Init();//初始化SysTick时钟延时
GPIO_InitTypeDef GPIO_InitStruct;
//打开GPIOE组时钟
//RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度
GPIO_Init(GPIOB, &GPIO_InitStruct);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; //引脚 7
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
// GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
// GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度
//
// GPIO_Init(GPIOE, &GPIO_InitStruct);
//
// PEout(7) = 1;
//空闲状态
// MAX30102_SCL = 1;
// MAX30102_SDA_OUT = 1;
}
其中的GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;一定要是GPIO_OType_PP,如果使用GPIO_OType_OD,无法产生信号。
其中的GPIO_PuPd_UP或者GPIO_PuPd_NOPULL不影响,都可产生信号
将GPIO_SetBits(GPIOB,GPIO_Pin_8);修改为MAX30102_SCL = 1;
无法产生高电平信号,所以程序中的问题主要在于此。
#define MAX30102_SCL PBout(8)
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
大概率是这边的位带操作代码的地址出现问题,导致无法控制寄存器
对代码的GPIO控制修改后,成功产生起始信号
与设计的相符合
具体修改的代码如下,注释的为原有代码,替换为新的GPIO控制方式:
/*---------------------------------------------------------------------------
* 函数:I2C_MAX30102_Start(void)
* 描述:IIC主机发出开始条件
* 参数:无
* 返回:无
*---------------------------------------------------------------------------*/
void I2C_MAX30102_Start(void)
{
I2C_MAX30102_Sda_Mode(GPIO_Mode_OUT); //sda线输出
//MAX30102_SDA_OUT=1;
MAX30102_SDA_H;
//MAX30102_SCL=1;
MAX30102_SCL_H;
delay_us(5);
//MAX30102_SDA_OUT=0;
MAX30102_SDA_L;
delay_us(5);
//MAX30102_SCL=0;//钳住I2C总线,准备发送或接收数据
MAX30102_SCL_L;
}
将PBOUT的方式修改为set和reset的方式,具体代码如下:
---------------------------------------------------------------------------*/
#define MAX30102_SCL PBout(8)
#define MAX30102_SDA_IN PBin(9)
#define MAX30102_SDA_OUT PBout(9)
#define MAX30102_SCL_H GPIO_SetBits(GPIOB,GPIO_Pin_8)
#define MAX30102_SCL_L GPIO_ResetBits(GPIOB,GPIO_Pin_8)
#define MAX30102_SDA_H GPIO_SetBits(GPIOB,GPIO_Pin_9)
#define MAX30102_SDA_L GPIO_ResetBits(GPIOB,GPIO_Pin_9)
#define MAX30102_SDA_READ GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_9)