最近一个项目需要测量海拔高度,在淘宝上买了一个气压高度计模块,芯片是MPL3115A2,I2C通讯接口的气压计模块。

到货后自己把排针焊上,准备测试。

之前已经在用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)函数,其中0x26为MPL芯片的工作模式配置寄存器地址。
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文件),需要对比

          ldscripts:FLASH配置文件,两个工程一致

          src:工程源文件(.c文件),需要对比

后面四个是工程相关文件,无需比较。

通过对比,可以清晰的看到哪些文件时一样的,哪些文件不一样。双击不一样的文件,可打开查看不一样的内容。

左边的目录多了cycle_buffer.c和cycle_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模块。

目前已经定位到了问题点,至于具体引发该问题的原因,还需要继续详细寻找和确认,我暂时还没有解决。

有经验的技术大拿们有空可以指点一下迷津。
---------------------
作者:blust5
链接:https://bbs.21ic.com/icview-3300590-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐