#申请原创# @21小跑堂 @21小跑堂
最近一个项目需要测量海拔高度,在淘宝上买了一个气压高度计模块,芯片是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模块。 目前已经定位到了问题点,至于具体引发该问题的原因,还需要继续详细寻找和确认,我暂时还没有解决。 有经验的技术大拿们有空可以指点一下迷津。
|
以开发板的例程为依托,移植IIC模块,对出现的问题严谨分析,逐步排查
问题已经解决,详见 https://bbs.21ic.com/icview-3300846-1-1.html?fromuser=blust5