Thorough understanding OLED module - using various methods Fun OLED module

oled module has four operating modes, namely, a parallel interface 6800,8080 two kinds of ways, 4 crossed wire SPI interface mode, IIC interface mode. By BS1 / BS2 module set (set by the hardware), the relationship between the interface module provided with the mode BS1 / BS2 shown in Table:
Here Insert Picture Description
This is one mode of operation of the module, as shown:
Here Insert Picture Description
ALIENTEK the OLED module default setting is BS0 then GND, BS1 and BS2 connected to VCC (8080 mode), if you want to set to other modes, it is necessary in the back surface of the OLED, modified BS0-BS2 is provided with a soldering iron. (Hardware changes) Here Insert Picture Description
from the schematic, we know that lead out of a total of 16 pins, in 16 lines, we only use the 15, one is floating. 15 lines, the power and ground account 2, the signal line 13 is left. In different modes, the number of signal lines we need is different, in the 8080 mode, all 13, of which there is a common, that is, the reset line RST (RES), the line we can directly access the MCU on reset (first reset mode as confirmation), so that a line can be omitted. In the IIC mode, it requires only two wires enough!

8 line parallel mode of operation will not be discussed here, we can refer to this article https://blog.csdn.net/qq_38410730/article/details/80033873

We have just learned that. iic mode of operation requires only two wires, as shown:
Here Insert Picture Description
iic mode of operation is to use two lines to simulate iic data obtained as:

//-----------------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)

For analog iic, we can refer to the article, ultra Details: The basic principle of IIC] [STM32

OLED controller SSD1306, that is to say: the bare screen is driven by SSD1306, this is a more widespread use led driver chip.

OLED module memory

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

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

Here Insert Picture Description

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

Here Insert Picture Description
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

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

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

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

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

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

发布了3 篇原创文章 · 获赞 0 · 访问量 206

Guess you like

Origin blog.csdn.net/weixin_43872149/article/details/104712248