有些时候,在移植代码的时候,总是一个一个模块的测试是否成功,本来这个思想也很好的,这样如果出现问题可以更方便快捷地找出问题来;但是这种分而治之的思想有些时候可能会狠狠坑我们一把!
有些时候,单独在一个模块上测试能够成功,但是移植成一个大的项目可能就会出错;将所有模块初始化完成,然后在while(1)循环里测试各个模块的功能,慢慢搭建框架;但是,这时候发现单独测试的时候能够成功的代码,在这里总会出现莫名其妙的bug,仔细检查函数配置成功了且正确,逻辑明确,反复检查也没发现错误,这个时候就有点让人灰心了。
最后,我一步步调试代码,调试啊调试(因为初始化的模块太多了),终于我发现咦,这里到oled初始化这里就不走了?刚刚还好好的啊?
于是我打开定义,刨根问底,发现了这个函数
//向OLED写入一个字节
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
//开启I2C1,产生起始信号
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
/*EV5,主模式*/
//EV5事件被检测到,发送设备地址-- 默认0x78
I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
//EV6事件被检测到,发送要操作的存储单元地址
I2C_SendData(I2C1, addr);//寄存器地址
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
//EV8事件被检测到,发送要存储的数据
I2C_SendData(I2C1, data);//发送数据
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
}
OLED写入一个字节数据过程
这是I2C方式写入数据方式,可以看到里面有很多循环等待,如果写数据不成功、没有应答等,程序就会一直卡死到这里。
因为这个OLED模块是基于I2C通讯方式,我找了另外一些关于OLED写数据的函数
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
// SDA_IN();
IIC_SDA=1;
delay_us(1);
IIC_SCL=1;
delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
可以看到基于I2C的通信方式的特殊性,在发送完一个数据后,程序要等待应答,在这里,使用while()循环等待应答。
在这里我没有接oled模块,但是在while循环中初始化了oled模块,并且写入了一些数据,但是因为没有硬件设备,所以发送不成功(收不到应答信号),所以出现程序卡死的问题。
在发现这个问题后,前段时间遇到的一些奇奇怪怪的错误也就解释得通了,恍然大悟的感觉真好,写下这个博客分享一下我的快乐。
如果你的程序检查逻辑之后没有问题,配置也还ok,看看是不是没有接一些已经初始化的硬件模块(特别是需要通信的),希望我的意见能帮助到你!!!