发新帖本帖赏金 50.00元(功能说明)我要提问
返回列表
打印

【技术分享】GD32硬件I2C调试中的问题与解决过程

[复制链接]
2326|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
blust5|  楼主 | 2023-5-5 10:51 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
#申请原创#      @21小跑堂   @21小跑堂
最近一个项目需要测量海拔高度,在淘宝上买了一个气压高度计模块,芯片是MPL3115A2I2C通讯接口的气压计模块。
到货后自己把排针焊上,准备测试。
之前已经在用GD32303C-EVAL开发板在做其他相关的开发工作了,包括屏幕显示、串口中断收发等功能,都是用的官方例程进行组合,然后做相应改动来进行的。
首先用官方的屏显例程,理解例程里的显示接口,根据需要修改成自己的显示内容;然后将串口中断收发例程融合进去,并自己添加了串口接收环形缓冲区功能。并成功将串口接收到的内容显示到屏幕上。
由于以上过程相对简单,且和本篇主题相关性不大,于是略过详细内容。
下一步开始的工作就是I2C接口驱动的移植,这里使用I2C_EEPROM例程进行融合,然后修改芯片地址即可(开发板上的EEPROM芯片是AT24Cxx,地址是0xA0,我手上的MPL3115A2气压计芯片地址是0xC0),可直接使用开发板上的I2C引脚,并且不需要将EEPROM芯片断开,通过地址即可进行选择读取。
在进行了一系列的融合与地址修改之后,并编写了MPL芯片的初始化程序,编译,通过,下载,运行,发现死机了……
#define DATA_STATUS_ADDR       0x06
#define DATA_START_ADDR        0x01
#define SYSMOD_ADDR            0x11
#define CTRL_REG1_ADDR         0x26
#define CTRL_REG2_ADDR         0x27

void MPL_Init(void)
{
        uint8_t reg_data;
        reg_data = 0x03;
        eeprom_byte_write(®_data, CTRL_REG1_ADDR);
}

通过断点追踪调试,发现程序卡在了I2C程序的一个状态位等待的语句上,一直卡在这里不往下运行了。
一开始怀疑哪里配置没有搞对,然后开始一步一步排查问题。
首先是直接运行官方例程,即直接读取和写入EEPROM,发现运行正常,读取和写入过程都是正常执行,且结果也符合预期。确认了例程没有问题。
然后进行下一步验证,直接在官方例程里将芯片地址由0xA0改为0xC0
at24cxx.c文件:
i2c.h文件:
同时根据芯片手册调整寄存器地址,进行读写验证。
由于大部分寄存器初始值都是0,这里读取0x0C寄存器进行验证,这个寄存器是设备ID,其值固定为0xC4
原例程里i2c_24c02_test()函数是用来验证EEPROM芯片的读写的,其内容如下。
/*!

    \brief      I2C read and write functions

    \param[in]  none

    \param[out] none

    \retval     I2C_OK or I2C_FAIL

*/

uint8_t i2c_24c02_test(void)

{

    uint16_t i;

    uint8_t i2c_buffer_write[BUFFER_SIZE];

    uint8_t i2c_buffer_read[BUFFER_SIZE];

   

    printf("\r\nAT24C02 writing...\r\n");

   

    /* initialize i2c_buffer_write */

    for(i = 0;i < BUFFER_SIZE;i++){

        i2c_buffer_write[i]=i;

        printf("0x%02X ",i2c_buffer_write[i]);

        if(15 == i%16){

            printf("\r\n");

        }

    }

    /* EEPROM data write */

    eeprom_buffer_write(i2c_buffer_write,EEP_FIRST_PAGE, BUFFER_SIZE);

    printf("AT24C02 reading...\r\n");

    /* EEPROM data read */

    eeprom_buffer_read(i2c_buffer_read,EEP_FIRST_PAGE, BUFFER_SIZE);

    /* compare the read buffer and write buffer */

    for(i = 0;i < BUFFER_SIZE;i++){

        if(i2c_buffer_read[i] != i2c_buffer_write[i]){

            printf("0x%02X ", i2c_buffer_read[i]);

            printf("Err:data read and write aren't matching.\n\r");

            return I2C_FAIL;

        }

        printf("0x%02X ", i2c_buffer_read[i]);

        if(15 == i%16){

            printf("\r\n");

        }

    }

    printf("I2C-AT24C02 test passed!\n\r");

    return I2C_OK;

}

现在修改其内容,将其原本内容删除,增加寄存器读取内容。
uint8_t i2c_24c02_test(void)

{

    uint16_t i;

    uint8_t i2c_buffer_write[BUFFER_SIZE];

    uint8_t i2c_buffer_read[BUFFER_SIZE];

   

    i2c_buffer_read[0] = 0x00;

    eeprom_buffer_read(i2c_buffer_read,0x0C, 1);

   

    return I2C_OK;

}

然后进行单步运行验证,发现程序运行完eeprom_buffer_read(i2c_buffer_read,0x0C, 1);语句之后,i2c_buffer_read[0]的值变成了0xC4,表明程序正确运行了。
为了保险起见,又再次验证了写操作。再次修改i2c_24c02_test(void)函数,其中0x26MPL芯片的工作模式配置寄存器地址。
uint8_t i2c_24c02_test(void)

{

    uint16_t i;

    uint8_t i2c_buffer_write[BUFFER_SIZE];

    uint8_t i2c_buffer_read[BUFFER_SIZE];

   

    i2c_buffer_write[0] = 0x00;

    eeprom_byte_write(i2c_buffer_write,0x26);

    eeprom_buffer_read(i2c_buffer_read,0x26, 1);

    i2c_buffer_write[0] = 0x03;

    eeprom_byte_write(i2c_buffer_write,0x26);

    eeprom_buffer_read(i2c_buffer_read,0x26, 1);

   

    return I2C_OK;

}

通过单步运行发现,可以成功写入寄存器,并进行读取验证。
由此确认,硬件没有问题,开发板和MPL芯片模块都是正常的,且两者的硬件连接也没有问题。
那么问题就是出在软件上了。
由于出问题的工程也是由例程进行移植的,下一步要怎么进行问题定位呢?
首先对源代码进行对比,查看差异点。
使用BCompare软件对两个工程的代码进行对比,结果如下:
首先把各个目录的含义简单说一下。
          .settings:工程配置文件夹,无需比较
          firmware:官方驱动库文件,;两个工程一致
          GD ARM MCU Release:编译时生成的相关文件,无需比较
          inc:工程头文件(.h文件),需要对比
          ldscriptsFLASH配置文件,两个工程一致
          src:工程源文件(.c文件),需要对比
后面四个是工程相关文件,无需比较。
通过对比,可以清晰的看到哪些文件时一样的,哪些文件不一样。双击不一样的文件,可打开查看不一样的内容。
左边的目录多了cycle_buffer.ccycle_buffer.h两个文件,这两个是增加的串口环形缓冲区的封装接口,跟I2C功能不相关,也跟单片机的底层驱动不相关,因此可以忽略。
然后查看systick.h文件:
左上角是全文的对比导图,红色是有区别的地方,蓝色是注释里有区别的地方,可以很直观的看到全文的异同点。这里可以看到有问题的工程只是多了一个全局变量的定义,不会影响I2C功能。
对比gd32f30x_it.c文件,发现区别点是增加了变量定义、while(1);while(1){}的区别、SysTick_Handler()函数的区别,以及多了串口中断函数,基本确认都和I2C功能无关。
picture.c文件的差异点只是因为没有调用一个图片的显示,从而注释掉了一个图片常量数组,也和I2C功能无关。
由此确认,功能异常是在main.c文件里的。
到这里就没法通过对比定位问题点了,因为有问题的工程前面调试其他功能对该文件做了许多修改。
那么要怎么定位问题点呢?
既然确认了问题出在main.c文件里,那么下一步可以针对这个文件进行单步调试,逐步验证出问题的函数语句在哪里。
从程序运行的起点:main()函数入口进行对比。
发现I2C初始化之前,不一样的地方只是多了一部分变量定义和串口中断初始化,变量定义不会影响程序运行,下面验证串口中断初始化是否会影响I2C功能。
注释掉该部分内容,再次调试程序,发现问题依然存在,基本排除串口中断初始化内容的影响。
再往下的部分,例程里已经没有其他内容了,直接到了EEPROM读写验证程序调用。
而有问题的工程还进行了很多的初始化和屏幕显示内容。为了定位问题点,先将测试函数移动到所有初始化内容之前进行验证。
改写MPL_Init()函数:
void MPL_Init(void)

{

uint8_t reg_data, get_data=0;



eeprom_buffer_read(&get_data, 0x0C, 1);

reg_data = 0x03;

eeprom_byte_write(®_data, CTRL_REG1_ADDR);

eeprom_buffer_read(&get_data, CTRL_REG1_ADDR, 1);

}

MPL_Init()函数在main()函数里往前移动,直接移动到I2C初始化完成之后:
MPL_Init()里设置断点,进行单步运行验证:
运行完438行的代码之后,变量get_data的值变成了0xC4,即读取成功了;
而继续往下运行,到运行完441行的代码之后,get_data的值变成了0x03,即对CTRL_REG1_ADDR寄存器的写入和读取操作成功了。
由此确认前面的初始化没有问题,应该是后面的其他初始化或代码运行影响到了I2C的功能。
下面就是继续往下验证,一步步的往下移动MPL_Init()的位置,进行调试、验证,看放到哪里之后会出问题。
通过这种方法定位,发现exmc_lcd_init()函数执行完之后,I2C的功能就会出问题。那么这个函数都做了什么呢?
这个函数初始化了屏幕驱动的GPIO口,以及EXMC模块。
目前已经定位到了问题点,至于具体引发该问题的原因,还需要继续详细寻找和确认,我暂时还没有解决。
有经验的技术大拿们有空可以指点一下迷津。

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 50.00 元 2023-05-11
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2023-5-11 17:05 回复TA
以开发板的例程为依托,移植IIC模块,对出现的问题严谨分析,逐步排查 
blust5 2023-5-8 09:09 回复TA
问题已经解决,详见 https://bbs.21ic.com/icview-3300846-1-1.html?fromuser=blust5 
沙发
369122197| | 2023-5-5 12:22 | 只看该作者
I2C时序应该是要微调的

使用特权

评论回复
板凳
blust5|  楼主 | 2023-5-5 15:05 | 只看该作者
369122197 发表于 2023-5-5 12:22
I2C时序应该是要微调的

时序应该是没问题,因为屏蔽掉那句初始化语句之后就可以正常读写操作了

使用特权

评论回复
发新帖 本帖赏金 50.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:业精于勤荒于嬉,行成于思毁于随。

72

主题

2834

帖子

11

粉丝