14.1  OLED显示屏的简单介绍

OLED

有机发光二极管( Organic Light Emitting Diode )。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。 LCD都需要背光,而OLED不需要,因为它是自发光的。这样同样的显示OLED 效果要来得好一些。以目前的技术,OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。

发光原理

OLED属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。OLED在电场的作用下,阳极产生的空穴和阴极产生的电子就会发生移动,分别向空穴传输层和电子传输层注入,迁移到发光层。当二者在发光层相遇时,产生能量激子,从而激发发光分子最终产生可见光。

OLED显示屏特点

(下文OLED显示屏介绍都以中景园电子的OLED显示屏为例)。

1、0.96 寸OLED 有黄蓝,白,蓝三种颜色可选;其中黄蓝是屏上1/4部分为黄光,下3/4 为蓝;而且是固定区域显示固定颜色,颜色和显示区域均不能修改;白光则为纯白,也就是黑底白字;蓝色则为纯蓝,也就是黑底蓝字。

2、分辨率为128*64。

3、多种接口方式;OLED 裸屏总共种接口包括:6800、8080 两种并行接口方式、3线或4线串行SPI 接口方式、IIC 接口方式(只需要2根线就可以控制 OLED 了!),这五种接口是通过屏上的BS0~BS2 来配置的。

IIC 接口模块接口定义

1、GND:电源地。

2、VCC:电源正(3~5.5V)。

3、SCL:OLED 的D0 脚,在IIC 通信中为时钟管脚。

4、SDA:OLED 的D1 脚,在IIC 通信中为数据管脚。

0.96 寸OLED 驱动IC

0.96 寸OLED显示屏所用的驱动IC为SSD1306;其具有内部升压功能;所以在设计的时候不需要再专一设计升压电路;当然了本屏也可以选用外部升压。SSD1306的每页包含了128个字节,总共8页,这样刚好是 128*64 的点阵大小。这点与1.3寸OLED驱动IC(SSD1106)稍有不同,SSD1106 每页是132个字节,也是8页。所以在用0.96寸OLED 移植1.3 寸OLED程序的时候需要将0.96寸的显示地址向右偏移2,这样显示就正常了;否则在用1.3 寸的时候1.3寸屏右边会有4个像素点宽度显示不正常或是全白,这点大家注意一下。其它的SSD1306和SSD1106 区别不大。

SSD1306的引脚定义

C1P /C1N C2P /C2N : 保留引脚,应保持NC,这里并联电容器。

VBAT : 连接到VDD。VDD指的是高电平

VSS: 接地引脚。

VDD: 用于核心逻辑操作的电源引脚。

BS【2:0】: MCU总线接口选择引脚

CS#: 该引脚是芯片选择输入(低电平使能)。

RES#: 该引脚为复位信号输入。当引脚拉低时,执行芯片初始化。在正常操作期间,保持该引脚高(即连接到VDD)。

D/C#: 这是数据/命令控制引脚当它被拉高(即连接到VDD)时,D[7:0]处的数据被视为数当它被拉低时,D【7:0】处的数据将传输到命令寄存器。在I2C模式下,该引脚充当SA0,用于从地址选择。 选择3线串行接口时,该引脚必须连接到VSS。有关MCU接口信号的详细关系,请参阅时序特性图。

R/W#(WR#): 这是连接到MCU接口的读/写控制输入引脚。当连接到6800系列微处理器时,该引脚将用作读/写(R/W#)选择输入。当该引脚拉高(即连接到VDD)时,将执行读取模式;**当该引脚拉低时,将执行写入模式。**选择8080接口模式时,该引脚将作为写入(WR#)输入。当该引脚拉低并选择芯片时,启动数据写入操作。选择串行接口时,该引脚必须连接到VSS。

E(RD#): 当连接到6800系列微处理器时,该引脚将用作启用(E)信号。当该引脚拉高(即连接到VDD)并选择芯片时,启动读/写操作。当连接到8080系列微处理器时,该引脚接收读取(RD#)信号。当该引脚拉低并选择芯片时,启动读取操作。 选择串行接口时,该引脚必须连接到VSS。文章地址https://www.yii666.com/blog/354859.html

D【7:0】: 这些是连接到微处理器数据总线的8位双向数据总线。

IREF: 这是输出电流参考引脚。该引脚和VSS之间应连接一个电阻器,以将IREF电流保持在12.5 uA。

VCOMH: COM信号的引脚取消选择了电压电平。该引脚和VSS之间应连接电容器。

VCC: 输入电源电压引脚。

VLSS: 模拟接地引脚。它应该从外部连接到VSS。

14.2  OLED显示屏的驱动程序

首先拿到SSD1306驱动的显示屏后,接上GND,VCC,SCL,SDA四条线后,屏幕是不会自动亮起的,需要CPU写入数据驱动它显示。查阅SSD1306的手册,我们可以发现与其通信的方式。

I2C通信方式

因为IIC在前面我们详细的叙述过,这里就不再重复的介绍了,简单的分析就是,我们先将IIC的驱动程序写好,然后在去数据手册寻找显示屏如何具体的驱动。

//延时
void IIC_delay(void)
{
	unsigned char t=1;
	while(t--);
}
//起始信号
void I2C_Start(void)
{
	OLED_SDA_Set();
	OLED_SCL_Set();
	IIC_delay();
	OLED_SDA_Clr();
	IIC_delay();
	OLED_SCL_Clr();
}
//结束信号
void I2C_Stop(void)
{
	OLED_SDA_Clr();
	OLED_SCL_Set();
	IIC_delay();
	OLED_SDA_Set();
}
//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
	OLED_SDA_Set();
	IIC_delay();
	OLED_SCL_Set();
	IIC_delay();
	OLED_SCL_Clr();
	IIC_delay();
}
//写入一个字节
void Send_Byte(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OLED_SCL_Clr();//将时钟信号设置为低电平
		if(dat&0x80)//将dat的8位从最高位依次写入
		{
			OLED_SDA_Set();
    }
		else
		{
			OLED_SDA_Clr();
    }
		IIC_delay();
		OLED_SCL_Set();
		IIC_delay();
		OLED_SCL_Clr();
		dat<<=1;
  }
}

这是我们常规驱动IIC的方式

  1. 起始信号
  2. 写入芯片地址,一般为0x78或者0x7a
  3. 应答信号
  4. 写入控制命令(D/C#)选择数据模式(0x40)/控制模式(0x00)
  5. 应答信号
  6. 写入数据/控制字(一个字节)
  7. 应答信号
  8. 结束信号
//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(unsigned char dat,unsigned char mode)
{
	I2C_Start();
	Send_Byte(0x78);
	I2C_WaitAck();
	if(mode){Send_Byte(0x40);}
        else{Send_Byte(0x00);}
	I2C_WaitAck();
	Send_Byte(dat);
	I2C_WaitAck();
	I2C_Stop();
}

这里的)0X78或者0x7A是原理图决定的,我们查看原理图其实也不得而知,因为这个显示使用的比较多,多家厂商多次设计,所以我们先看一下那里决定的,翻到数据手册第二十页,可以看到如下。

在结合原理图我们再看中景园提供的原理图。

在这里我们可以看到控制D/C脚位的高低就可以完成地址的选择。

初始化SSD1306

由于一开始我们需要初始化SSD1306,因此我们需要先写入一些控制字来命令SSD1306的工作模式。鉴于指令集网上的手册中已有许多资料,这里不再赘述,只简单介绍一些用到的指令。

OLED_WR_Byte(0xAE,OLED_CMD);//开启OLED屏显示

以上是开启OLED屏关闭的命令,0xAE是控制字,OLED_CMD是写入命令。以下同理,便不再赘述

OLED_WR_Byte(0x00,OLED_CMD);//设置列起始地址的高位
OLED_WR_Byte(0x10,OLED_CMD);//设置列起始地址的低位
OLED_WR_Byte(0xB0,OLED_CMD);//设置目标显示位置页的起始地址

这三行代码用来控制显示的位置。这里就必须提一下SSD1306的内存地址模式,因为不同的模式下写入相同的数据输出的图像不同。SSD1306中有三种不同的内存地址模式:页地址模式,水平地址模式,垂直地址模式。这里我们使用的是页地址模式,其结构如下所示:

可以看到,这是128*64的显示屏,其中横向分为128段(列),竖向分为8页,每页有8行。选择页地址模式后,数据的填充如下所示:

在用户定义的地址写入数据后,列地址会自动加1,写完最后一列后,列地址指针会重置为列开始地址,而页地址不会变。至此,我们就很好理解上述的三行命令,其中0x00和0x10设置了列起始地址为0,而0xb0则设置了页起始地址为0。将上面代码结合我们一起写到初始化中:

//OLED的初始化				    
void OLED_Init(void)
{
        OLED_WR_Byte(0xAE,OLED_CMD);//--打开OLED显示
	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  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--设置显示的对比度(00H~FFH)
	OLED_WR_Byte(0xFF,OLED_CMD); // 对比度为0xCF
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--设置正常显示
	OLED_WR_Byte(0xA8,OLED_CMD);//--设置复用率(1~64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-设置显示偏移 RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-偏移量为0
	OLED_WR_Byte(0xd5,OLED_CMD);//--设置时钟分频率(1~16),振荡器频率
	OLED_WR_Byte(0x80,OLED_CMD);//--0~3位为时钟分频率(1),4~7位为振荡器频率(1000)
	OLED_WR_Byte(0xD9,OLED_CMD);//--设置重充电周期
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--设置COM引脚硬件配置
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--设置Vcomh取消选择水平
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/ 
}

显示函数

这里我们直接上程序,先看程序在分析每个步骤的作用。

//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f),OLED_CMD);
}   	  
//开启OLED显示    
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}
//关闭OLED显示     
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}	
//反显函数
void OLED_ColorTurn(unsigned char i)
{
	if(i==0)
		{
			OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
		}
	if(i==1)
		{
			OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
		}
}
//屏幕旋转180度
void OLED_DisplayTurn(unsigned char i)
{
	if(i==0)
		{
			OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
			OLED_WR_Byte(0xA1,OLED_CMD);
		}
	if(i==1)
		{
			OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
			OLED_WR_Byte(0xA0,OLED_CMD);
		}
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!	  
void OLED_Clear(void)  
{  
	unsigned char 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); 
	} //更新显示
}

//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63				 
//sizey:选择字体 6x8  8x16
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char sizey)
{      	
	unsigned char c=0,sizex=sizey/2;
	unsigned int i=0,size1;
	if(sizey==8)size1=6;
	else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
	c=chr-' ';//得到偏移后的值
	OLED_Set_Pos(x,y);
	for(i=0;i<size1;i++)
	{
		if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);
		if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);//6X8字号
		else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);//8x16字号
//		else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用户添加字号
		else return;
	}
}
//m^n函数
unsigned int oled_pow(unsigned char m,unsigned char n)
{
	unsigned int result=1;	 
	while(n--)result*=m;    
	return result;
}				  
//显示数字
//x,y :起点坐标
//num:要显示的数字
//len :数字的位数
//sizey:字体大小		  
void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned char len,unsigned char sizey)
{         	
	unsigned char t,temp,m=0;
	unsigned char enshow=0;
	if(sizey==8)m=2;
	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+(sizey/2+m)*t,y,' ',sizey);
				continue;
			}else enshow=1;
		}
	 	OLED_ShowChar(x+(sizey/2+m)*t,y,temp+'0',sizey);
	}
}
//显示一个字符号串
void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *chr,unsigned char sizey)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		
		OLED_ShowChar(x,y,chr[j++],sizey);
		if(sizey==8)x+=6;
		else x+=sizey/2;
	}
}
//显示汉字
void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char *p)
{      			    
	unsigned char t,wordNum;
	while(*p != '\0')
	{
		for(wordNum=0;wordNum<20;wordNum++)
		{
			if(Hzk[wordNum].Char[0]== *p && Hzk[wordNum].Char[1]== *(p+1))
			{
				OLED_Set_Pos(x,y);		
        for(t=0;t<16;t++){OLED_WR_Byte(Hzk[wordNum].Hex[t],1);}
				OLED_Set_Pos(x,y+1);	
        for(t=0;t<16;t++){OLED_WR_Byte(Hzk[wordNum].Hex[t+16],1);}					
				break;
			}
		}
		p += 2;
		x+=16;
	}
}

//显示图片
//x,y显示坐标
//sizex,sizey,图片长宽
//BMP:要显示的图片
void OLED_DrawBMP(unsigned char x,unsigned char y,unsigned char sizex, unsigned char sizey,unsigned char BMP[])
{ 	
  unsigned int j=0;
	unsigned char i,m;
	sizey=sizey/8+((sizey%8)?1:0);
	for(i=0;i<sizey;i++)
	{
		OLED_Set_Pos(x,i+y);
    for(m=0;m<sizex;m++)
		{      
			OLED_WR_Byte(BMP[j++],OLED_DATA);	    	
		}
	}
} 

14.3  取模工具的使用

上述的显示汉字等都需要自己取模,或者使用标准的字库去实现因为STC89C52单片机的内存有限,所以我们直接取我们需要的话汉字模型。

汉字取模

首先打开软件设置我们的取模方式

打开设置之后我们就可以操作了

设置完成后,点击左下角的确认按钮,然后就可以再点击生成字模,即可完成。

再把这个复制到程序里面如下图所示

我们对原来厂商提供的程序进行了修改,这样的方式更加的方便。然后在主函数调用即可。

OLED_ShowCHinese(0,0,"特纳斯");

图片取模

打开取模软件

2.1 点击模式,选择图形模式

2.2 点击文件,打开要取模的图片

2.3点击选项,进行设置图片设置如下,然后点击确定。

3.最后点击生成字模。

4.将生成的字模复制到bmp.h的数组中

发表回复

后才能评论