吃透OLED显示原理——玩转OLED模块各种使用方法

吃透OLED显示原理——玩转OLED模块各种使用方法 我是白开水呢 2024-01-26 14:50:33 413

oled模块有4种工作模式,分别是6800、8080两种并行接口方式、 4线的穿行SPI接口方式、IIC接口方式。通过模块的BS1/BS2设置(通过硬件来设置),BS1/BS2的设置与模块接口模式的关系如表所示:

这是其中一种工作方式的模块,如图:

ALIENTEK OLED模块默认设置是BS0接GND,BS1和BS2接VCC(8080模式),如果想要设置成其他的模式,则需要在OLED的背面,用烙铁修改BS0-BS2的设置。(硬件改动)

从原理图中我们可以知道,引出来总共有16个管脚,在16条线中,我们只用了15条,有一个是悬空的。15条线中,电源和地线占了2条,还剩下13条信号线。在不同模式下,我们需要的信号线数量是不同的,在8080模式下,需要全部13条,这其中有一条是共同的,那就是复位线RST(RES),该线我们可以直接接在MCU的复位上(要先确认复位方式一样),这样可以省掉一条线。 而在IIC模式下,仅需要2条线就够了!

8线并行工作模式这里不做介绍,大家可以去参考一下这篇文章:【常用模块】OLED显示模块(原理讲解、STM32实例操作)

刚才我们就了解到。iic的工作模式只需要两条线,如图:

iic的工作模式就是用两根线来模拟iic得到数据,如图:

//-----------------OLED IIC端口定义----------------                         

#define OLED_SCLK_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_5)//SDA IIC接口的时钟信号
#define OLED_SCLK_Set() GPIO_SetBits(GPIOA,GPIO_Pin_5)

#define OLED_SDIN_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_7)//SCL IIC接口的数据信号
#define OLED_SDIN_Set() GPIO_SetBits(GPIOA,GPIO_Pin_7)

对于模拟iic,大家可以参考一下这篇文章,超详细:【STM32】IIC的基本原理

OLED控制器为SSD1306,也就是说:裸屏由SSD1306驱动,这也是一种较为广泛使用的led驱动芯片。

OLED模块显存

OLED本身是没有显存的,它的显存是依赖于SSD1306提供的。SSD1306的显存总共为128 64bit大小,SSD1306将这些显存分为了8页。每页包含了128个字节,总共8页,这样刚好是12864的点阵大小。

但是由于OLED不能一次控制一个点阵,只能控制8个点阵;而且是垂直方向扫描控制;如下图;因此垂直方向坐标可选为0 ~~ 7;(8*8=64);水平方向可选坐标0~127.

因为每次写入都是按字节写入的,这就存在一个问题,如果我们使用只写方式操作模块,那么,每次要写8个点,这样,我们在画点的时候,就必须把要设置的点所在的字节的每个位都搞清楚当前的状态(0/1?),否则写入的数据就会覆盖掉之前的状态,结果就是有些不需要显示的点,显示出来了,或者该显示的没有显示了。这个问题在能读的模式下,我们可以先读出来要写入的那个字节,得到当前状况,在修改了要改写的位之后再写进GRAM,这样就不会影响到之前的状况了。但是这样需要能读GRAM,对于3线或4线SPI模式,模块是不支持读的,而且读->改->写的方式速度也比较慢。

所以我们采用的办法是在STM32的内部建立一个OLED的GRAM(共128个字节),在每次修改的时候,只是修改STM32上的GRAM(实际上就是SRAM),在修改完了之后,一次性把STM32上的GRAM写入到OLED的GRAM。当然这个方法也有坏处,就是对于那些SRAM很小的单片机(比如51系列)就比较麻烦了。

//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127    
//[1]0 1 2 3 ... 127    
//[2]0 1 2 3 ... 127    
//[3]0 1 2 3 ... 127    
//[4]0 1 2 3 ... 127    
//[5]0 1 2 3 ... 127    
//[6]0 1 2 3 ... 127    
//[7]0 1 2 3 ... 127                
u16 OLED_GRAM[128][8];     

//更新显存到LCD         
void OLED_Refresh_Gram(void)
{
    u8 i,n;            
    for(i=0;i<8;i++)  
    {  
        OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
        OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
        OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
        for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA); 
    }   
}

SSD1306命令

1:命令0X81:设置对比度。包含两个字节,第一个0X81为命令,随后发送的一个字节为要设置的对比度的值。这个值设置得越大屏幕就越亮。
2:命令0XAE/0XAF:0XAE为关闭显示命令;0XAF为开启显示命令。
3:命令0X8D:包含2个字节,第一个为命令字,第二个为设置值,第二个字节的BIT2表示电荷泵的开关状态,该位为1,则开启电荷泵,为0则关闭。在模块初始化的时候,这个必须要开启,否则是看不到屏幕显示的。
4:命令0XB0~B7:用于设置页地址,其低三位的值对应着GRAM的页地址。
5:命令0X00~0X0F:用于设置显示时的起始列地址低四位。
6:命令0X10~0X1F:用于设置显示时的起始列地址高四位。

更多的命令请参考这个,强烈建议去看,很详细:SSD1306(OLED驱动芯片)指令详解

介绍完工作模式以及驱动芯片,我们可以开始使用这一款iic oled模块

代码详解

#define OLED_CMD  0    //写命令
#define OLED_DATA 1    //写数据

对oled进行初始化

//初始化SSD1306                        
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);      //初始化GPIO
     GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7);    

    delay_ms(200);

    OLED_WR_Byte(0xAE,OLED_CMD);//--display off
    OLED_WR_Byte(0x00,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
}

OLED_Clear(); //清屏,每次初始化完成后建议先清理屏幕

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!      
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 (0x00,OLED_CMD);      //设置显示位置—列低地址
        OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
        for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); 
    } //更新显示
}

在我们平时使用的自模中,有两种常见的取模方式,一个是 6 8,另一个则是8 16的,第一个说的是在8行6列的矩形表格中取出我们想要的字符,第二个则是在16行8列的矩形表格中取出字符。正如下面代码注释中写的一样,因为oled中每一页只有8个行,所以就需要使用下一页的空间。所以就有了我们平时使用选择的字体大小,当然,这些都是常用的字体大小,我们也可以自己通过字符取模软件制作自己喜欢的字体大小。关于字符取模软件,将在后面讲解。

/* 在指定位置显示一个字符,包括部分字符
    x:0~127
    y:0~63             
    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;}  //Max_Column:最大列:128; x:设置列数; y:设置页数
    if(Char_Size ==16)  //此时需要两页的同一列,8*16的点阵
    {
        OLED_Set_Pos(x,y);     //若 x = y = 2,则设置的为第3页的第3列,  注意:每一页只有八行
        for(i=0;i<8;i++)     
        OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);   //通过i的递增,循环画点,此时将第2页第2列的8行都写入了数据
        OLED_Set_Pos(x,y+1);   //由于画点的数目行数不够,此时需要第3页的第2列来续画点
        for(i=0;i<8;i++)       
        OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);   //接着画完,直到第16个点结束
    }
    else 
    {    
        OLED_Set_Pos(x,y);  //6*8的点阵,不需要其他的页来续画
        for(i=0;i<6;i++)
        OLED_WR_Byte(F6x8[c][i],OLED_DATA);      //二维数组,c控制第几行,i控制第几列,故不需要其他的操作即可画完
    }
}

下面的的 if(x>120)并不是错误,因为前面的x+=8;下文的注释中有解释,可以好好想想。

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;   //x 设置的是列,一个字符的大小为8*16,即行16列8,每次显示为一个后,都需要向高列移8列
        if(x>120){x=0;y+=2;} // 最高为128列,超过的话则需要重新从零列开始,由于此时需要别的页数来续画,避免重叠,需要 y += 2。
        j++; //循环画字符串
    }
}

显示2个数字,具体都在下面的代码中写出,需要注意的是,下面的 “ “ 表示的是ASCII值32

//m^n函数
u32 oled_pow(u8 m,u8 n)
{
    u32 result=1;     
    while(n--)result*=m;    
    return result;
}            

//显示2个数字
//x,y :起点坐标     
//len :数字的位数
//size:字体大小
//mode:模式    0,填充模式;1,叠加模式
//num:数值(0~4294967295);               
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{             
    u8 t,temp;
    u8 enshow=0;                           
    for(t=0;t<len;t++)
    {
        temp=(num/oled_pow(10,len-t-1))%10;
        if(enshow==0&&t<(len-1))
        {
            if(temp==0)
            {
                OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
                continue;
            }else enshow=1; 

        }
         OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
    }
}

上面的几个都是我们平时最为基本的用法,有些学习者想要用它来完成更加不一样的操作,下面便有,介绍之前,先来介绍一下我们常用的取模软件

PCtoLCD2002

界面如下!

我们在用的时候先打开左上角的文件,进行新建。输入我们的宽高,6 8或者8 16都是按照这个来取模的,可以自己设定大小。在正上角的地方有一个齿轮一样的东西,我们在设定好大小之后便需要打开它。如图:


把上面的都做好之后就可以画自己想要的东西了。因为oled是128 64,所以我们最大的大小就是这个,我们可以在128 64上写字,画图,等等,出来的就是一整个画面,这也是满屏的一种方式,后面还有一种取模软件,取得是图片,比如一些动漫人物。

介绍完取模软件,我们便可以继续我们的操作了!

1:画直线
通常我们使用如下图中一样的点想画直线时总是得到一个个点,这是因为我们一般使用的字符大小都是6 8或者8 16,里面的点大小并不是充满整个矩形的(上面有解释)我们需要做的便是打开我们的字符软件,把点改大一点便可!

2:显示图片(比如一些动漫人物)
先上图

这个跟上面的差不多,各有千秋!
篇幅感觉有点长了。还有更多的玩法,下次更新,也欢迎介绍给我,私信即可!如有错误感谢指出,共同学习。
两个软件在百度都有。

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
关于作者
我是白开水呢

我是白开水呢

暂无个性签名~

原创2
阅读526
收藏0
点赞0
评论0
打赏用户 0
我要创作
分享技术经验,可获取创作收益
分类专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
我是白开水呢
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区