这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » N32A455硬件I2C调试

共3条 1/1 1 跳转至

N32A455硬件I2C调试

助工
2024-03-30 15:18:16   被打赏 40 分(兑奖)     打赏

【前言】

i2c的通迅可以用IO来摸拟也可以使用硬件来实现,但是软件的I2C很难实现精准的发送波特率,我在调试MAX30100时,如果遇到软件I2C难以匹配到100KHz或者400KHz的速率,导到与传感器不能正常的通迅。经过认真的阅读官方文档,实现了硬件I2C的收发。在此分享如下。

【硬件】

1、国民技术N32A455车规级开发板。

2、MAX30100传感器。

【软件环境】

1、MDK5.39

2、N32A455 SDK

【I2C通道的选择】

根据开发板的原理图,I2C1接到了板载的EEROM上,并且给出了上拉电阻,因为选择PB8、PB9。

image.png

根据用户手册,I2C使用PB8、PB9需要对GPIO进行复用。

image.png

【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;

}



高工
2024-03-30 17:20:44     打赏
2楼

一般I2C的通信速率并不严格。

偏差实现没有啥问题

333Kbps也是可以的


工程师
2024-04-09 10:01:00     打赏
3楼

不错不错


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]