51单片机学习笔记:使用SPI接口实现OLED显示

0.96寸OLED显示

  • 1.OLED简介
  • 1.2 OLED结构
  • 1.2 OLED特点
  • 1.3 发光原理
  • 1.4 模块参数
  • 2.OLED显示编程
  • 2.1 OLED显示库
  • 2.2 汉字取模
  • 2.3 OLED显示
  • 1.OLED简介

    有机发光二极管(Organic Light-Emitting Diode, OLED)又称为有机电激光显示、有机发光半导体。由美籍华裔教授邓青云(Ching W. Tang)于1979年在实验室中发现。OLED显示技术具有自发光、广视角、几乎无穷高的对比度、较低耗电、极高反应速度等优点。

    1.2 OLED结构

    由基板、阴极、阳极、空穴注入层(HIL)、电子注入层(EIL)、空穴传输层(HTL)、电子传输层(ETL)、电子阻挡层(EBL)、空穴阻挡层(HBL)、发光层(EML)等部分构成。其中,基板是整个器件的基础,所有功能层都需要蒸镀到器件的基板上;通常采用玻璃作为器件的基板,但是如果需要制作可弯曲的柔性OLED器件,则需要使用其它材料如塑料等作为器件的基板。阳极与器件外加驱动电压的正极相连,阳极中的空穴会在外加驱动电压的驱动下向器件中的发光层移动,阳极需要在器件工作时具有一定的透光性,使得器件内部发出的光能够被外界观察到;阳极最常使用的材料是ITO。空穴注入层能够对器件的阳极进行修饰,并可以使来自阳极的空穴顺利的注入到空穴传输层;空穴传输层负责将空穴运输到发光层;电子阻挡层会把来自阴极的电子阻挡在器件的发光层界面处,增大器件发光层界面处电子的浓度;发光层为器件电子和空穴再结合形成激子然后激子退激发光的地方;空穴阻挡层会将来自阳极的空穴阻挡在器件发光层的界面处,进而提高器件发光层界面处电子和空穴再结合的概率,增大器件的发光效率;电子传输层负责将来自阴极的电子传输到器件的发光层中;电子注入层起对阴极修饰及将电子传输到电子传输层的作用;阴极中的电子会在器件外加驱动电压的驱动下向器件的发光层移动,然后在发光层与来自阳极的空穴进行再结合。

    1.2 OLED特点

    (1)功耗低——与LCD相比,OLED不需要背光源,而背光源在LCD中是比较耗能的一部分,所以OLED是比较节能的。例如,24in的AMOLED模块功耗仅仅为440mw,而24in的多晶硅LCD模块达到了605mw。
    (2)响应速度快——OLED技术与其他技术相比,其响应速度快,响应时间可以达到微秒级别。较高的响应速度更好的实现了运动的图像。根据有关的数据分析,其响应速度达到了液晶显示器响应速度的1000倍左右。
    (3)较宽的视角——与其他显示相比,由于OLED是主动发光的,所以在很大视角范围内画面是不会显示失真的。其上下,左右的视角宽度超过170度。
    (4)能实现高分辨率显示——大多高分辨率的OLED显示采用的是有源矩阵也就是AMOLED,它的发光层可以是吸纳26万真彩色的高分辨率,并且随着科学技术的发展,其分辨率在以后会得到更高的提升。
    (5)宽温度特性——与LCD相比,OLED可以在很大的温度范围内进行工作,根据有关的技术分析,温度在-40摄氏度到80摄氏度都是可以正常运行的。这样就可以降低地域限制,在极寒地带也可以正常使用。
    (6)OLED能够实现软屏——OLED可以在塑料、树脂等不同的柔性衬底材料上进行生产,将有机层蒸镀或涂布在塑料基衬上,就可以实现软屏。
    (7)OLED成品的质量比较轻——与其他产品相比,OLED的质量比较小,厚度与LCD相比是比较小的,其抗震系数较高,能够适应较大的加速度,振动等比较恶劣的环境。

    1.3 发光原理

    OLED器件的发光过程可分为:电子和空穴的注入、电子和空穴的传输、电子和空穴的再结合、激子的退激发光。具体为:
    (1)电子和空穴的注入。处于阴极中的电子和阳极中的空穴在外加驱动电压的驱动下会向器件的发光层移动,在向器件发光层移动的过程中,若器件包含有电子注入层和空穴注入层,则电子和空穴首先需要克服阴极与电子注入层及阳极与空穴注入层之间的能级势垒,然后经由电子注入层和空穴注入层向器件的电子传输层和空穴传输层移动;电子注入层和空穴注入层可增大器件的效率和寿命。关于OLED器件电子注入的机制还在不断的研究当中,目前最常被使用的机制是穿隧效应和界面偶极机制。
    (2)电子和空穴的传输。在外加驱动电压的驱动下,来自阴极的电子和阳极的空穴会分别移动到器件的电子传输层和空穴传输层,电子传输层和空穴传输层会分别将电子和空穴移动到器件发光层的界面处;与此同时,电子传输层和空穴传输层分别会将来自阳极的空穴和来自阴极的电子阻挡在器件发光层的界面处,使得器件发光层界面处的电子和空穴得以累积。
    (3)电子和空穴的再结合。当器件发光层界面处的电子和空穴达到一定数目时,电子和空穴会进行再结合并在发光层产生激子。
    (4)激子的退激发光。在发光层处产生的激子会使得器件发光层中的有机分子被活化,进而使得有机分子最外层的电子从基态跃迁到激发态,由于处于激发态的电子极其不稳定,其会向基态跃迁,在跃迁的过程中会有能量以光的形式被释放出来,进而实现了器件的发光。

    1.4 模块参数


    0.96寸OLED模块
    电压:3V~5V DC
    工作温度:-30℃~70℃
    高分辨率:128 * 64
    面板尺寸:26.70 * 19.26 * 1.85mm / 1.03 * 0.76 * 0.07英寸(约)
    有效面积:21.74 * 11.2mm /0.86*0.44英寸(约)
    驱动IC:SSD1306
    128 * 64 LED显示模块,支持多种控制芯片。
    完全兼容51系列,MSP430系列,STM32 / 2,CSR IC等
    超低功耗:全屏点亮0.08W
    超高亮度和对比度可调
    带嵌入式驱动/控制器
    接口类型为SPI

    2.OLED显示编程

    2.1 OLED显示库

    OLED屏显示需要编写显示驱动代码,一般都可以在网上搜到OLED的显示demo程序,主要显示代码如下

    #include "oled.h"
    #include "oledfont.h" 
     	
    //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 			   
    void delay_ms(unsigned int ms)
    {                         
    	unsigned int a;
    	while(ms)
    	{
    		a=1800;
    		while(a--);
    		ms--;
    	}
    	return;
    }
    #if OLED_MODE==1
    //向SSD1106写入一个字节。
    //dat:要写入的数据/命令
    //cmd:数据/命令标志 0,表示命令;1,表示数据;
    void OLED_WR_Byte(u8 dat,u8 cmd)
    {
    	DATAOUT(dat);	    
    	if(cmd)
    	  OLED_DC_Set();
    	else 
    	  OLED_DC_Clr();		   
    	OLED_CS_Clr();
    	OLED_WR_Clr();	 
    	OLED_WR_Set();
    	OLED_CS_Set();	  
    	OLED_DC_Set();	 
    } 	    	    
    #else
    //向SSD1306写入一个字节。
    //dat:要写入的数据/命令
    //cmd:数据/命令标志 0,表示命令;1,表示数据;
    void OLED_WR_Byte(u8 dat,u8 cmd)
    {	
    	u8 i;			  
    	if(cmd)
    	  OLED_DC_Set();
    	else 
    	  OLED_DC_Clr();		  
    	OLED_CS_Clr();
    	for(i=0;i<8;i++)
    	{			  
    		OLED_SCLK_Clr();
    		if(dat&0x80)
    			{
    		   OLED_SDIN_Set();
    			}
    else
    		   OLED_SDIN_Clr();
    				OLED_SCLK_Set();
    		dat<<=1;   
    	}				 		  
    	OLED_CS_Set();
    	OLED_DC_Set();   	  
    } 
    #endif
    	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)|0x01,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_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); 
    	} //更新显示
    }
    
    
    //在指定位置显示一个字符,包括部分字符
    //x:0~127
    //y:0~63
    //mode:0,反白显示;1,正常显示				 
    //size:选择字体 16/12 
    void OLED_ShowChar(u8 x,u8 y,u8 chr)
    {      	
    	unsigned char c=0,i=0;	
    		c=chr-' ';//得到偏移后的值			
    		if(x>Max_Column-1){x=0;y=y+2;}
    		if(SIZE ==16)
    			{
    			OLED_Set_Pos(x,y);	
    			for(i=0;i<8;i++)
    			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
    			OLED_Set_Pos(x,y+1);
    			for(i=0;i<8;i++)
    			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
    			}
    			else {	
    				OLED_Set_Pos(x,y+1);
    				for(i=0;i<6;i++)
    				OLED_WR_Byte(F6x8[c][i],OLED_DATA);
    				
    			}
    }
    
    //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,' ');
    				continue;
    			}else enshow=1; 
    		 	 
    		}
    	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0'); 
    	}
    } 
    
    //显示一个字符号串
    void OLED_ShowString(u8 x,u8 y,u8 *chr)
    {
    	unsigned char j=0;
    	while (chr[j]!='\0')
    	{		OLED_ShowChar(x,y,chr[j]);
    			x+=8;
    		if(x>120){x=0;y+=2;}
    			j++;
    	}
    }
    
    //显示汉字
    void OLED_ShowCHinese(u8 x,u8 y,u8 no)
    {      			    
    	u8 t,adder=0;
    	OLED_Set_Pos(x,y);	
        for(t=0;t<16;t++)
    		{
    				OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
    				adder+=1;
         }	
    		OLED_Set_Pos(x,y+1);	
        for(t=0;t<16;t++)
    			{	
    				OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
    				adder+=1;
          }					
    }
    
    //初始化SSD1306					    
    void OLED_Init(void)
    {
        OLED_RST_Set();
    	delay_ms(100);
    	OLED_RST_Clr();
    	delay_ms(100);
    	OLED_RST_Set(); 
    
    	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
    	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);//--set contrast control register
    	OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
    	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);//--set normal display
    	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
    	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
    	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
    	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
    	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
    	OLED_WR_Byte(0x12,OLED_CMD);
    	OLED_WR_Byte(0xDB,OLED_CMD);//--set 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_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
    	
    	OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/ 
    	OLED_Clear();
    	OLED_Set_Pos(0,0); 	
    }
    

    2.2 汉字取模

    OLED显示屏除了可以显示字符串之外,还可以显示汉字,图片等;字符串显示直接在代码里写,汉字和图片都需要用取模软件生成字库,下面是常用的取模软件

    选择字符模式

    点击选项或者点击小齿轮,进行驱魔配置

    将配置中的A51格式改为C51格式,其他保持默认

    输入OLED显示屏上要显示的汉字,点击生成字模,可看到下方出现了一串十六进制数,每一个汉字取模都会转换成32个十六进制数,可以连续几个汉字一起取模

    将生成的字模代码拷贝至工程中,放在字模头文件的数组中,除了注释的文字,数组中不要出现其他文字,否则会编译出错,如下

    字模数组在上面OLED显示库中的汉字显示函数中被调用

    //显示汉字
    void OLED_ShowCHinese(u8 x,u8 y,u8 no)
    {      			    
    	u8 t,adder=0;
    	OLED_Set_Pos(x,y);	
        for(t=0;t<16;t++)
    		{
    				OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
    				adder+=1;
         }	
    		OLED_Set_Pos(x,y+1);	
        for(t=0;t<16;t++)
    			{	
    				OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
    				adder+=1;
          }					
    }
    

    2.3 OLED显示

    烧写程序前,按程序中OLED控制引脚(SPI)定义连接引脚,SPI接口需要用到5个引脚,另外是5V供电和GND

    sbit OLED_CS	=	P2^0; //片选
    sbit OLED_DC 	=	P2^1;//数据/命令控制
    sbit OLED_RST 	=	P2^2;//复位RES
    sbit OLED_SDIN	=	P2^3;//D1(MOSI) 数据
    sbit OLED_SCL	=	P2^4;//时钟 D0(SCLK?
    

    在main函数中,调用函数OLED_ShowString可以显示字符串,调用函数OLED_ShowCHinese可以显示汉字

    void main(void)
    {	
    	OLED_Init();  //oled屏幕初始化
    	OLED_Clear(); //oled清屏	
    
    	//OLED_ShowString(0,0,"OLED test");	//显示字符串
    	//显示汉字	OLED_ShowCHinese 参数1 X坐标,参数2 Y坐标 
    	OLED_ShowCHinese(0,0,0);
    	OLED_ShowCHinese(18,0,1);
    	OLED_ShowCHinese(36,0,2);
    	OLED_ShowCHinese(54,0,3);
    	OLED_ShowCHinese(72,0,4);
    	
    	while(1);
    }
    

    显示效果如下图

    物联沃分享整理
    物联沃-IOTWORD物联网 » 51单片机学习笔记:使用SPI接口实现OLED显示

    发表评论