freeelectron 发表于 2022-3-15 18:03

【BLE 5.3无线MCU CH582】5、硬件I2C驱动0.96吋OLED

<p>系列文章:</p>

<p><a href="https://bbs.eeworld.com.cn/thread-1195192-1-1.html" target="_blank">【BLE 5.3无线MCU CH582】1、初识CH582开发板(开箱)</a></p>

<p><a href="https://bbs.eeworld.com.cn/thread-1195464-1-1.html" target="_blank">【BLE 5.3无线MCU CH582】2、MounRiver IDE初体验</a></p>

<p><a href="https://bbs.eeworld.com.cn/thread-1196099-1-1.html#pid3126477" target="_blank">【BLE 5.3无线MCU CH582】3、非阻塞方式点灯</a></p>

<p><a href="https://bbs.eeworld.com.cn/thread-1196188-1-1.html" target="_blank">【BLE 5.3无线MCU CH582】4、串口不定长数据接收</a></p>

<p>&nbsp;</p>

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

<p>&nbsp;可以看出只有1个I2C接口。</p>

<p>&nbsp;</p>

<p><span style="font-size:20px;">2、I2C引脚</span></p>

<p>&nbsp;PB13:SCL,PB12:SDA。</p>

<p>&nbsp;</p>

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

<p> &nbsp;需要注意的是I2C与<span style="font-size:10.56pt"><span style="font-family:黑体"><span style="color:#000000">USB2共用引脚,同一时刻只能使用其中一个功能。</span></span></span></p>

<p> &nbsp;</p>

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

<p>(1)初始化i2c</p>

<pre>
<code class="language-cpp">void I2cInit(void)
{
    GPIOB_ModeCfg( GPIO_Pin_12|GPIO_Pin_13, GPIO_ModeIN_PU );
    I2C_Init( I2C_Mode_I2C, 400000, I2C_DutyCycle_2, I2C_Ack_Enable, I2C_AckAddr_7bit, 0x35 );

    I2C_Cmd(ENABLE);
}
</code></pre>

<p>(2)写OLED</p>

<p>下面分写命令和数据.</p>

<pre>
<code class="language-cpp">void OledWriteCmd(UINT8 var)
{
    while( I2C_GetFlagStatus( I2C_FLAG_BUSY ) != RESET );

    I2C_GenerateSTART( ENABLE );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT ) );
    I2C_Send7bitAddress( I2C_SLAVE_ADDRESS7, I2C_Direction_Transmitter );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=SET );
    I2C_SendData( 0x00 );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=SET );
    I2C_SendData( var );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
    I2C_GenerateSTOP( ENABLE );
}

void OledWriteData(UINT8 var)
{
    while( I2C_GetFlagStatus( I2C_FLAG_BUSY ) != RESET );

    I2C_GenerateSTART( ENABLE );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT ) );
    I2C_Send7bitAddress( I2C_SLAVE_ADDRESS7, I2C_Direction_Transmitter );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=SET );
    I2C_SendData( 0x40 );

    while( I2C_GetFlagStatus( I2C_FLAG_TXE ) !=SET );
    I2C_SendData( var );

    while( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
    I2C_GenerateSTOP( ENABLE );
}
</code></pre>

<ul>
</ul>

<p>(3)OLED相关驱动</p>

<pre>
<code class="language-cpp">//坐标设置:也就是在哪里显示
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);      //设置显示位置—列低地址
    SSD1306_WriteCmd (0x10);      //设置显示位置—列高地址   
    for(n=0;n&lt;128;n++)
      SSD1306_WriteData(0);
} //更新显示
}

//在指定位置显示一个字符,包括部分字符
//x:0~127,y:0~7
//Char_Size:选择字体 16/12
void OledShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{             
uint8_t c=0,i=0;       

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

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

while (str!='\0')
{
    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);
}                                       
}</code></pre>

<p>(4)OLED初始化</p>

<pre>
<code class="language-cpp">//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
}
</code></pre>

<p>&nbsp;(5)显示测试</p>

<pre>
<code class="language-cpp">void OledDisplayTest(void)
{
        I2cInit();
        OledInit();
       
        OledClear();

        OledShowString(20,0,(char *)("EEWORLD"),16);

        OledShowCN(84,0,0);
        OledShowCN(100,0,1);

    OledShowString(32,3,(char *)("CH582"),16);

        OledShowCN(80,3,2);
        OledShowCN(96,3,3);

    OledShowString(40,6,(char *)("2022-03-15"),16);
}</code></pre>

<p>&nbsp;(6)一些定义</p>

<pre>
<code>#define I2C_SLAVE_ADDRESS7   0x78//0x3c,0x3d

#define         MAX_COLUMN          128
#define         MAX_ROW                  64

#defineSSD1306_WriteCmd   OledWriteCmd   
#defineSSD1306_WriteData    OledWriteData</code></pre>

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

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<ul>
</ul>

lugl4313820 发表于 2022-3-15 18:22

写得非常好,学习了,我原来是用软件i2c做的

freeelectron 发表于 2022-3-15 19:25

lugl4313820 发表于 2022-3-15 18:22
写得非常好,学习了,我原来是用软件i2c做的

<p>软件的也不错,殊途同归</p>

freebsder 发表于 2022-3-15 23:08

<p>还是比较接近底层的封装,现在很多spi,i2c,uart的封装一来就是缓冲,链表,看着就头疼</p>

freeelectron 发表于 2022-3-16 08:43

freebsder 发表于 2022-3-15 23:08
还是比较接近底层的封装,现在很多spi,i2c,uart的封装一来就是缓冲,链表,看着就头疼

<p>那种可能是为了统一接口吧,但是作为单片机来讲,意义不大。</p>

yaoquan5201314 发表于 2022-4-17 23:20

<p>写的挺好的,MARK一下,等有空了也试一下硬件I2C驱动OLED屏。</p>

daoyeah 发表于 2024-9-17 21:04

<p>老大可否上个工程源码,文中代码在582F用ssd130612832没有动静</p>
页: [1]
查看完整版本: 【BLE 5.3无线MCU CH582】5、硬件I2C驱动0.96吋OLED