吃透OLED模块——玩转OLED模块各种使用方法

Iria ·
更新时间:2024-09-21
· 764 次阅读

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线并行工作模式这里不做介绍,大家可以去参考一下这篇文章https://blog.csdn.net/qq_38410730/article/details/80033873

刚才我们就了解到。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页,这样刚好是128*64的点阵大小。
在这里插入图片描述

但是由于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:显示图片(比如一些动漫人物)

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


作者:又是心心想念雪姐姐的一天



oled 方法

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章