[PIC?/AVR?/dsPIC?产品]如何解决PIC单片机的硬件I2C在驱动OLED显示屏时候的不好用问题

网友反应在硬件时钟设置较高的时候驱动SSD1306控制器的OLED模块时候无法工作,同样在设置I2C时钟为高速模式时候(1MHz,实际配置为800KHz)也无法工作。
那么这是什么情况呢?
经过我的实验总结如下:
SSD1306通常仅支持100Khz的低速模式和400KHz的中速模式。
查询相关资料内容:SSD1306 i2c的最大速度取决于具体的硬件和软件实现。 一般来说,它支持标准模式(100 kHz)和快速模式(400 kHz)的I2C通信。
所以设置为高速模式无法正常工作。
这是第一个解决的问题,即使用OLED模块时候,硬件I2C的速度推荐设置为400KHz。

接下面分析为何MCU系统时钟设置为高速的时候无法正常工作。
经过阅读MCC的源代码,I2C的通信为非阻塞通信,即不会卡顿在某处等待,如果不满足条件就会跳过去。所以如果系统时钟速度设置过高,导致在I2C从机还没来得及反应的时候就执行完毕了,这个时候总线繁忙,会自动跳过。相关库函数代码如下:

bool I2C1_Write(uint16_t address, uint8_t *data, size_t dataLength)

{

    bool retStatus = false;

    if (!I2C1_IsBusy())

    {

        i2c1Status.busy = true;

        i2c1Status.address = address;

        i2c1Status.switchToRead = false;

        i2c1Status.writePtr = data;

        i2c1Status.writeLength = dataLength;

        i2c1Status.readPtr = NULL;

        i2c1Status.readLength = 0;

        i2c1Status.errorState = I2C_ERROR_NONE;

        I2C1_WriteStart();

        retStatus = true;

    }

    return retStatus;

}

如何解决这个不同步问题呢?修改库函数?NO,我是不主张修改的,因为非阻塞可以防止整个系统卡死在某处。
经过尝试可以在每次调用完该函数后进行一定时间的延时。
经过测试,在系统时钟设置为最高速度64MHz的情况下,每次调用I2C写函数后延时50个系统周期可以正常通信,当低于49后开始不稳定。

void OLED_Write_cmd(uint8_t cmd)

{

    uint8_t cmd2[2];

    cmd2[0] = 0x00;

    cmd2[1] = cmd;

    I2C1_Write(0x3C, cmd2, 2);

    DELAY_microseconds(50);

}

void OLED_Write_data(uint8_t data)

{

    uint8_t data2[2];

    data2[0] = 0x40;

    data2[1] = data;

    I2C1_Write(0x3C, data2, 2);

    DELAY_microseconds(50);

}

这里推荐在64Mhz时钟下延时50,该函数并非是50us经过测试该函数参数对应位System Clock的时钟周期数。
8MHz系统时钟下,设置延时参数为1
16MHz系统时钟下,设置延时参数为6
其他时钟下,请参考以上范围调整到最小合适值即可。
总结:很多人说硬件I2C不好用,其实不是不好用,是很多时候没有能严格按照通信协议规范设置相关参数,比如常用的这个OLED模块,经常用于一些显示,比如文中提到的这个单片机,如果你没能很好的设置相关参数,并对时钟进行同步的延时操作,就无法正常高速点亮屏幕,你会以为是硬件的I2C不好用,其实不是。该文相信可以为很多用I2C硬件不好用的朋友提供一份参考,相信以后的项目中硬件I2C越来越流行。
---------------------
作者:gaoyang9992006
链接:https://bbs.21ic.com/icview-3353486-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。