51单片机 | 实现SMG12864液晶显示器操作
————————————————————————————————————————————
SMG12864液晶显示器
- 128*64个点位,可以显示图形或8*4个汉字
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
引脚功能:
基本结构:
- 行驱动器:IC3
- 列驱动器:IC1,IC2
- 128*64全点阵液晶显示器
内部结构:IC1,IC2,IC3含有的主要功能器件
- 指令寄存器 IR:写指令时将指令存入IR
- 数据寄存器 DR:写数据时将数据存入DR
- 忙标志 BF:BF=1表示模块在内部操作,此时不接收外部指令和数据。BF=0时为准备状态,可以接收指令和数据
- 显示控制触发器 DDF:屏幕显示开和关的控制,DFF=1开,DFF=0关
- 显示数据内存 DDRAM:存储图形显示数据
- XY地址计数器:9位计数器(高3位为X,低6位为Y)作为DDRAM指针,X只能通过指令设置,Y地址计数器具有指令循环功能,读写数据完成后Y地址自动+1
- Z地址计数器:6位计数器,用于显示行扫描同步,完成一行扫描后Z地址自动+1,向下一行扫描。RST复位后,Z地址归零。显示屏幕的起始行由此指令控制。此模块的DDRAM共64行,屏幕可以循环滚动显示64行。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
操作时序:
读操作时序图:
写操作时序图:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
操作指令:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DDRAM与地址、显示位置的关系 p.s.CS2对应左屏,CS1对应右屏
- DDRAM与页地址的对应关系
在图中可以看到每个CS由8页(64行)*64列组成
- DDRAM与地址显示位置的对应关系
- DDRAM与起始行对应关系
DDRAM行 |
62 |
63 |
0 |
1 |
... |
29 |
屏幕显示行 |
1 |
2 |
3 |
4 |
... |
32 |
————————————————————————————————————————————
SMG12864液晶显示器使用实例
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
设计要求:一行显示八个字,滚动显示
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
实现思路:
- 动态显示:利用余晖效应循环显示八个汉字
- 滚动显示:每次显示完毕后在下一次循环中显示在下一行(建立新的初始行,每次循环将初始行+1)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
主体模块:
- 初始化LCD,清屏
- 动态显示
- 滚动控制
- 开始行建立
- 开始行+1
- 汉字显示
- 第一个字
- 第二个字
- ...
- 第八个字
- 全屏显示
子模块:
- 初始化
- 清屏
- 汉字显示
- 选择左右屏(前四个字在左屏,后四个字在右屏)
- 显示字的上半部分
- 建立行(页)
- 建立列
- 遍历写入字库对应位置
- 显示字的下半部分
- 同上半部分
LCD基本操作:
- 读状态
- rs=0;rw=1;
- do循环(BUSY为1忙碌时)
- P0置空
- 打开使能
- 读入P0
- 关闭使能
- 检测BF标志位状态(引脚DB7)
- 写指令:
- 读状态
- rs=0;rw=0;
- 写入指令
- E负跳变沿
- 写数据
- 读状态
- rs=0;rw=1;
- 写入数据
- E负跳变沿
- 选择屏幕
- 全屏显示:CS1/2=0;
- 右屏显示(CS1控制):CS2高电平,CS1低电平
- 左屏显示(CS2控制):CS1高电平,CS2低电平
- 清屏:
- 选择全屏
- 循环遍历8页
- 建立该页
- 建立第0行
- 遍历该页的所有列写入内容
- 开始行建立:指令码 11xxxxxx
- 行(页)建立:指令码 10111xxx
- 列建立:指令码 01xxxxxx
- 显示开闭:指令码 0011111x
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
清屏过程:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
字显示过程:
- 第1个字(对应字库1~2行)
- 上半部分,第0页(字库第1行)
- 逐列显示 0,1,2,3,4,5,6,7(字库第0,1,2,3,4,5,6,7位)
- 下半部分,第1页(字库第2行)
- 逐列显示 0,1,2,3,4,5,6,7(字库第8,9,10,11,12,13,14,15位)
- 第2个字(对应字库前3~4行)
- 第0页(字库第3行)
- 逐列显示 17,18,19,20,21,22,23,24
- 第1页(字库第4行)
- 逐列显示 25,26,27,28,29,30,31,32
- ...
- 第i个字(对应字库 i*2-1~i*2行)
- 第0页(字库第 i*2-1行)
- 字库第 (i-1)*32,(i-1)*32+1,(i-1)*32+2,(i-1)*32+3...,(i-1)*32+7 位
- 第1页(字库第 i*2行)
- 字库第 (i-1)*32+16,(i-1)*32+17,(i-1)*32+18,(i-1)*32+19...,(i-1)*32+31 位
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
实现代码:
#include <reg52.h>
#include <intrins.h>
#define DATA P0
typedef unsigned char uchar;
sbit rs = P2 ^ ;
sbit rw = P2 ^ ;
sbit en = P2 ^ ;
sbit cs1 = P2 ^ ;
sbit cs2 = P2 ^ ;
uchar code zhCN[] =
{
//每个位置占据1列8行
//第1个字
//上半部分:第1列,第2列,第3列...第8列
0x00, 0x04, 0x04, 0xC4, 0xB4, 0x8C, 0x87, 0x84, 0xF4, 0x84, 0x84, 0x84, 0x84, 0x04, 0x00, 0x00,
//下半部分
0x00, 0x00, 0x20, 0x18, 0x0E, 0x04, 0x20, 0x40, 0xFF, 0x00, 0x02, 0x04, 0x18, 0x30, 0x00, 0x00,
//第2个字
0x20, 0x20, 0x2A, 0x2A, 0xAA, 0x6A, 0x3E, 0x2B, 0xAA, 0xAA, 0xEA, 0xAA, 0x2A, 0x22, 0x20, 0x00,
0x82, 0x82, 0x45, 0x45, 0x25, 0x15, 0x0D, 0xFF, 0x04, 0x0C, 0x14, 0x24, 0x65, 0xC2, 0x42, 0x00,
//第3个字
0x00, 0x00, 0xF8, 0x48, 0x48, 0x48, 0x48, 0xFF, 0x48, 0x48, 0x48, 0x48, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0F, 0x04, 0x04, 0x04, 0x04, 0x3F, 0x44, 0x44, 0x44, 0x44, 0x4F, 0x00, 0x70, 0x00,
//第4个字
0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0xE2, 0x12, 0x0A, 0x06, 0x02, 0x00, 0x80, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x41, 0x81, 0x7F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
//第5个字
0x40, 0x20, 0xD0, 0x4C, 0x43, 0x44, 0x48, 0xD8, 0x30, 0x10, 0x00, 0xFC, 0x00, 0x00, 0xFF, 0x00,
0x00, 0x00, 0x3F, 0x40, 0x40, 0x42, 0x44, 0x43, 0x78, 0x00, 0x00, 0x07, 0x20, 0x40, 0x3F, 0x00,
//第6个字
0x20, 0x24, 0x2C, 0x35, 0xE6, 0x34, 0x2C, 0x24, 0x00, 0xFC, 0x24, 0x24, 0xE2, 0x22, 0x22, 0x00,
0x21, 0x11, 0x4D, 0x81, 0x7F, 0x05, 0x59, 0x21, 0x18, 0x07, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
//第7个字
0x00, 0x04, 0x04, 0x04, 0xFF, 0x54, 0x54, 0x54, 0x54, 0x54, 0xFF, 0x04, 0x04, 0x04, 0x00, 0x00,
0x11, 0x51, 0x49, 0x4D, 0x4B, 0x49, 0x49, 0x7D, 0x49, 0x49, 0x4B, 0x45, 0x4D, 0x59, 0x09, 0x00,
//第8个字
0x10, 0x10, 0x10, 0xFE, 0x10, 0x50, 0x40, 0xFE, 0x20, 0x20, 0xFF, 0x10, 0x10, 0xF8, 0x10, 0x00,
0x20, 0x20, 0x10, 0x1F, 0x08, 0x08, 0x00, 0x3F, 0x40, 0x40, 0x4F, 0x42, 0x44, 0x43, 0x70, 0x00
};
void Delay(uchar m)
{
uchar i;
for (; m > ; --m)
for (i = ; i < ; ++i)
;
}
/* 检查忙碌状态 */
void CheckState()
{
//通过读取BF标志位(引脚9,对应P0^7)状态判断模块是否繁忙
uchar dat;
//读状态下RS=0,R/W=1,E=1
rs = ;
rw = ;
do
{
DATA = 0x00; //RST=0正常,BUSY=0准备状态,ON/OFF=0显示关,
en = ;
_nop_(); //打开使能后检测模块状态
dat = DATA; //状态读入dat
en = ;
dat = 0x80 & dat; // 1000 0000 & dat 通过与操作隔离后7位,仅判断最高位
}
while(dat != 0x00); //P0^7为0时跳出循环,BUSY=0准备状态
}
/* 选择要显示的屏幕 */
void SelectScreen(uchar screen)
{
//CS为低电平时亮
switch(screen)
{
case : //全屏显示
cs1 = ; //IC1控制右半屏
_nop_();
_nop_();
_nop_();
cs2 = ; //IC2控制左半屏
_nop_();
_nop_();
_nop_();
break;
case : //右屏显示
cs1 = ;
_nop_();
_nop_();
_nop_();
cs2 = ;
_nop_();
_nop_();
_nop_();
break;
case : //左屏显示
cs1 = ;
_nop_();
_nop_();
_nop_();
cs2 = ;
_nop_();
_nop_();
_nop_();
break;
}
}
/* 写命令操作 */
void SendCommandToLCD(uchar com)
{
//p.s.在CS1或CS2低电平情况下才能写入命令
CheckState();
rs = ;
rw = ;
DATA = com;
en = ;
_nop_();
_nop_();
en = ;
}
/* 显示开闭 */
void SetOnOff(uchar onoff)
{
//传入0,关显示,00111110|00000000 = 00111110
//传入1,开显示,00111110|00000001 = 00111111
onoff = 0x3E | onoff;
SendCommandToLCD(onoff); //写指令onoff
}
/* 行(页)建立 */
void SetLine(uchar page)
{
//10111000|00000000 = 10111000
//10111000|00000001 = 10111001
//10111000|00000010 = 10111010
//...
//10111000|00000111 = 10111111
//共8页,设置页地址
page = 0xb8 | page;
SendCommandToLCD(page);
}
/* 列建立 */
void SetColumn(uchar column)
{
//01xxxxxx 后六位为列地址
//先通过与操作屏蔽前2位,将地址范围限制在0-63之间
//再通过或操作将列操作指令前两位代入
column = column & 0x3F; //00000000 & 00111111 = 00000000
column = 0x40 | column; //01000000 | 00000000 = 01000000
SendCommandToLCD(column);
}
/* 写入数据 */
void WriteByte(uchar dat)
{
CheckState();
rs = ;
rw = ;
DATA = dat;
en = ;
_nop_();
_nop_();
en = ;
}
/* 清屏 */
void ClearScreen(uchar screen)
{
uchar i, j;
SelectScreen(screen);
for (i = ; i < ; ++i) //书上源码建立16页,此处建立8页正常运行
{
SetLine(i); //建立行
SetColumn(); //建立列
for (j = ; j < ; ++j) //通过遍历该行的64列写入内容,列地址自动+1
WriteByte(0x00);
}
}
/* 设置开始行 */
void SetStartLine(uchar startline)
{
//设置开始行时前两位为11
startline = 0xC0 | startline;
SendCommandToLCD(startline);
}
/* 初始化 */
void InitLCD()
{
CheckState(); //检查LCD状态,是否繁忙,每次操作前都要进行检查
SelectScreen(); //全屏显示,将要显示的屏幕部分置为低电平
SetOnOff(); //关显示,1为开显示,0位关显示
SelectScreen();
SetOnOff(); //开显示
SelectScreen();
ClearScreen(); //0清全屏,1清左屏,2清右屏
SetStartLine(); //从第0行开始
}
/* 显示汉字 */
void Display(uchar screen, uchar page, uchar column, uchar num)
{
uchar i;
SelectScreen(screen); //选择对应要亮的屏
/* 显示字的上半部分 */
SetLine(page); //建立行(页)
column = column & 0x3F; // column & 00111111 屏蔽前两位
SetColumn(column); //建立列
//写入字库第32*num~32*num+16位的数据
//第1个字:0~15,对应字库第1行
//第2个字:32~47,对应字库第3行
//第3个字:64~79,对应字库第5行
//...
for (i = ; i < ; ++i)
WriteByte(zhCN[i + * num]);
/* 显示字的下半部分 */
SetLine(page + );
SetColumn(column);
//写入字库第32*num+16~32*num+32位的数据
//第1个字:16~31,对应字库第2行
//第2个字:48~63,对应字库第4行
//第3个字:80~95,对应字库第6行
//...
for (i = ; i < ; ++i)
WriteByte(zhCN[i + * num + ]);
}
/* 主函数 */
int main()
{
uchar i;
/* 初始化LCD,书上进行了一系列初始化操作,仅清屏的话不影响运行结果 */
// InitLCD();
ClearScreen(); //清屏
/* 动态显示 */
while() //余晖效应
{
for (i = ; i < ; ++i) //如果此处改成10,则在向上移动10行后重新出现在第0行,此处使用64的倍数来实现无限循环(无间断),书上源码循环128次,64次也无影响
{
/* 滚动控制 */
SetStartLine(i); //循环建立开始行,每一次循环都上移1行
/* 汉字显示 */
Display(, , * , ); //左屏,第0页, 0列,第0个字
Display(, , * , ); //左屏,第0页, 16列,第1个字
Display(, , * , ); //左屏,第0页, 32列,第2个字
Display(, , * , ); //左屏,第0页, 48列,第3个字
Display(, , * , ); //右屏,第0页, 64列,第4个字
Display(, , * , ); //右屏,第0页, 80列,第5个字
Display(, , * , ); //右屏,第0页, 96列,第6个字
Display(, , * , ); //右屏,第0页,128列,第7个字
SelectScreen(); //由于显示后四个字符时左屏处于关显示状态,需要打开全屏
Delay();
}
}
}
51单片机 | 实现SMG12864液晶显示器操作的更多相关文章
- 基于51单片机的CAN通讯协议C语言程序
//-----------------------函数声明,变量定义-------------------------------------------------------- #includ ...
- Windows 通用应用尝试开发 “51单片机汇编”总结
一.前言 终于完成windows通用应用“51单片机汇编”,半年前开始玩WindowsPhone开发的第一个真正意义上的App(还很多缺点=_=).开发从1月中旬考完试到今天,期间实习了半个月,玩了几 ...
- [新概念51单片机C语言教程·郭天祥] 1、 基础知识必备
目录: 单片机的大致介绍 1-1.通俗定义 1-2.51系列产品 1-3.标号意思 1-4.引脚介绍 1-5.用C语言开 ...
- 关于51单片机P0口的结构及上拉问题
1.P0作为地址数据总线时,V1和V2是一起工作的,构成推挽结构.高电平时,V1打开,V2截止:低电平时,V1截止,V2打开.这种情况下不用外接上拉电阻.而且,当V1打开,V2截止,输出高电平的时候, ...
- 51单片机 Keil C 延时程序的简单研究
应用单片机的时候,经常会遇到需要短时间延时的情况.需要的延时时间很短,一般都是几十到几百微妙(us).有时候还需要很高的精度,比如用单片机驱动DS18B20的时候,误差容许的范围在十几us以内,不然很 ...
- 51单片机引脚ALE/PROG/PSEN/EA/Vpp
51单片机的4个控制引脚,其中一个引脚是复位引脚(RST/Vpd),RST是复位引脚,当RST输入端保持2个机器周期以上高电平时,就实现复位. ALE/PROG是地址锁存允许信号输出端,在读写外部存储 ...
- 51单片机实现对24C02进行页写、顺序读取并显示验证
//************************************************************************************* //**程序名称:51单 ...
- STM32GPIO口8种模式细致分析(类比51单片机)
关于STM32GPIO口的8种工作模式,我们先引出一些问题? STM32GPIO口如果既要输入又要输出怎么办? 1.浮空输入模式 上图红色的表示便是浮空输入的过程,外部输入时0读出的就是0,外部输入时 ...
- 基于51单片机IIC通信的PCF8591学习笔记
引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...
随机推荐
- for...in与点语法
语法 for...in语句循环一个指定的变量来循环一个对象所有可枚举的属性.如下所示 for (variable in object){ statements } 问题 在实际的使用过程中发现,在fo ...
- likely,unlikely宏与GCC内建函数__builtin_expect()
在 GCC 手册中对 __builtin_expect() 的描述是这样的: 由于大部分程序员在分支预测方面做得很糟糕,所以 GCC 提供了这个内建函数来帮助程序员处理分支预测,优化程序.其第一个参数 ...
- UVALive 6451:Tables(模拟 Grade D)
VJ题目链接 题意:模拟输出表格 思路:模拟……很暴力 代码: #include <cstdio> #include <cstring> #include <cstdli ...
- iOS手势UIGestureRecognizer的使用失效问题
问题:视图正常展示在界面中,父层是放在window上的,底部的一个控件的点击事件失效(所有设置都正常) 解决思路:虽然视图能够正常展示,但是发现父类视图的底部尺寸比子类的视图的尺寸小,也就是说上层视图 ...
- 洛谷 P1060 开心的金明【DP/01背包】
题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:"你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就 ...
- 华农oj Problem L: CreatorX背英语【STL】
Problem L: CreatorX背英语 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 53 Solved: 36 [Submit][Status][ ...
- FZU-2271 X(Floyd)
Problem 2271 X Accept: 303 Submit: 1209Time Limit: 1500 mSec Memory Limit : 32768 KB Problem ...
- Lightoj 1348 Aladdin and the Return Journey (树链剖分)(线段树单点修改区间求和)
Finally the Great Magical Lamp was in Aladdin's hand. Now he wanted to return home. But he didn't wa ...
- HNOI2004 郁闷的出纳员(Splay)
郁闷的出纳员 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的 ...
- 【bzoj4403】【序列统计】不降转升+组合数添项合并
(上不了p站我要死了,侵权度娘背锅) Description 给定三个正整数N.L和R,统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量.输出答案对10^6+3取模的结果. Input ...