freeelectron 发表于 2022-7-13 19:58

【N32L43x评测】6、软硬件I2C驱动0.96吋OLED

本帖最后由 freeelectron 于 2022-7-13 20:02 编辑

<p>本文使用软硬件I2C驱动0.96吋OLED,先上一张图:</p>

<p></p>

<p><span style="font-size:22px;">1、关于N32L43x的I2C接口</span></p>

<p>&nbsp;两路i2c,本文使用i2c1。</p>

<p>&nbsp;</p>

<p><span style="font-size:24px;">2、开发板上I2C的引脚</span></p>

<p> <span style="font-size:24px;">&nbsp;3、通用引脚复用为I2C</span></p>

<p> &nbsp;&nbsp;</p>

<p><span style="font-size:24px;">4、代码实现</span></p>

<p><span style="font-size:18px;">(1)硬件i2c</span></p>

<pre>
<code>void I2cInit(void)
{
    I2C_InitType i2c1_master;
    GPIO_InitType i2c1_gpio;
       
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C1, ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);

    /*PB8 -- SCL; PB9 -- SDA*/
    GPIO_InitStruct(&amp;i2c1_gpio);
    i2c1_gpio.Pin               = GPIO_PIN_8 | GPIO_PIN_9;
    i2c1_gpio.GPIO_Slew_Rate    = GPIO_Slew_Rate_High;
    i2c1_gpio.GPIO_Mode         = GPIO_Mode_AF_OD;
    i2c1_gpio.GPIO_Alternate    = GPIO_AF4_I2C1;
    i2c1_gpio.GPIO_Pull         = GPIO_Pull_Up;          
    GPIO_InitPeripheral(GPIOB, &amp;i2c1_gpio);

    I2C_DeInit(I2C1);
    i2c1_master.BusMode   = I2C_BUSMODE_I2C;
    i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
    i2c1_master.OwnAddr1    = I2C_OWN_ADDRESS7;
    i2c1_master.AckEnable   = I2C_ACKEN;
    i2c1_master.AddrMode    = I2C_ADDR_MODE_7BIT;
    i2c1_master.ClkSpeed    = 100000; // 100K

    I2C_Init(I2C1, &amp;i2c1_master);
    I2C_Enable(I2C1, ENABLE);
}


void OledWriteCmd(uint8_t var)
{
    while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY));
       
    I2C_GenerateStart(I2C1, ENABLE);
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)); // EV5
       
    I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDRESS7, I2C_DIRECTION_SEND);
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG));// EV6
               
    I2C_SendData(I2C1, 0x00);
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8

        I2C_SendData(I2C1, var);
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8
       
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED));// EV8-2
             
        I2C_GenerateStop(I2C1, ENABLE);

}


void OledWriteData(uint8_t var)
{
    while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY));
       
    I2C_GenerateStart(I2C1, ENABLE);
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)); // EV5
       
    I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDRESS7, I2C_DIRECTION_SEND);
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG));// EV6
               
    I2C_SendData(I2C1, 0x40);
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8

        I2C_SendData(I2C1, var);
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8
       
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED));// EV8-2
             
        I2C_GenerateStop(I2C1, ENABLE);       
}
</code></pre>

<p><span style="font-size:18px;">(2)软件i2c</span></p>

<pre>
<code>#define _SCL_PORTGPIOB
#define _SCL_PIN   GPIO_PIN_8

#define _SDA_PORTGPIOB
#define _SDA_PIN   GPIO_PIN_9



void _I2C_Init(void)
{
        //PB8:SCLPB9:SDA
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
       
        GPIO_InitType i2c1_gpio;
       
        GPIO_InitStruct(&amp;i2c1_gpio);
    i2c1_gpio.Pin               = GPIO_PIN_8 | GPIO_PIN_9;
       
        i2c1_gpio.GPIO_Current      = GPIO_DC_12mA;
    i2c1_gpio.GPIO_Slew_Rate    = GPIO_Slew_Rate_High;
    i2c1_gpio.GPIO_Mode         = GPIO_Mode_Out_PP;
    i2c1_gpio.GPIO_Pull         = GPIO_Pull_Up;          
       
    GPIO_InitPeripheral(GPIOB, &amp;i2c1_gpio);
}


void _SDA_IN(void)
{
        GPIO_InitType i2c1_gpio;

        GPIO_InitStruct(&amp;i2c1_gpio);
    i2c1_gpio.Pin               = GPIO_PIN_9;
       
        i2c1_gpio.GPIO_Current      = GPIO_DC_12mA;
    i2c1_gpio.GPIO_Slew_Rate    = GPIO_Slew_Rate_High;
    i2c1_gpio.GPIO_Mode         = GPIO_Mode_Input;
    i2c1_gpio.GPIO_Pull         = GPIO_Pull_Up;          
       
    GPIO_InitPeripheral(GPIOB, &amp;i2c1_gpio);
}


void _SDA_OUT(void)
{
        GPIO_InitType i2c1_gpio;

        GPIO_InitStruct(&amp;i2c1_gpio);
    i2c1_gpio.Pin               =   GPIO_PIN_9;
       
        i2c1_gpio.GPIO_Current      = GPIO_DC_12mA;
    i2c1_gpio.GPIO_Slew_Rate    = GPIO_Slew_Rate_High;
    i2c1_gpio.GPIO_Mode         = GPIO_Mode_Out_PP;
    i2c1_gpio.GPIO_Pull         = GPIO_Pull_Up;          
       
    GPIO_InitPeripheral(GPIOB, &amp;i2c1_gpio);
       
}

void _I2C_Start(void)
{
        _SDA_OUT();
        GPIO_SetBits(_SDA_PORT,_SDA_PIN);//SDA=1
        DelayUs(10);
        GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
        DelayUs(10);
        GPIO_ResetBits(_SDA_PORT,_SDA_PIN);//SDA=0
        DelayUs(10);
        GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0
        DelayUs(10);
}

void _I2C_Stop(void)
{
        _SDA_OUT();
       
        GPIO_ResetBits(_SDA_PORT,_SDA_PIN);//SDA=0
        DelayUs(10);
        GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
        DelayUs(10);
        GPIO_SetBits(_SDA_PORT,_SDA_PIN);//SDA=1
        DelayUs(10);
}


void _I2C_Ack(void)
{
        _SDA_OUT();
        GPIO_ResetBits(_SDA_PORT,_SDA_PIN);//SDA=0
        DelayUs(5);
        GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
        DelayUs(5);
        GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
}


void _I2C_NAck(void)
{
        GPIO_SetBits(_SDA_PORT,_SDA_PIN);//SDA=1
    DelayUs(10);
    GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
    DelayUs(10);
    GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0
        DelayUs(10);       
}


uint8_t _I2C_Wait_Ack(void)
{
        uint8_t ucErrTime=0;

#if 0       
        SDA_IN();
       GPIO_SetBits(_SDA_PORT,_SDA_PIN);//释放总线
#else       
        GPIO_SetBits(_SDA_PORT,_SDA_PIN);//释放总线
       _SDA_IN();
#endif       

        DelayUs(5);
        GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
        DelayUs(5);
        while(GPIO_ReadInputDataBit(_SDA_PORT,_SDA_PIN))
        {
                ucErrTime++;
                if(ucErrTime&gt;250)
                {
                        _I2C_Stop();
                        return 1;
                }
        }
        GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0
        DelayUs(5);
        return 0;
}


uint8_t _I2C_Read_Byte(uint8_t ack)
{
        uint8_t i,rxdata=0;

#if 0       
        _SDA_IN();
        GPIO_SetBits(_SDA_PORT,_SDA_PIN);//释放总线
#else       
        GPIO_SetBits(_SDA_PORT,_SDA_PIN);//释放总线
        _SDA_IN();
#endif       

        for(i=0;i&lt;8;i++ )
        {
                GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0
                DelayUs(5);
                GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
                DelayUs(5);
                rxdata&lt;&lt;=1;
                if(GPIO_ReadInputDataBit(_SDA_PORT,_SDA_PIN))
                {
                        rxdata|=0x01;
                }
                DelayUs(5);
        }
    if (!ack)
                _I2C_NAck();//nACK
    else
                _I2C_Ack(); //ACK
               
    return rxdata;
}

void _I2C_Send_Byte(uint8_t txd)
{
        uint8_t i;
       
        _SDA_OUT();
        GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0

        for(i=0;i&lt;8;i++)
        {
                if((txd&amp;0x80)==0x80)
                        GPIO_SetBits(_SDA_PORT,_SDA_PIN);
                else
                        GPIO_ResetBits(_SDA_PORT,_SDA_PIN);
                               
                txd&lt;&lt;=1;
                DelayUs(5);
                GPIO_SetBits(_SCL_PORT,_SCL_PIN);//SCL=1
                DelayUs(5);
                GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0
                DelayUs(5);
        }
}


void _SSD1306_WriteCmd(uint8_t var)
{
        _I2C_Start();
        _I2C_Send_Byte(I2C_SLAVE_ADDRESS7); //write addr
        _I2C_Wait_Ack();
        _I2C_Send_Byte(0x00);
        _I2C_Wait_Ack();
        _I2C_Send_Byte(var);
        _I2C_Wait_Ack();
        _I2C_Stop();
}


void _SSD1306_WriteData(uint8_t var)
{
        _I2C_Start();
        _I2C_Send_Byte(I2C_SLAVE_ADDRESS7); //write addr
        _I2C_Wait_Ack();
        _I2C_Send_Byte(0x40);
        _I2C_Wait_Ack();
        _I2C_Send_Byte(var);
        _I2C_Wait_Ack();
        _I2C_Stop();
}</code></pre>

<p><span style="font-size:18px;">(3)OLED驱动相关</span></p>

<pre>
<code>//SSD1306初始化
void OledInit(void)
{
DelayMs(300);
DelayMs(300);

//SSD1306复位之后,默认的是页寻址方式

SSD1306_WriteCmd(0xAE);//--display off

SSD1306_WriteCmd(0x00);//--set low column address
SSD1306_WriteCmd(0x10);//--set high column address
SSD1306_WriteCmd(0x40);//--set start line address

SSD1306_WriteCmd(0xB0);//--set page address

SSD1306_WriteCmd(0x81);// contract control
SSD1306_WriteCmd(0xFF);//--128   
SSD1306_WriteCmd(0xA1);//set segment re-map0 to 127
SSD1306_WriteCmd(0xA6);//set normaldisplay
SSD1306_WriteCmd(0xA8);//set multiplex ratio(1 to 64)
SSD1306_WriteCmd(0x3F);//--1/32 duty

SSD1306_WriteCmd(0xC8);//Com scan direction
SSD1306_WriteCmd(0xD3);//set display offset
SSD1306_WriteCmd(0x00);//no offset
       
SSD1306_WriteCmd(0xD5);//set display clock divide ratio/oscillator frequency
SSD1306_WriteCmd(0x80);//
       
SSD1306_WriteCmd(0xD8);//set area color mode off
SSD1306_WriteCmd(0x05);//
       
SSD1306_WriteCmd(0xD9);//Set Pre-Charge Period
SSD1306_WriteCmd(0xF1);//
       
SSD1306_WriteCmd(0xDA);//set com pinhardware configuartion
SSD1306_WriteCmd(0x12);//
       
SSD1306_WriteCmd(0xDB);//set Vcomh
SSD1306_WriteCmd(0x30);//0x20,0.77xVcc
       
SSD1306_WriteCmd(0x8D);//set charge pump enable
SSD1306_WriteCmd(0x14);//
       
SSD1306_WriteCmd(0xAF);//--turn on Oled panel
}

//坐标设置:也就是在哪里显示
void OledSetPos(uint8_t x, uint8_t y)
{
//以下3个寄存器只在页寻址的模式下有效       
SSD1306_WriteCmd(0xb0+y);             //页地址设置   0xb0~0xb7
SSD1306_WriteCmd(((x&amp;0xf0)&gt;&gt;4)|0x10); //列高位地址设置
SSD1306_WriteCmd((x&amp;0x0f));         //列低位地址设置
}

//开启Oled显示
void OledDisplayOn(void)
{
SSD1306_WriteCmd(0X8D);//SET DCDC命令
SSD1306_WriteCmd(0X14);//DCDC ON
SSD1306_WriteCmd(0XAF);//DISPLAY ON
}

//关闭Oled显示   
void OledDisplayOff(void)
{
SSD1306_WriteCmd(0X8D);//SET DCDC命令
SSD1306_WriteCmd(0X10);//DCDC OFF
SSD1306_WriteCmd(0XAE);//DISPLAY OFF
}

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样
void OledClear(void)
{
uint8_t i,n;       

for(i=0;i&lt;8;i++)
{
    SSD1306_WriteCmd (0xb0+i);    //设置页地址(0~7)
    SSD1306_WriteCmd (0x00);      //设置显示位置&mdash;列低地址
    SSD1306_WriteCmd (0x10);      //设置显示位置&mdash;列高地址   
    for(n=0;n&lt;128;n++)
      SSD1306_WriteData(0);
} //更新显示
}

//在指定位置显示一个字符,包括部分字符
//x:0~127,y:0~7
//Char_Size:选择字体 16/12

////8*16的点阵,取模方式:阴码、逆向、列行式

void OledShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{             
uint8_t c=0,i=0;       

c=chr-&#39; &#39;;//得到偏移后的值                       
if(x&gt;MAX_COLUMN-1)
{
    x=0;
    y=y+2;
}
if(Char_Size ==16)
{
    for(i=0;i&lt;8;i++)
    {
          OledSetPos(x+i,y);       
      SSD1306_WriteData(Ascii_8x16<i>);//先写上半部分
    }
   
    for(i=0;i&lt;8;i++)
    {
          OledSetPos(x+i,y+1);
      SSD1306_WriteData(Ascii_8x16);//后写下半部分
    }
       
}
else
{       
    OledSetPos(x,y);
    for(i=0;i&lt;6;i++)
    {
      SSD1306_WriteData(Ascii_6x12<i>);
    }
}
}

//显示一个字符串
void OledShowString(uint8_t x,uint8_t y,char *str,uint8_t Char_Size)
{
unsigned char j=0;

while (str!=&#39;\0&#39;)
{
    OledShowChar(x,y,str,Char_Size);
    x+=8;
    if(x&gt;120)
    {
      x=0;
      y+=2;
    }
    j++;//移动一次就是一个page,取值0-7
}
}

//显示汉字
//由于汉字是16*16大小的,所以最多显示4行汉字
//index:在汉字取模中的索引
void OledShowCN(uint8_t x,uint8_t y,uint8_t index)
{                                
uint8_t t;
       
OledSetPos(x,y);       
for(t=0;t&lt;16;t++)
{
    SSD1306_WriteData(Hzk);
}       

OledSetPos(x,y+1);       
for(t=0;t&lt;16;t++)
{       
    SSD1306_WriteData(Hzk);
}                                       
}
</i></i></code></pre>

<p><i><i><span style="font-size:18px;">(4)OLED显示测试</span></i></i></p>

<pre>
<i><i>
<code>void OledDisplayTest(void)
{
#if SOFT_I2C
        _I2C_Init();       
#else       
        I2cInit();
#endif
       
        OledInit();
        OledClear();

        OledShowString(40,2,(char *)(&quot;Nation&quot;),16);
        OledShowString(40,4,(char *)(&quot;N32L43x&quot;),16);

        OledShowCN(24,6,0);
        OledShowCN(40,6,1);
        OledShowCN(56,6,2);
        OledShowCN(72,6,3);
}</code></i></i></pre>

<p><i><i><span style="font-size:24px;">5、现象</span></i></i></p>

<p><i><i></i></i></p>

freeelectron 发表于 2022-7-13 20:00

<p>上传完整代码和字库</p>

<p></p>

秦天qintian0303 发表于 2022-7-14 09:33

<p>是否有遇到ST的IIC通信卡死现象?国民科技的IIC过程是循环等待标志位的策略吗?</p>

freeelectron 发表于 2022-7-14 10:17

秦天qintian0303 发表于 2022-7-14 09:33
是否有遇到ST的IIC通信卡死现象?国民科技的IIC过程是循环等待标志位的策略吗?

<p>测试的时候是没有卡死的</p>

xld0932 发表于 2022-7-28 22:44

freeelectron 发表于 2022-7-14 10:17
测试的时候是没有卡死的

<p>明天参考楼主的程序测试一下,今天对照官方示例程序,I2C不稳定,会有卡住的情况;后面还专门给I2C引脚焊接了上拉电阻,也没有改善;同样的功能,使用其它MCU的硬件I2C就完全没有问题;问题还是在底层I2C这一块,上层的配置,应用没问题&hellip;&hellip;</p>

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan58.gif" width="54" /></p>

lugl4313820 发表于 2022-9-6 16:13

秦天qintian0303 发表于 2022-7-14 09:33
是否有遇到ST的IIC通信卡死现象?国民科技的IIC过程是循环等待标志位的策略吗?

<p>应该需要加入等超时的参数,楼主这里可能没有加进这个参数。</p>

秦天qintian0303 发表于 2022-9-6 17:16

lugl4313820 发表于 2022-9-6 16:13
应该需要加入等超时的参数,楼主这里可能没有加进这个参数。

<p>IIC的速度不开,感觉确实没有模拟IIC方便,细节修改会直观一点</p>

天若有情天亦老 发表于 2024-7-15 15:40

xld0932 发表于 2022-7-28 22:44
明天参考楼主的程序测试一下,今天对照官方示例程序,I2C不稳定,会有卡住的情况;后面还专门给I2C引脚焊 ...

<p>请问官方的示例程序是在什么地方下的</p>
页: [1]
查看完整版本: 【N32L43x评测】6、软硬件I2C驱动0.96吋OLED