STM32学习笔记——OLED的使用

OLED同样也是STM32的一个重要外设模块,在程序代码的调试起着较为重要的作用,这篇文章就通过OLED的数据手册来分析下OLED的使用方法。


前言

OLED作为STM32的一个较为重要的外设,其作用也是为了方便调试代码。OLED模块的驱动可以使用8080SPI四线SPI3线I2C的方法进行驱动。本文主要根据OLED的数据手册分析8080SPI四线I2C的使用。


一、配置OLED的IO口

因为OLED模块分为8080串行、SPI四线、I2C,不同的数据传输模式导致了其不同的接线方法以及配置方法。

1.8080并口模式

对于8080并口的配置,参考正点原子的例程。
其中8080并口,总共需要13根信号线通信,
这些信号线如下:

CS: OLED 片选信号。
WR(RW):向 OLED 写入数据。
RD:从 OLED 读取数据。
D[7:0]8 位双向数据线。
RST(RES):硬复位 OLED。
DC:命令/数据标志(0,读写命令; 1,读写数据)

在知道了接线之后,我们必须将所使用到的IO口使能:

//初始化SSD1306					    
void OLED_Init(void)
{ 	
 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG, ENABLE);	 //使能PC,D,G端口时钟

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_6;	 //PD3,PD6推挽输出  
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOD, &GPIO_InitStructure);	  //初始化GPIOD3,6
 	GPIO_SetBits(GPIOD,GPIO_Pin_3|GPIO_Pin_6);	//PD3,PD6 输出高

 
 	GPIO_InitStructure.GPIO_Pin =0xFF; //PC0~7 OUT推挽输出
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOC,0xFF); //PC0~7输出高

 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;				 //PG13,14,15 OUT推挽输出
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOG,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);						 //PG13,14,15 OUT  输出高


  							  
	

在将IO口初始化之后,我们需要做的第三件事情便是如何写入命令或者数据到OLED中,因为使用OLED的时候,需要配置,而那些配置的命令是每个OLED出厂的已经存在了,属于命令范畴,我们所要了解的就是如何使用8080并口模式向OLED中写入命令。
如正点原子里写的,


#define DATAOUT(x) GPIO_Write(GPIOC,x);//输出  

void OLED_WR_Byte(u8 dat,u8 cmd)
{
	DATAOUT(dat);	    
 	OLED_RS=cmd;   //DC位
	OLED_CS=0;	   
	OLED_WR=0;	 
	OLED_WR=1;		//只有在WR由低变高的时候才能传入数据
	OLED_CS=1;	  
	OLED_RS=1;	 
} 	    	

代码分析:根据之前的相应IO口连接,同时在OLED的数据手册中有提及到的,只有当OLED的片选CS拉低后以及WR拉低后,才可以开始往OLED中写入数据。后续的OLED初始化配置都是基于这个函数所进行的操作的。

2.SPI四线

SPI四线的OLED也只是万变不离其宗,一样的初始化过程,先确认接线,相比于8080串口接线方法,SPI四线极大的缩小了IO口的使用数量,更加便于使用。

/*
	OLED0.96                  							STM32
	GND        				<----------->					GND
	VCC        				<----------->					3.3V
	SCL                 	<----------->					PE3
	SDA        				<----------->					PE4
	DC(Data/Command)	    <----------->					PE0
	RST						<----------->					PE2
*/


这里拿我之前所使用过的一个串口配置为主,来讲解。

首先也是不变的初始化各个IO口:

void OLED_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4);

初始化完成后,也是需要进行判断,判断用户是打算写入数据还是写入命令:


//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 cmd)
{	
	u8 i;			  
	if(cmd)
	  OLED_RS_Set();
	else 
	  OLED_RS_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_RS_Set();   	  
} 

有了这个主要的函数之后,便可以对OLED进行配置,如同上文所提到的8080串口配置一样,

	OLED_RST_Set();
	delay_us(100000);
	OLED_RST_Clr();
	delay_us(20);
	OLED_RST_Set();
	
	OLED_WR_Byte(0xA8, OLED_CMD);
	OLED_WR_Byte(0x3F, OLED_CMD);
	OLED_WR_Byte(0xD3, OLED_CMD);
	OLED_WR_Byte(0x00, OLED_CMD);
	OLED_WR_Byte(0x40, OLED_CMD);
	OLED_WR_Byte(0xA1, OLED_CMD);   //A0/A1:控制屏幕显示左右映射翻转的
	OLED_WR_Byte(0xC0, OLED_CMD);   //C0/C8:控制屏幕上下映射翻转的
	OLED_WR_Byte(0xDA, OLED_CMD);
	OLED_WR_Byte(0x12, OLED_CMD);   //02h:隔行显示  12h:逐行显示
	OLED_WR_Byte(0x81, OLED_CMD);
	OLED_WR_Byte(0x7F, OLED_CMD);
	OLED_WR_Byte(0xA4, OLED_CMD);
	OLED_WR_Byte(0xA6, OLED_CMD);
	OLED_WR_Byte(0xD5, OLED_CMD);
	OLED_WR_Byte(0x80, OLED_CMD);
	OLED_WR_Byte(0x8D, OLED_CMD);
	OLED_WR_Byte(0x14, OLED_CMD);
	OLED_WR_Byte(0xAF, OLED_CMD);
	
	OLED_Clear();

3.I2C

这里I2C和上文提到的OLED的初始化以及功能实现都大致相同,就不做太多的语言描述。

/*
	OLED0.96                  							STM32
	GND        				<----------->					GND
	VCC        				<----------->					3.3V
	D0(SCLK)            	<----------->					PA0			
	D1SDIN()           	   <----------->					PA1
	RES        			 	<----------->					PA2
	DC(Data/Command)	   <----------->					PA3
	CS						<----------->					PA4
*/

OLED的初始化和配置如下:

void OLED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4);
	
	OLED_RST_Set();
	delay_us(100000);
	OLED_RST_Clr();
	delay_us(20);
	OLED_RST_Set();
	
	OLED_WR_Byte(0xA8, OLED_CMD);
	OLED_WR_Byte(0x3F, OLED_CMD);
	OLED_WR_Byte(0xD3, OLED_CMD);
	OLED_WR_Byte(0x00, OLED_CMD);
	OLED_WR_Byte(0x40, OLED_CMD);
	OLED_WR_Byte(0xA1, OLED_CMD);   //A0/A1:控制屏幕显示左右映射翻转的
	OLED_WR_Byte(0xC0, OLED_CMD);   //C0/C8:控制屏幕上下映射翻转的
	OLED_WR_Byte(0xDA, OLED_CMD);
	OLED_WR_Byte(0x12, OLED_CMD);   //02h:隔行显示  12h:逐行显示
	OLED_WR_Byte(0x81, OLED_CMD);
	OLED_WR_Byte(0x7F, OLED_CMD);
	OLED_WR_Byte(0xA4, OLED_CMD);
	OLED_WR_Byte(0xA6, OLED_CMD);
	OLED_WR_Byte(0xD5, OLED_CMD);
	OLED_WR_Byte(0x80, OLED_CMD);
	OLED_WR_Byte(0x8D, OLED_CMD);
	OLED_WR_Byte(0x14, OLED_CMD);
	OLED_WR_Byte(0xAF, OLED_CMD);
	
	OLED_Clear();
}

也就是照着本文提到的第二种方法能完成的。这里就不太多描述。
但是,与上文提到的第二种方法不同,在数据/命令选择中,与上文不同

//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();  //拉低CS
	
	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的IO多了SDIN这一个IO口,而这个IO的存在,导致其OLED_WR_ Byte()的写法不太一样。需要注意一下即可。

二、阅读datasheet(数据手册)

OLED可以传输命令(cmd)或者是数据(data),而传输命令或者传输数据,都需要依靠之前提到的函数,以8080串口方式为例的OLED初始化,正点原子的代码中也很明确的注释了:

	OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示
	OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率
	OLED_WR_Byte(80,OLED_CMD);   //[3:0],分频因子;[7:4],震荡频率
	OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数
	OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64) 
	OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移
	OLED_WR_Byte(0X00,OLED_CMD); //默认为0

	OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数.
													    
	OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置
	OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭
	OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式
	OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
	OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127;
	OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
	OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置
	OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置
		 
	OLED_WR_Byte(0x81,OLED_CMD); //对比度设置
	OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)
	OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期
	OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;
	OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率
	OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

	OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
	OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示	    						   
	OLED_WR_Byte(0xAF,OLED_CMD); //开启显示	 
	OLED_Clear();

由于使用的同一厂家的OLED屏幕,所以往OLED中写入的命令字符也是大致的相同,SPI四线写入的命令为:

	
	OLED_WR_Byte(0xA8, OLED_CMD);
	OLED_WR_Byte(0x3F, OLED_CMD);
	OLED_WR_Byte(0xD3, OLED_CMD);
	OLED_WR_Byte(0x00, OLED_CMD);
	OLED_WR_Byte(0x40, OLED_CMD);
	OLED_WR_Byte(0xA1, OLED_CMD);   //A0/A1:控制屏幕显示左右映射翻转的
	OLED_WR_Byte(0xC0, OLED_CMD);   //C0/C8:控制屏幕上下映射翻转的
	OLED_WR_Byte(0xDA, OLED_CMD);
	OLED_WR_Byte(0x12, OLED_CMD);   //02h:隔行显示  12h:逐行显示
	OLED_WR_Byte(0x81, OLED_CMD);
	OLED_WR_Byte(0x7F, OLED_CMD);
	OLED_WR_Byte(0xA4, OLED_CMD);
	OLED_WR_Byte(0xA6, OLED_CMD);
	OLED_WR_Byte(0xD5, OLED_CMD);
	OLED_WR_Byte(0x80, OLED_CMD);
	OLED_WR_Byte(0x8D, OLED_CMD);
	OLED_WR_Byte(0x14, OLED_CMD);
	OLED_WR_Byte(0xAF, OLED_CMD);
	
	OLED_Clear();

不难发现,也是万变不离其宗。

总结

在这三种OLED的驱动方式中,最重要的还是需要了解OLED_WR_Byte()这个函数
后续的显示操作也是和此函数息息相关。而对于一些OLED屏幕的描点函数和显示数字和字符函数,如果后续有时间的话,我会逐渐完善的。

(SPI驱动OLED的方法和I2C驱动OLED的方法我在上文好像混淆了,准确的说第二种方法应该叫六针OLED,而第三种应该是七针OLED)

对于该篇文章的话,我也不太敢打包票说我能讲清楚OLED不同驱动方式下的详细步骤以及为什么,我只能尽我所能。如果有什么不足以及不对的地方希望大家指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值