打印

在线等!gd32F103 PMBus及I2C问题

[复制链接]
699|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 l00140627 于 2024-5-25 09:50 编辑

      项目中用到gd32F103C8T6做主控单元,I2C总线挂了多个外设,如下:
  • I2C0:从设备是一个英飞凌mx38740电压管理芯片,走PMBus。PB8,PB9是SCL和SDA
  • I2C1:从设备是一个TCA9548  I2C Switch芯片; Switch上外挂一个风扇芯片31760,一个TMP112温度芯片,走普通的I2C协议。 PB10是SCL, PB11是SDA
  • 用官方硬件I2C API操作
   问题:
  • I2C1读取31760正常;
  • I2C0读取38740失败, 逻辑分析仪时序如图1


                   图1  硬件API读取I2C0时序

   3  软件模拟,读取I2C0也失败,逻辑分析仪时序如图2
   
                    图2  软件模拟I2C0时序

硬件I2C关键代码如下:
1) I2C初始化

/* 描述:IIC0控制器初始化
* 参数:use_remap   是否使用GPIO remep, 0 不启用, 其他 启用
        speed       I2C速率
        i2c_address I2C地址
        address_mode 地址模式(7bit或10bit)
* 返回值:无*/  
void i2c0_init_ex(uint32_t speed, uint32_t i2c_address)
{
    // 1 使能GPIOB端口的外设时钟
    rcu_periph_clock_enable(RCU_GPIOB);

    // 2 使能映射功能的时钟
    rcu_periph_clock_enable(RCU_AF);

    // 3 设置AFIO_PCF0寄存器的I2C0_REMAP=1;
    gpio_pin_remap_config(GPIO_I2C0_REMAP,ENABLE);

    // 4 I2C0_SCL映射到PB8引脚,I2C0_SDA映射到PB9引脚;
    gpio_init(GPIOB,GPIO_MODE_AF_OD,GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9);

    // 5 使能RCU_I2C0时钟
    rcu_periph_clock_enable(RCU_I2C0);

    // 6 I2C0_SPEED=400000位/秒,配置I2C为快速模式,Tlow/Thigh=2;
    i2c_clock_config(I2C0,speed,I2C_DTCY_2);

    // 7 配置为I2C模式,I2C从机设备使用7位地址为I2C0_SLAVE_ADDRESS7(0xA0)
    i2c_mode_addr_config(I2C0,I2C_I2CMODE_ENABLE,I2C_ADDFORMAT_7BITS,i2c_address);

    // 8 使能I2C外设
    i2c_enable(I2C0);

    // 9 I2C_ACK_ENABLE:ACKEN=1,允许发送ACK应答
    i2c_ack_config(I2C0,I2C_ACK_ENABLE);

}


static uint8_t check_process(uint32_t i2c,i2c_flag_enum flag,uint8_t max_try, uint8_t step)
{
    uint8_t count = 0;
    while (!i2c_flag_get(i2c,flag))
    {
        count++;
        if (count >= max_try)
        {
            return step;
        }
    }
    return 0;
}


/* 描述:指定地址读出一个字节的数据
* 参数:ReadAddr: 需要读出数据的地址
*       ReadByte: 读出的数据值的存放指针
* 返回值:0:读取成功       其他:读取错误*/
uint8_t i2c_read_byte(uint32_t i2c,uint8_t slave_addr,uint16_t ReadAddr, uint8_t addr_len,uint8_t *ReadByte)
{   
    uint8_t err = 0;
    uint16_t count = 0;
    uint8_t check_busy = 0;
    int max_try = 100;

    //PROCESS(i2c_flag_get(i2c, I2C_FLAG_I2CBSY),count,1);
    while (i2c_flag_get(i2c,I2C_FLAG_I2CBSY))
    {
        count++;
        if (count >= max_try)
        {
            i2c_deinit(i2c);
            // return 1;
        }
    }

    /* 1 发送一个起始位到I2C总线 */
    i2c_start_on_bus(i2c);         
    if (err = check_process(i2c, I2C_FLAG_SBSEND,max_try,2)) return err;

    /* 2 等待起始位发送完成 */
    // while(!i2c_flag_get(i2c, I2C_FLAG_SBSEND));

    /* 3 发送器件地址,写数据 */   
    i2c_master_addressing(i2c, slave_addr<<1, I2C_TRANSMITTER);

    /* 4 等待从机地址发送完成 */
    count = 0;
    if (err = check_process(i2c, I2C_FLAG_ADDSEND, max_try,3)) return err;
    // while(!i2c_flag_get(i2c, I2C_FLAG_ADDSEND));

    /* 5 清除从机地址发送完成标志位 */
    i2c_flag_clear(i2c, I2C_FLAG_ADDSEND);         

    /* 6 等待IIC发送区为空 */
    count = 0;
    if (err = check_process(i2c, I2C_FLAG_TBE, max_try,4)) return err;
    // while(!i2c_flag_get(i2c, I2C_FLAG_TBE));     

    /* 7 IIC发送数据,AT24C02需要读出数据的地址*/
    uint8_t high = (ReadAddr >> 8) & 0xFF;
    uint8_t low = ReadAddr & 0xFF;
    if (addr_len == 1)
    {
        i2c_data_transmit(i2c, low);        
    }
    else
    {
        i2c_data_transmit(i2c, high);
        // while(!i2c_flag_get(i2c, I2C_FLAG_TBE));
        count = 0;
        if (err = check_process(i2c, I2C_FLAG_TBE, max_try,5)) return err;
        i2c_data_transmit(i2c, low);
    }

    /* 8 等待IIC发送区为空,即发送完成 */
    // while(!i2c_flag_get(i2c, I2C_FLAG_TBE));
    count = 0;
    if (err = check_process(i2c, I2C_FLAG_TBE, max_try,6)) return err;  

    /* 9 再次发送一个起始位到I2C总线 */
    i2c_start_on_bus(i2c);         

    /* 10 等待起始位发送完成 */
    // while(!i2c_flag_get(i2c, I2C_FLAG_SBSEND));  
    count = 0;
    if (err = check_process(i2c, I2C_FLAG_SBSEND, max_try,7)) return err;

    /* 11 发送器件地址,读数据 */
    i2c_master_addressing(i2c, slave_addr<<1, I2C_RECEIVER);     

    /* 12 等待从机地址发送完成 */
    // while(!i2c_flag_get(i2c, I2C_FLAG_ADDSEND));
    count = 0;
    if (err = check_process(i2c, I2C_FLAG_ADDSEND,max_try,8)) return err;

    /* 13 设置为非应答 NACK, 要在清除FLAG_ADDSEND前*/
    i2c_ack_config(i2c, I2C_ACK_DISABLE);           

    /* 114 清除从机地址发送完成标志位 */
    i2c_flag_clear(i2c, I2C_FLAG_ADDSEND);      

    /* 15 发送一个停止位到I2C总线 */
    i2c_stop_on_bus(i2c);               

    /* 16 等待数据寄存器可读 */
    // while(!i2c_flag_get(i2c, I2C_FLAG_RBNE));   
    count = 0;
    if (err = check_process(i2c, I2C_FLAG_RBNE,max_try,9)) return err;  

    /* 17 读出接收到的数据 */
    *ReadByte  = i2c_data_receive(i2c);

    /* 18 使能应答 ACK, */
    i2c_ack_config(i2c, I2C_ACK_ENABLE);            
    err = 0;
    return err;
}




使用特权

评论回复
沙发
l00140627|  楼主 | 2024-5-25 09:57 | 只看该作者
最早的代码是用的官方demo的写法:
/* send a start condition to I2C bus */
    i2c_start_on_bus(I2C0);

    /* wait until SBSEND bit is set */
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));

    /* send slave address to I2C bus */
    i2c_master_addressing(I2C0, slave_address << 1, I2C_TRANSMITTER);

    /* wait until ADDSEND bit is set */
   while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND));

Keil单步发现是卡在I2C_FLAG_ADDSEND这一步。
后来改为了上面的做法,增加了次数判断

使用特权

评论回复
板凳
zoule45| | 2024-5-25 18:28 | 只看该作者
我 也遇到类似的问题,同样是IIC设备,一个可以,一个行不通,莫名的不执行,之前用GD32E230的可以,现在改用GD32L235了,因为Api函数不一致,做了改动,调试了一周没进展。

使用特权

评论回复
地板
zoule45| | 2024-5-25 18:29 | 只看该作者
官网的demo  都是while 执行,出了eepram 那个demo 加了延迟判断处理,其他的都没有延迟,超时了咋办?

使用特权

评论回复
5
l00140627|  楼主 | 2024-5-26 19:05 | 只看该作者
zoule45 发表于 2024-5-25 18:28
我 也遇到类似的问题,同样是IIC设备,一个可以,一个行不通,莫名的不执行,之前用GD32E230的可以 ...

后来有进展了吗?

使用特权

评论回复
6
丙丁先生| | 2024-5-27 05:51 | 只看该作者

使用特权

评论回复
7
zoule45| | 2024-5-27 14:17 | 只看该作者
l00140627 发表于 2024-5-26 19:05
后来有进展了吗?

在GD32L235上运行事,SCL时钟线都不是规律性的。不知道什么原因,按照官方给的demo配置的参数,除了i2c以外,没运行其他代码

使用特权

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

本版积分规则

1

主题

3

帖子

0

粉丝