[研电赛技术支持] 正确修复 GD32 写入 EEPROM 出错问题

[复制链接]
1451|0
Haizangwang 发表于 2025-9-3 17:01 | 显示全部楼层 |阅读模式
       GD32 访问 EEPROM 有时会发现数据写操作后不生效,需要写的数据没有正确写入。查阅了网上的一些文章,都没有得到很好的解答和正确修复。STM32或其他MCU访问EEPROM也可能遇到类似问题。

        我们来看下GD32的EEPROM的官方代码:

at24cxx.h

at24cxx.c

      我们发现在处理硬件flag置位时为防止程序卡死,at24cxx.h 设置了重试次数的限制:

#define AT24CXX_BIT_CHECK_SUCCESS(bit)                \
    {                                                 \
        int i = 0;                                    \
        for (i = 0; i < AT24CXX_BIT_CHECK_COUNT; i++) \
        {                                             \
            if (!(bit))                               \
                break;                                \
            delay_while_ms(1);                        \
        }                                             \
        if (i == AT24CXX_BIT_CHECK_COUNT)             \
            return;                                   \
    }

       在实际判断中不使用 while(1)  而是使用以上代码,check硬件flag bit时,超出尝试次数就退出并且没有给出任何出错信息。看似程序执行完了实际上有可能是超过循环次数退出。

    /* wait until I2C bus is idle */
    AT24CXX_BIT_CHECK_SUCCESS(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));

    /* send a start condition to I2C bus */
    i2c_start_on_bus(I2C1);

    /* wait until SBSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_SBSEND));

    /* send slave address to I2C bus */
    i2c_master_addressing(I2C1, eeprom_address, I2C_TRANSMITTER);

    /* wait until ADDSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));
    /* clear the ADDSEND bit */
    i2c_flag_clear(I2C1,Iw2C_FLAG_ADDSEND);

      我们将头文件 AT24CXX_BIT_CHECK_SUCCESS 部分做如下修改:

#if 1

#define AT24CXX_BIT_CHECK_SUCCESS(bit)  while(bit)

#else

#define AT24CXX_BIT_CHECK_SUCCESS(bit)                \
    {                                                 \
        int i = 0;                                    \
        for (i = 0; i < AT24CXX_BIT_CHECK_COUNT; i++) \
        {                                             \
            if (!(bit))                               \
                break;                                \
            delay_while_ms(1);                        \
        }                                             \
        if (i == AT24CXX_BIT_CHECK_COUNT)             \
            return;                                   \
    }
#endif

      这样如果发生硬件flag bit没有置位就会卡死在 while(1) 循环的位置;

      我们拿MDK调试时就可以查出程序在访问EEPROM时卡死在哪里。

      经过调试,我们发现第二轮或更后几轮卡死在check I2C_FLAG_ADDSEND flag bit位置:

    i2c_master_addressing(I2C1, eeprom_address, I2C_TRANSMITTER);

    /* wait until ADDSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));
    /* clear the ADDSEND bit */
    i2c_flag_clear(I2C1,I2C_FLAG_ADDSEND);

      就是GD32 在第n轮,发 EEPROM 写访问地址的时候卡死了。网上大部分的分析是I2C_FLAG_ADDSEND被置位了,但由于某些异常被意外快速清掉了。带着JTAG不行,全速跑就可以。修改个其他地方让异常不出现,等各种奇怪的解释和解法。

      其实我们读一下EEPROM的手册就能找到正确答案和解法。EEPROM手册告诉我们写访问的典型时间是3ms , 最多5ms。那正确的分析就来了。GD32运行比较快,两次写操作之间的程序运行时间的间隔小于5ms就可能EEPROM 还处在上一次的写操作状态,不响应I2C的地址请求。而且这样也错过了GD32 发的I2C地址,这样就会一直卡在:

    /* wait until ADDSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));

      对这种情况我们有两种解法:

1) 对于时效性要求不高的系统简单粗暴的解法,在写函数开始时加6ms的延时,以保证上一次操作已经完成;

void eeprom_page_write(uint8_t* p_buffer, uint8_t write_address, uint16_t number_of_byte)
{
// Page write time is 3ms typical , max 5ms
    delay_1ms(6) ;

// ===================================================================

    /* wait until I2C bus is idle */
    AT24CXX_BIT_CHECK_SUCCESS(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));

    /* send a start condition to I2C bus */
    i2c_start_on_bus(I2C1);

    /* wait until SBSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_SBSEND));

    /* send slave address to I2C bus */
    i2c_master_addressing(I2C1, eeprom_address, I2C_TRANSMITTER);

    /* wait until ADDSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));
    /* clear the ADDSEND bit */
    i2c_flag_clear(I2C1,I2C_FLAG_ADDSEND);

2) 对于时效性要求高的系统我们需要在 I2C_FLAG_ADDSEND check 失败几轮后,

      a. 停止 I2C 访问;

    /* send a stop condition to I2C bus */
    i2c_stop_on_bus(I2C1);

      b. 重新 check I2C_FLAG_I2CBSY flag bit, 启动I2C;

    /* wait until I2C bus is idle */
    AT24CXX_BIT_CHECK_SUCCESS(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));

    /* send a start condition to I2C bus */
    i2c_start_on_bus(I2C1);

    /* wait until SBSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_SBSEND));

      c. 然后重新发送 I2C 地址请求;

    /* send slave address to I2C bus */
    i2c_master_addressing(I2C1, eeprom_address, I2C_TRANSMITTER);

    /* wait until ADDSEND bit is set */
    AT24CXX_BIT_CHECK_SUCCESS(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));
    /* clear the ADDSEND bit */
    i2c_flag_clear(I2C1,I2C_FLAG_ADDSEND);

      这样就能保证在 EEPROM 在上一轮写操作完成后能迅速响应下一轮写操作,而不必等固定的6ms延时。

      这是正确的分析和治本的解法。STM32或其他MCU访问EEPROM也可能遇到类似问题。

      第二种解法的具体实现方法请大家自己完成。可以贴在评论区里。
————————————————
版权声明:本文为CSDN博主「小庐知行」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tideyin123/article/details/150869332

您需要登录后才可以回帖 登录 | 注册

本版积分规则

75

主题

240

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部