OLED同样也是STM32的一个重要外设模块,在程序代码的调试起着较为重要的作用,这篇文章就通过OLED的数据手册来分析下OLED的使用方法。
前言
OLED作为STM32的一个较为重要的外设,其作用也是为了方便调试代码。OLED模块的驱动可以使用8080、SPI四线、SPI3线、I2C的方法进行驱动。本文主要根据OLED的数据手册分析8080、SPI四线、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不同驱动方式下的详细步骤以及为什么,我只能尽我所能。如果有什么不足以及不对的地方希望大家指正。