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

oled模块有4种工作模式,分别是6800、8080两种并行接口方式、 4线的穿行SPI接口方式、IIC接口方式。通过模块的BS1/BS2设置(通过硬件来设置),BS1/BS2的设置与模块接口模式的关系如表所示:
吃透OLED模块——玩转OLED模块各种使用方法
这是其中一种工作方式的模块,如图:
吃透OLED模块——玩转OLED模块各种使用方法
ALIENTEK OLED模块默认设置是BS0接GND,BS1和BS2接VCC(8080模式),如果想要设置成其他的模式,则需要在OLED的背面,用烙铁修改BS0-BS2的设置。(硬件改动)吃透OLED模块——玩转OLED模块各种使用方法
从原理图中我们可以知道,引出来总共有16个管脚,在16条线中,我们只用了15条,有一个是悬空的。15条线中,电源和地线占了2条,还剩下13条信号线。在不同模式下,我们需要的信号线数量是不同的,在8080模式下,需要全部13条,这其中有一条是共同的,那就是复位线RST(RES),该线我们可以直接接在MCU的复位上(要先确认复位方式一样),这样可以省掉一条线。 而在IIC模式下,仅需要2条线就够了!

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

刚才我们就了解到。iic的工作模式只需要两条线,如图:
吃透OLED模块——玩转OLED模块各种使用方法
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模块——玩转OLED模块各种使用方法

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

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

因为每次写入都是按字节写入的,这就存在一个问题,如果我们使用只写方式操作模块,那么,每次要写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命令

吃透OLED模块——玩转OLED模块各种使用方法
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

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

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

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

1:画直线
通常我们使用如下图中一样的点想画直线时总是得到一个个点,这是因为我们一般使用的字符大小都是6 * 8或者8 * 16,里面的点大小并不是充满整个矩形的(上面有解释)我们需要做的便是打开我们的字符软件,把点改大一点便可!
吃透OLED模块——玩转OLED模块各种使用方法
2:显示图片(比如一些动漫人物)

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

上一篇:合并一个文文件夹下的所有Excel文件


下一篇:[jsoi2015]染色问题