【前言】
i2c的通迅可以用IO来摸拟也可以使用硬件来实现,但是软件的I2C很难实现精准的发送波特率,我在调试MAX30100时,如果遇到软件I2C难以匹配到100KHz或者400KHz的速率,导到与传感器不能正常的通迅。经过认真的阅读官方文档,实现了硬件I2C的收发。在此分享如下。
【硬件】
1、国民技术N32A455车规级开发板。
2、MAX30100传感器。
【软件环境】
1、MDK5.39
2、N32A455 SDK
【I2C通道的选择】
根据开发板的原理图,I2C1接到了板载的EEROM上,并且给出了上拉电阻,因为选择PB8、PB9。
根据用户手册,I2C使用PB8、PB9需要对GPIO进行复用。
【I2C实始化】
1、先重定义一下I2C的IO,方便阅读:
#define I2C1_SCL_PIN GPIO_PIN_8
#define I2C1_SDA_PIN GPIO_PIN_9
#define GPIOx GPIOB
2、初始化函数如下:
void my_I2C_Init(void)
{
/** GPIO configuration and clock enable */
GPIO_InitType GPIO_InitStructure; //声明GPIO结构体
I2C_InitType I2C_InitStructure; //声明I2C结构体
/** enable peripheral clk*/
I2C1_peripheral_clk_en(); //使能i2c时钟
I2C_DeInit(I2C1); //重置I2C1
I2C1_scl_clk_en(); //打开SCL 的GPIO时钟
I2C1_sda_clk_en(); //打开SDA的GPIO时钟
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE); //使能APB2时钟
GPIO_ConfigPinRemap(GPIO_RMP_I2C1, ENABLE); //复用i2c端口
GPIO_InitStructure.Pin = I2C1_SCL_PIN | I2C1_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);
/* 配置I2C 为 400K */
I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
I2C_InitStructure.OwnAddr1 = 0xff;
I2C_InitStructure.AckEnable = I2C_ACKEN;
I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
I2C_InitStructure.ClkSpeed = I2C_Speed;
I2C_Init(I2C1, &I2C_InitStructure);
}
2、向MAX00指定的地址发送一个byte的命令。
/**
* @ 功能:向MAX30100指定地址写入命令.
* @param 需要写入的地址.
* @param 需要写入的值 .
* 返回值 0为正常.
*/
uint8_t max30100_Bus_Write(uint8_t Register_Address, uint8_t Word_Data)
{
sMAX30100Timeout = sEE_LONG_TIMEOUT; //重置超时时间
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY)) //等待i2c1的总线为空闲
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/* 向总线发送起始信号 */
I2C_GenerateStart(I2C1, ENABLE);
/** 等待总线的回复 */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 写入从机地址 */
I2C_SendAddr7bit(I2C1, 0xAE, I2C_DIRECTION_SEND);
/** 等待从机反回ACK */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 向MAX30100发送需要写入的寄存器地址 */
I2C_SendData(I2C1, Register_Address);
/**等待发送完成标志 */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 向总线写入数据 */
I2C_SendData(I2C1, Word_Data);
/** 等待发送结束的标志位 */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/**发送STOP信号 */
I2C_GenerateStop(I2C1, ENABLE);
return 0;
}
3、从MAX30100读取指定寄存器一个byte。
/**
* @ 功能:从MAX30100指定地址读取一个byte.
* @param 需要写入的地址.
* @param 需要写入的值 .
* 返回值 0为正常.
*/
uint8_t max30100_Bus_Read(uint8_t Register_Address)
{
uint8_t data;
//等待i2c1的总线为空闲
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
//清除ACK标志位,并使能返回ACK
I2C_ConfigNackLocation(I2C1, I2C_NACK_POS_CURRENT); // clear ACKPOS
I2C_ConfigAck(I2C1, ENABLE);
/* 向总线发送起始信号 */
I2C_GenerateStart(I2C1, ENABLE);
/** 等待总线的回复 */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 写入从机地址 即MAX30100的地址*/
I2C_SendAddr7bit(I2C1, 0xAE, I2C_DIRECTION_SEND);
/**等待发送完成标志 */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Clear EV6 by setting again the PE bit */
I2C_Enable(I2C1, ENABLE);
/** 向MAX30100发送需要读取的寄存器地址 */
I2C_SendData(I2C1, Register_Address);
/** 等待发送结束标志 */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 产生起始信号 */
I2C_GenerateStart(I2C1, ENABLE);
/** Test on EV5 and clear it */
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 发送以读的方式发送从机地址 */
I2C_SendAddr7bit(I2C1, 0xAE, I2C_DIRECTION_RECV);
sMAX30100Timeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2C1, I2C_FLAG_ADDRF))
{
if ((sMAX30100Timeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** 不需要等待ACK */
I2C_ConfigAck(I2C1, DISABLE);
(void)(I2C1->STS1); /// clear ADDR
(void)(I2C1->STS2);
/* 发送结束信号 */
I2C_GenerateStop(I2C1, ENABLE);
//读取数据
data = I2C_RecvData(I2C1);
return data;
}