lilijin1995 发表于 2023-1-5 11:21

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

本帖最后由 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, OLED_DATA);
      adder += 1;
    }
    OLED_Set_Pos(x, y + 1);
    for (t = 0; t < 16; t++) {
      OLED_WR_Byte(Hzk, OLED_DATA);
      adder += 1;
    }
}其中,x和y是屏幕的坐标,而no应该是中文字的索引,Hzk是中文字库,char Hzk[]例程没有加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!='\0')
    {   OLED_ShowChar(x,y,chr,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, OLED_DATA);
      OLED_Set_Pos(x, y + 1);
      for (i = 0; i < 8; i++)
            OLED_WR_Byte(F8X16, OLED_DATA);
    } else {
      OLED_Set_Pos(x, y);
      for (i = 0; i < 6; i++)
            OLED_WR_Byte(F6x8, 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, 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中实现的,所以需要修改一下。


最终实现的效果图如下:





geraldbetty 发表于 2023-1-9 11:22

这个使用宏定义很简单。            

biechedan 发表于 2023-1-9 11:30

这个彩色oled不好用的吗?            

jackcat 发表于 2023-1-9 12:00

CH32V203和stm32很兼容的。

qiufengsd 发表于 2023-1-9 13:30

可以使用硬件spi接口吗?            

mickit 发表于 2023-1-9 15:29

怎么实现汉字和英文 的混合显示?

gygp 发表于 2023-1-9 16:47

这个的驱动芯片是哪个?            

chenjun89 发表于 2023-1-9 19:14

哈哈,中景元的OLED屏,我手上很多个。

pmp 发表于 2023-1-10 19:29

能不能把arduin的库移植过来。

macpherson 发表于 2023-2-4 12:25

oled的刷屏速度是多少?            

zerorobert 发表于 2023-2-4 15:11

ips屏幕的显示效果也非常不错的。

ulystronglll 发表于 2023-2-4 16:21

使用的硬件的iic吗?            

qiufengsd 发表于 2023-2-4 19:44

oled的iic的驱动速度是多少?

bestwell 发表于 2023-2-4 20:43

可以移植gui过来的吗?            

yeates333 发表于 2023-2-4 21:23

这个屏幕驱动还是比较简单的。            

wilhelmina2 发表于 2023-2-4 22:53

这个代码基本上就是兼容的。            

tpgf 发表于 2023-2-6 11:17

在时序上需要进行细微的调节吗

lilijin1995 发表于 2023-2-6 11:40

tpgf 发表于 2023-2-6 11:17
在时序上需要进行细微的调节吗

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

木木guainv 发表于 2023-2-6 11:58

请问控制这个屏幕使用的是at指令吗

磨砂 发表于 2023-2-6 12:06

感觉主要的部分都不需要改动 稍微变动一下配置就行了
页: [1] 2
查看完整版本: 移植1.3寸I2C的OLED屏STM32驱动到CH32V203