发新帖本帖赏金 30.00元(功能说明)我要提问
12下一页
返回列表
打印
[单片机芯片]

移植1.3寸I2C的OLED屏STM32驱动到CH32V203

[复制链接]
1110|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 lilijin1995 于 2023-1-5 14:22 编辑

今天给大家分享的是移植1.3寸I2C的OLED屏STM32驱动到CH32V203的过程。
一、硬件资料:
    1.  1.3寸OLED屏
       这里我们用的是中景园1.3寸OLED屏,原理图如下图,其中I2C地址是0x78,因为是默认DC接GND,R11是焊接的,另外我们可以看到这款屏幕用的是SSH1106驱动的,原理图可以打开附件。

    2.  SH1106
       SH1106是一款单片CMOS OLED/PLED驱动器,带控制器,用于有机/聚合物发光二极管点阵图形显示系统。SH1106由132个分段、64个共用点组成,可支持132 x 64的最大显示分辨率。它是专为共阴极型OLED面板而设计的。SH1106内置对比度控制、显示RAM振荡器和高效Dc-Dc转换器,减少外部组件数量和功耗。SH1106适用于各种紧凑便携的应用,如手机的子显示器、计算器、MP3播放器等。规格书可以在附件中找到。   
      3. CH32V203评估板
       评估板是我们自己设计的,主要用于项目前期的评估测试,如下图:

二、软件移植
官方提供的例程是有基于STM32、STM8和51的,我们选择STM32F103C8T6的,如附件OLED.zip
    1.首先是OLED_Init,代码如下:
void OLED_Init(void) {

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);         //使能A端口时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                  //速度50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);          //初始化GPIOD3,6
    GPIO_SetBits(GPIOA, GPIO_Pin_5 | GPIO_Pin_7);

    Delay_Ms(800);
    OLED_WR_Byte(0xAE, OLED_CMD);          //--display off
    OLED_WR_Byte(0x02, OLED_CMD);          //---set low column address
    OLED_WR_Byte(0x10, OLED_CMD);          //---set high column address
    OLED_WR_Byte(0x40, OLED_CMD);          //--set start line address
    OLED_WR_Byte(0xB0, OLED_CMD);          //--set page address
    OLED_WR_Byte(0x81, OLED_CMD); // contract control
    OLED_WR_Byte(0xFF, OLED_CMD); //--128
    OLED_WR_Byte(0xA1, OLED_CMD); //set segment remap
    OLED_WR_Byte(0xA6, OLED_CMD); //--normal / reverse
    OLED_WR_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
    OLED_WR_Byte(0x3F, OLED_CMD); //--1/32 duty
    OLED_WR_Byte(0xC8, OLED_CMD); //Com scan direction
    OLED_WR_Byte(0xD3, OLED_CMD); //-set display offset
    OLED_WR_Byte(0x00, OLED_CMD); //

    OLED_WR_Byte(0xD5, OLED_CMD); //set osc division
    OLED_WR_Byte(0x80, OLED_CMD); //

    OLED_WR_Byte(0xD8, OLED_CMD); //set area color mode off
    OLED_WR_Byte(0x05, OLED_CMD); //

    OLED_WR_Byte(0xD9, OLED_CMD); //Set Pre-Charge Period
    OLED_WR_Byte(0xF1, OLED_CMD); //

    OLED_WR_Byte(0xDA, OLED_CMD); //set com pin configuartion
    OLED_WR_Byte(0x12, OLED_CMD); //

    OLED_WR_Byte(0xDB, OLED_CMD); //set Vcomh
    OLED_WR_Byte(0x30, OLED_CMD); //

    OLED_WR_Byte(0x8D, OLED_CMD); //set charge pump enable
    OLED_WR_Byte(0x14, OLED_CMD); //

    OLED_WR_Byte(0xAF, OLED_CMD); //--turn on oled panel
}
可以发现,这是通过IO口模拟的方式去实现的I2C,如果是一般情况IO口模拟的话,基本配置为开漏输出模式,但由于这里是驱动OLED,只需要写,不需要读取,所以这里配置推挽输出。命令注释已经很详细了,如果需要学习的话,直接看附件的      SH1106的规格书。      
          2.清屏
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!          
void OLED_Clear(void) {
    u8 i, n;
    for (i = 0; i < 8; i++) {
        OLED_WR_Byte(0xb0 + i, OLED_CMD);    //设置页地址(0~7)
        OLED_WR_Byte(0x02, OLED_CMD);      //设置显示位置—列低地址
        OLED_WR_Byte(0x10, OLED_CMD);      //设置显示位置—列高地址
        for (n = 0; n < 128; n++)
            OLED_WR_Byte(0, OLED_DATA);
    } //更新显示
}
清屏就是往芯片里面写0。

          3.OLED_WR_Byte的实现
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command) {
    IIC_Start();
    Write_IIC_Byte(0x78);            //Slave address,SA0=0
    IIC_Wait_Ack();
    Write_IIC_Byte(0x00);                        //write command
    IIC_Wait_Ack();
    Write_IIC_Byte(IIC_Command);
    IIC_Wait_Ack();
    IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data) {
    IIC_Start();
    Write_IIC_Byte(0x78);                        //D/C#=0; R/W#=0
    IIC_Wait_Ack();
    Write_IIC_Byte(0x40);                        //write data
    IIC_Wait_Ack();
    Write_IIC_Byte(IIC_Data);
    IIC_Wait_Ack();
    IIC_Stop();
}
void OLED_WR_Byte(unsigned dat, unsigned cmd) {
    if (cmd) {

        Write_IIC_Data(dat);

    } else {
        Write_IIC_Command(dat);

    }

}
上面是基本的写命令和数据的操作,是I2C的时序,I2C的时序图如下,

时序的要求也如下:


由于STM32和CH32的库函数名字基本一样的,所以我们基本不用修改太多。

         4.OLED_ShowCHinese显示中文函数的实现
void OLED_ShowCHinese(u8 x, u8 y, u8 no) {
    u8 t, adder = 0;
    OLED_Set_Pos(x, y);
    for (t = 0; t < 16; t++) {
        OLED_WR_Byte(Hzk[2 * no][t], OLED_DATA);
        adder += 1;
    }
    OLED_Set_Pos(x, y + 1);
    for (t = 0; t < 16; t++) {
        OLED_WR_Byte(Hzk[2 * no + 1][t], OLED_DATA);
        adder += 1;
    }
}
其中,x和y是屏幕的坐标,而no应该是中文字的索引,
Hzk是中文字库,char Hzk[][32]例程没有加const,我们自己减少,因为家里const就是占用Flash空间,否则是SRAM空间。这个字库是通过取模软件生产的。
取模软件可以用PCtoLCD2002,它可以生成图片和中文字库。如果有条件的话,现在市面上也有自带中文库的OLED屏,或者可以自己加字库芯片。               
        5.OLED_ShowString显示字符串函数的实现
//显示一个字符号串
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 Char_Size) {
    unsigned char j = 0;
    while (chr[j]!='\0')
    {   OLED_ShowChar(x,y,chr[j],Char_Size);
        x+=8;
        if(x>120) {x=0;y+=2;}
        j++;
    }
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示                                 
//size:选择字体 16/12
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 Char_Size) {
    unsigned char c = 0, i = 0;
    c = chr - ' '; //得到偏移后的值
    if (x > Max_Column - 1) {
        x = 0;
        y = y + 2;
    }
    if (Char_Size == 16) {
        OLED_Set_Pos(x, y);
        for (i = 0; i < 8; i++)
            OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA);
        OLED_Set_Pos(x, y + 1);
        for (i = 0; i < 8; i++)
            OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA);
    } else {
        OLED_Set_Pos(x, y);
        for (i = 0; i < 6; i++)
            OLED_WR_Byte(F6x8[c][i], OLED_DATA);

    }
}
可以看到,这个函数提供了F8X16和F6x8两个不同点阵的ASCII码表,F6x8是6*8点阵的,F8X16是8*16点阵的,乘积越大,字符串字体越大。
        6.OLED_DrawBMP显示图片函数的实现
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1,
        unsigned char y1, unsigned char BMP[]) {
    unsigned int j = 0;
    unsigned char x, y;

    if (y1 % 8 == 0)
        y = y1 / 8;
    else
        y = y1 / 8 + 1;
    for (y = y0; y < y1; y++) {
        OLED_Set_Pos(x0, y);
        for (x = x0; x < x1; x++) {
            OLED_WR_Byte(BMP[j++], OLED_DATA);
        }
    }
}
图片我们的显示关键不是这个函数的实现,关键在于取模,图片库也是取模软件取的,例程是这样调用图片库显示的
OLED_DrawBMP(0,0,128,8,BMP1);其中unsigned char BMP1[] ,BMP1是普通变量,这将会占用非常多的SRAM,SRAM非常小,所以不建议这样,我们可以改一下,将其定义为只读的,const unsigned char BMP1[],调用时候强制转换一下:OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);

最后在main函数中实现的中文、字符串、图片显示如下:
int main(void) {
    u8 t;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n", SystemCoreClock);
    printf("This is printf example\r\n");

    OLED_Init();            //初始化OLED
    OLED_Clear();
    t=' ';
    OLED_ShowCHinese(0, 0, 0);             //中
    OLED_ShowCHinese(18, 0, 1);            //景
    OLED_ShowCHinese(36, 0, 2);            //园
    OLED_ShowCHinese(54, 0, 3);            //电
    OLED_ShowCHinese(72, 0, 4);            //子
    OLED_ShowCHinese(90, 0, 5);            //科
    OLED_ShowCHinese(108, 0, 6);            //技
    while(1)
    {
        OLED_Clear();
        OLED_ShowCHinese(0,0,0);            //中
        OLED_ShowCHinese(18,0,1);           //景
        OLED_ShowCHinese(36,0,2);           //园
        OLED_ShowCHinese(54,0,3);           //电
        OLED_ShowCHinese(72,0,4);           //子
        OLED_ShowCHinese(90,0,5);           //科
        OLED_ShowCHinese(108,0,6);          //技
        OLED_ShowString(6,3,"1.30' OLED TEST",16);
        //OLED_ShowString(8,2,"ZHONGJINGYUAN");
        // OLED_ShowString(20,4,"2014/05/01");
        OLED_ShowString(0,6,"ASCII:",16);
        OLED_ShowString(63,6,"CODE:",16);
        OLED_ShowChar(48,6,t,16);//显示ASCII字符
        t++;
        if(t>'~')t=' ';
        OLED_ShowNum(103,6,t,3,16);//显示ASCII字符的码值
        Delay_Ms(1000);
        OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
        Delay_Ms(1000);
    }
}
Delay_Ms是CH32中实现的,所以需要修改一下。


最终实现的效果图如下:





中景园电子1.3OLED-4PIN显示屏IIC接口原理图.pdf

62.42 KB

1.3寸竖屏横屏-控制芯片SH1106手册.pdf

1.47 MB

OLED.zip

1.83 MB

使用特权

评论回复

打赏榜单

21ic小管家 打赏了 30.00 元 2023-02-08
理由:签约作者奖励

沙发
geraldbetty| | 2023-1-9 11:22 | 只看该作者
这个使用宏定义很简单。              

使用特权

评论回复
板凳
biechedan| | 2023-1-9 11:30 | 只看该作者
这个彩色oled不好用的吗?              

使用特权

评论回复
地板
jackcat| | 2023-1-9 12:00 | 只看该作者
CH32V203和stm32很兼容的。

使用特权

评论回复
5
qiufengsd| | 2023-1-9 13:30 | 只看该作者
可以使用硬件spi接口吗?              

使用特权

评论回复
6
mickit| | 2023-1-9 15:29 | 只看该作者
怎么实现汉字和英文 的混合显示?

使用特权

评论回复
7
gygp| | 2023-1-9 16:47 | 只看该作者
这个的驱动芯片是哪个?              

使用特权

评论回复
8
chenjun89| | 2023-1-9 19:14 | 只看该作者
哈哈,中景元的OLED屏,我手上很多个。

使用特权

评论回复
9
pmp| | 2023-1-10 19:29 | 只看该作者
能不能把arduin的库移植过来。

使用特权

评论回复
10
macpherson| | 2023-2-4 12:25 | 只看该作者
oled的刷屏速度是多少?              

使用特权

评论回复
11
zerorobert| | 2023-2-4 15:11 | 只看该作者
ips屏幕的显示效果也非常不错的。

使用特权

评论回复
12
ulystronglll| | 2023-2-4 16:21 | 只看该作者
使用的硬件的iic吗?              

使用特权

评论回复
13
qiufengsd| | 2023-2-4 19:44 | 只看该作者
oled的iic的驱动速度是多少?

使用特权

评论回复
14
bestwell| | 2023-2-4 20:43 | 只看该作者
可以移植gui过来的吗?              

使用特权

评论回复
15
yeates333| | 2023-2-4 21:23 | 只看该作者
这个屏幕驱动还是比较简单的。              

使用特权

评论回复
16
wilhelmina2| | 2023-2-4 22:53 | 只看该作者
这个代码基本上就是兼容的。              

使用特权

评论回复
17
tpgf| | 2023-2-6 11:17 | 只看该作者
在时序上需要进行细微的调节吗

使用特权

评论回复
18
lilijin1995|  楼主 | 2023-2-6 11:40 | 只看该作者
tpgf 发表于 2023-2-6 11:17
在时序上需要进行细微的调节吗

在要求的范围内,我直接144主频上跑,没调

使用特权

评论回复
19
木木guainv| | 2023-2-6 11:58 | 只看该作者
请问控制这个屏幕使用的是at指令吗

使用特权

评论回复
20
磨砂| | 2023-2-6 12:06 | 只看该作者
感觉主要的部分都不需要改动 稍微变动一下配置就行了

使用特权

评论回复
发新帖 本帖赏金 30.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

54

主题

162

帖子

4

粉丝