01-01: 0.96寸oled模块详解
版权声明:Copyright © 2016-2019,Stardust Studio, All Rights Reserved
本文将从以下四个方面详细介绍oled模块的使用:
一、入门
二、进阶
三、高阶
四、网传程序存在的不合理问题
一、入门
在阅读后续内容时请先详细阅读:
1.OLED显示模块驱动原理及应用(点击即可跳转到http://blog.sina.com.cn/s/blog_57ad1bd20102wtq8.html);
2.(中文翻译版,版本:据内容推断为Sep 2007 Rev 1.0)OLED SSD1306 DataSheet(点击即可跳转到https://wenku.baidu.com/view/10b60c264a73f242336c1eb91a37f111f1850d88.html);
3.(英文原版,版本:Aug 2010 Rev 1.5)OLED SSD1306 DataSheet(点击即可跳转到https://wenku.baidu.com/view/3510c4f2915f804d2a16c1a8.html);
4.0.96寸OLED驱动详解(带字库)(点击即可跳转到https://wenku.baidu.com/view/cc5ae02b7f1922791788e831.html);
注:(1)本文是参照英文原版Rev 1.5,使用stm32F103系列单片机和4Pin IIC OLED来编写驱动程序的;
(2)中文翻译版存在一些问题,仅供参考,所以要结合英文原版手册学习;
(3)百度文库要付费下载,读者可以下载 “ XX文库 " 免费下载文档;
学习方法:(1)前三份文档是重点,先着重看完第一份文档,对oled有了了解之后详细看第二份文档,结合英文手册对oled有总体认知;
(2)在学习时要对各种寄存器写入进行测试以便加深理解;
二、进阶
在大致看完前面四份文档后相信读者对oled显示屏已经有了大致的了解,那么现在到了看寄存器配置的时候了:
void OLED_Init(void)
{
//delay_ms(100); //这里的延时没有必要,IIC初始化相当于延时
/*指令集根据(SSD1306 DataSheet Aug 2010)编写*/
WriteCmd(0xae); //(display off)
/*(一)基础指令*/
WriteCmd(0x81); //(set contrast control register,亮度控制)
WriteCmd(0xff); //(亮度调节范围:0x00~0xff)
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content,a5即白屏,无视RAM内容,用于测试显示屏)
WriteCmd(0xa6); //(set normal display,a7:反相显示,亮的部分变暗,暗的部分变亮)
// WriteCmd(0xae); //(display off,顶部已开启)
// WriteCmd(0xaf); //(turn on oled panel,底部已开启)
/*************/
/*(二)滚动显示*/
// WriteCmd(0x26);//(26h:水平右滚 27:水平左滚 29h:Vertical and Right Horizontal Scroll 2ah:Vertical and Left Horizontal Scroll)
// WriteCmd(0x00);//(固态字节(芯片固定字节,用户不可改动)
// WriteCmd(0x00);//(滚动起始页)
// WriteCmd(0x01);//(滚动速度,64 frames)
// WriteCmd(0x07);//(滚动结束页)
// WriteCmd(0x00);//(固态字节,如果是29h或2ah(h是16进制数的意思)则取值为0-63)
// WriteCmd(0xff);//(固态字节,如果是29h或2ah(h是16进制数的意思)则注释这行)
// WriteCmd(0x2e); //(关闭滚屏)
// WriteCmd(0x2f); //(激活滚屏)
// WriteCmd(0xA3); //(设置滚屏范围)
// WriteCmd(0x08); //(不滚屏的行数)
// WriteCmd(0x20); //(要滚屏的行数)
/*************/
/*(三)地址设置*/
// WriteCmd(0x00); //(set low column address,初始化设置了没用,因为OLED_SetPos函数中会重设)
//
// WriteCmd(0x10); //(set high column address,初始化设置了没用,因为OLED_SetPos函数中会重设)
WriteCmd(0x20); //(Set Memory Addressing Mode)
WriteCmd(0x10); //(00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid)
// WriteCmd(0x21); //(列(起止)地址)
// WriteCmd(0x00); //A[6:0] : Column start address, range : 0-127d,(RESET=0d)
// WriteCmd(0x7F); //B[6:0]: Column end address, range : 0-127d,(RESET =127d)
//
// WriteCmd(0x21); //(行(起止)地址)
// WriteCmd(0x00); //A[2:0] : Page start Address, range : 0-7d,(RESET = 0d)
// WriteCmd(0x07); //B[2:0] : Page end Address, range : 0-7d,(RESET = 7d)
// WriteCmd(0xb0); //(Set Page Start Address for Page Addressing Mode,0-7,初始化设置了没用,因为OLED_SetPos函数中会重设)
/*************/
/*(四)硬件配置*/
WriteCmd(0x40); //(set start line address,从RAM中哪一行起读取显示内容)
WriteCmd(0xa1); //(set segment re-map 0 to 127,列显示次序反向)A0h, X[0]=0b: column address 0 is mapped to SEG0 (RESET) | A1h, X[0]=1b: column address 127 is mapped to SEG0
WriteCmd(0xa8); //(set multiplex ratio设置只扫描哪些行,可提高帧率)
WriteCmd(0x3F); //(范围16-63)
WriteCmd(0xc8); //(Set COM Output Scan Direction,即行扫方向)C0h, X[3]=0b: normal mode (RESET) Scan from COM0 to COM[N –1] | C8h, X[3]=1b: remapped mode. Scan from COM[N-1] to COM0
WriteCmd(0xd3); //(set display offset,行扫偏移,公共极(垂直)方向移动)
WriteCmd(0x00); //(not offset)
WriteCmd(0xda); //(set com pins hardware configuration,4种组合,再结合行扫方向有8种)
WriteCmd(0x12); //(设为默认值)A[4]=1b(RESET), Alternative COM pin configuration | A[5]=0b(RESET), Disable COM Left/Right remap
/*************/
/*(五)时序设置*/
WriteCmd(0xd5); //(set display clock divide ratio/oscillator frequency)
WriteCmd(0xf0); //(set divide ratio频率设置成最高407 kHz,分频系数为1)
WriteCmd(0xd9); //(set pre-charge period,充电、放电周期1-15设置)
WriteCmd(0x22); //(设置为默认值2DCLK)
WriteCmd(0xdb); //(设置Vcomh电压,防止单元串扰,Vcomh反压)
WriteCmd(0x20); //(20h ~ 0.77 x VCC (RESET))
/*************/
/*(六)高级图形命令*/
// WriteCmd(0x23); //(显示屏淡出和闪烁设置)
// WriteCmd(0x21); //(Fade Mode & 16 Frames) A[5:4] = 00b Disable Fade Out/Blinking Mode[RESET] | A[5:4] = 10b Enable Fade Out mode | A[5:4] = 11b Enable Blinking mode
// WriteCmd(0xD6); //(放大一倍显示,矢量放大)
// WriteCmd(0x01); //A[0] = 0b Disable Zoom in Mode[RESET] | A[0] = 1b Enable Zoom in Mode
/*****************/
/*(七)充电泵设置*/
WriteCmd(0x8d); //(电荷泵设置,VBAT = 3.3V~4.2V to Charge Pump VCC = 7.0V~7.5V,使oled点亮)
WriteCmd(0x14); //(使能电荷泵)
WriteCmd(0xe3); //(空操作)
/***************/
WriteCmd(0xaf); //(turn on oled panel)
}
以上便是所有指令的详解,读者需要对这一部分内容重点掌握,其中IIC协议可以百度到较为详细全面的资料,在这里就不复述了。
三、高阶
在对oled进行正确配置并点亮后,就需要对其原理进行深刻理解了,下面是4 pin oled的原理图,着重学要注意的是画圈部分(市面上绝大多数4 pin oled都是使用这张原理图来封装的):
有需要的读者还可以查阅OLED数据表了解其电气特性,附上一张封装图:
四、网传程序存在的不合理问题
在对上述进行理解后,我们来讨论一下在网上传播的oled驱动程序存在的问题。
我们来看一下网上流传的这段程序:
void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
对比看一下英文手册:
意思是在正常显示模式下需要以下三个步骤来定义起始点坐标:
1. 通过命令 B0h 到 B7h 来设置目标显示位置的页开始地址
2. 通过 00h~0Fh 来设置低开始列地址的指针
3. 通过命令 10h~1Fh 来设置高开始列地址
其中三条指令可以互换顺序,细心的读者可能发现问题了:
OLED_SetPos函数中这条指令:
WriteCmd((x&0x0f)|0x01);
设置低地址时会导致二进制数据最后一位始终置1,这将会导致在设置坐标点时只能设置奇数列!
正确的书写形式是:
WriteCmd(x&0x0f);
即函数应该为:
void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd(x&0x0f);
}
我们通过下面这个例子来加深理解:
在main函数中加入如下代码:
for(i=0;i<128;i++)
{
OLED_DrawPoint(i,31,1);
}
其中OLED_DrawPoint函数为画点函数:
/******************************************************
Function: void OLED_DrawPoint(u8 x,u8 y,u8 t)
Description: 在屏幕上任一位置画点,填充或清空
Input: x:0~127
y:0~63
t:1 填充 0,清空
******************************************************/
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
u8 data; //data1当前点的数据
if(x>127||y>63)return;// 超出范围了
OLED_SetPos(x,y/8);
data = 0x01<<(y%8);
if(t==0) data=0x00;
WriteDat(data);
}
其中u8为数据类型宏定义:
typedef uint8_t u8;
在修正前,oled会断点显示,即只显示奇数列:
修正后显示正常:
后记——凡尘
时间匆忙,未能用篇幅举代码实例帮大家理解和校对错误,更多的是结构和整体压缩的内容,如有错别字和内容不合理的地方欢迎大家批评指正,如有想深入探讨或是项目需求的朋友欢迎加入QQ群:QQ群:
星尘Arduino社区:630581178
星尘STM8/32社区:700155855
笔者邮箱:fanchen995@gmail.com