文章框架
前言
好吧蒸播,最終我還是決定把LCD和串口通信分開(kāi)寫(xiě)。
首先祝各位新春快樂(lè)萍肆,雞年大吉袍榆。上班的事業(yè)有成,上學(xué)的天天向上塘揣。過(guò)大年呢包雀,還真沒(méi)啥心情碼字。
借著爆竹聲咱扯會(huì)兒LCD(液晶顯示器亲铡,Liquid Crystal Display)才写。今天要實(shí)戰(zhàn)的這款俗稱為L(zhǎng)CD1602,尤其注意這個(gè)1602奖蔓,他說(shuō)明了這款顯示器的顯示能力:每行16個(gè)字符琅摩,共2行,乃字符顯示器(僅ASCII)锭硼。
參數(shù)
顯示容量|芯片工作電壓|工作電流|模塊最佳工作電壓
:---:|:---:|:---:|:---:|:---:
16×2 Char|4.5~5.5V|2.0mA(5.0V)|5.0V
引腳
Vss | Vdd | VO | RS | R/W | E | D0~7 | BLA | BLK |
---|---|---|---|---|---|---|---|---|
接地 | 正極 | 對(duì)比度調(diào)節(jié) | 數(shù)據(jù)(H)/命令(L)選擇端 | 讀(H)/寫(xiě)(L)選擇端 | 使能(Enable)信號(hào) | 數(shù)據(jù)口 | 背光電源正極 | 背光電源負(fù)極 |
LCD上也有一個(gè)單片機(jī),用于控制屏幕顯示蜕劝。我們并不是直接操作那塊屏幕檀头,而是與那個(gè)單片機(jī)交互轰异。
其中D0~7這8個(gè)數(shù)據(jù)口就是用于交互的,為并行傳輸暑始。
指令
因?yàn)槲覀円蚅CD內(nèi)嵌的單片機(jī)交互搭独,所以需要指令。下面所列的東西都是當(dāng)RS為低電平時(shí)發(fā)送的(若為高電平廊镜,就識(shí)別為數(shù)據(jù))牙肝。
- 數(shù)據(jù)指針
第一行 | 第二行 |
---|---|
0x80 | 0xC0 (0x80+0x40) |
- 顯示相關(guān)
模式:
指令 | 功能 |
---|---|
0x38 | 設(shè)置16×2顯示,5x7點(diǎn)陣嗤朴,8位數(shù)據(jù)接口 |
方式:
0 | 0 | 0 | 0 | 1 | D | C | B |
---|---|---|---|---|---|---|---|
||||Display配椭,1:開(kāi)顯示 | Cursor,1:顯示光標(biāo) | Blink雹姊,1:光標(biāo)閃爍 |
0 | 0 | 0 | 0 | 0 | 1 | N | S |
---|---|---|---|---|---|---|---|
|||||Next股缸,1:讀/寫(xiě)一個(gè)字符后,指針自動(dòng)加1 | Shift吱雏,1:寫(xiě)字符時(shí)敦姻,相對(duì)字符靜止的屏幕移動(dòng) |
清屏:
指令 | 功能 |
---|---|
0x01 | 數(shù)據(jù)指針清0且所有指針清空 |
0x02 | 僅數(shù)據(jù)指針清0 |
- 操作
指令 | 功能 |
---|---|
0x10 | 光標(biāo)左移 |
0x14 | 光標(biāo)右移 |
0x18 | 整體左移 |
0x1c | 整體右移 |
電平
簡(jiǎn)單說(shuō)下,邏輯電路中只有高電平和低電平歧杏,也就是程序里面的1和0镰惦。但是在物理層面上,它需要一個(gè)具體的表現(xiàn)犬绒,然后整理成標(biāo)準(zhǔn)旺入。
TTL就是本例中要用到的一種電平,單片機(jī)和LCD通過(guò)引腳傳遞電信號(hào)懂更,從而達(dá)到1和0的傳遞眨业。
TTL中的低電平(0)表現(xiàn)為0V,高電平(1)表現(xiàn)為5V
實(shí)例
界內(nèi)顯示
- 電路
在Proteus里沮协,1602就是LM016L
龄捡,除了沒(méi)有背光燈電源外用法一致(VEE
是對(duì)比度調(diào)節(jié),本例不用)慷暂。
RP1
為上拉電阻聘殖,用于提高電壓。由于這款單片機(jī)的P0引腳組的電壓低于5V行瑞,所以需要上拉至5V奸腺,達(dá)到TTL的標(biāo)準(zhǔn)。
那個(gè)圓形的刻著Volts
字樣的東西是電壓表血久,連接兩個(gè)沒(méi)被上拉電壓的P0引腳突照,具體數(shù)值看后面的演示圖。
- 代碼
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
//coefficient of 1602 display(16 row 2 col. 5*7px per Char)
#define LCD_CLEAR 0x01 //宏定義:清屏
#define DISPLAY_MODE_1602 0x38 //宏定義:1602顯示模式
#define DISPLAY_OFF 0x08 //宏定義:關(guān)顯示
#define DISPLAY_ON_NO_CURSOR 0x0c //宏定義:開(kāi)顯示且無(wú)光標(biāo)
#define DISPLAY_ON_WITH_CURSOR_NO_BLINK 0x0e //宏定義:開(kāi)顯示且有光標(biāo)但不閃爍
#define DISPLAY_ON_WITH_CURSOR_BLINK 0x0f //宏定義:開(kāi)顯示且有光標(biāo)且閃爍
#define AUTO_BACK_STEP 0x04 //宏定義:讀/寫(xiě)時(shí)指針自動(dòng)減1
#define AUTO_NEXT_STEP 0x06 //宏定義:讀/寫(xiě)時(shí)指針自動(dòng)加1
#define AUTO_DISPLAY_MOVE_LEFT 0x07 //宏定義:字符相對(duì)靜止氧吐,整屏左移
#define AUTO_DISPLAY_MOVE_RIGHT 0x05 //宏定義:字符相對(duì)靜止讹蘑,整屏右移
#define ALL_MOVE_LEFT 0x18 //宏定義:屏幕左移
#define ALL_MOVE_RIGHT 0x1c //宏定義:屏幕右移
#define CURSOR_MOVE_LEFT 0x10 //宏定義:光標(biāo)左移
#define CURSOR_MOVE_RIGHT 0x14 //宏定義:光標(biāo)右移
#define FIRST_ROW 0x80 //宏定義:第一行頭地址
#define SECOND_ROW FIRST_ROW+0x40 //宏定義:第二行頭地址
uchar code fst[] = "Hello World!"; //第一行要顯示的數(shù)據(jù)數(shù)組
uchar code sec[] = ""; //第二行要顯示的數(shù)據(jù)數(shù)組
uchar num; //字符計(jì)數(shù)
sbit enable = P0^5; //使能端
sbit RS = P0^7; //數(shù)據(jù)/命令切換
sbit RW = P0^6; //讀/寫(xiě)切換
sbit anode = P0^0; //連接電壓表陽(yáng)極
sbit cathode = P0^1; //連接電壓表陰極
//粗制的延時(shí)器末盔,沒(méi)走一次這個(gè)函數(shù)大約為1ms,適用于11.0592MHz及附近
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
//寫(xiě)命令
void writeCmd(uchar cmd)
{
RS = 0; //切換為寫(xiě)命令模式
P2 = cmd;
delay(1); //注意
enable = 1; //執(zhí)行座慰!
delay(1); //注意
enable = 0; //執(zhí)行完畢陨舱!
}
void writeDat(uchar dat){
RS = 1; //切換為數(shù)據(jù)模式
P2 = dat;
delay(1); //注意
enable = 1;
delay(1); //注意
enable = 0;
}
//初始化函數(shù)
void init(){
anode = 1;
cathode = 0;
RW = 0; //寫(xiě)模式,本例只往LCD寫(xiě)數(shù)據(jù)
enable = 0;
writeCmd(DISPLAY_MODE_1602); //發(fā)送命令:1602模式
writeCmd(DISPLAY_ON_WITH_CURSOR_BLINK); //發(fā)送命令:開(kāi)始顯示并閃爍光標(biāo)
writeCmd(AUTO_NEXT_STEP); //發(fā)送命令:數(shù)據(jù)指針自動(dòng)加1
writeCmd(LCD_CLEAR); //發(fā)送命令:清屏
}
void main(){
init();
writeCmd(FIRST_ROW); //發(fā)送命令:開(kāi)始從第一行寫(xiě)入
for(num=0;num<=12;num++){
writeDat(fst[num]); //發(fā)送數(shù)據(jù)版仔,每次一字節(jié)
}
while(1);
}
好游盲,說(shuō)一下上面代碼中標(biāo)//注意
的地方,全都是delay(1)
蛮粮。
為了什么呢益缎,不是蛋疼,是因?yàn)閱纹瑱C(jī)給LCD傳送信號(hào)時(shí)蝉揍,數(shù)據(jù)是要放在數(shù)據(jù)線上的链峭,要是LCD還沒(méi)讀完單片機(jī)給它發(fā)的啥就把內(nèi)容撤走的話,就會(huì)造成數(shù)據(jù)丟失又沾。
就是這個(gè)道理弊仪,為了傳輸穩(wěn)定,所以延時(shí)一小會(huì)兒杖刷。這個(gè)延時(shí)的數(shù)值需要大家自己去試励饵,并不一定所有的情況都延時(shí)大約1ms就夠的。
- 效果
越界顯示
本例用于顯示字符數(shù)超過(guò)16個(gè)的情況滑燃。
代碼改動(dòng)
uchar code fst[] = "1234567890ABCDEF"; //第一行要顯示的數(shù)據(jù)數(shù)組
uchar code sec[] = "1234567890ABCDEFGHIJK"; //第二行要顯示的數(shù)據(jù)數(shù)組
- 初始化函數(shù)
void init(){
anode = 1;
cathode = 0;
RW = 0;
enable = 0;
writeCmd(DISPLAY_MODE_1602);
writeCmd(DISPLAY_ON_NO_CURSOR); //換成不閃的役听,雖然跟這個(gè)新需求沒(méi)什么聯(lián)系,就是給你演示下效果
writeCmd(AUTO_NEXT_STEP);
writeCmd(LCD_CLEAR);
}
- 主函數(shù)
void main(){
init();
writeCmd(FIRST_ROW); //發(fā)送命令:開(kāi)始從第一行寫(xiě)入
for(num=0;num<=16;num++){
writeDat(fst[num]);
}
writeCmd(SECOND_ROW); //發(fā)送命令:開(kāi)始從第二行寫(xiě)入
for(num=0;num<=20;num++){
writeDat(sec[num]);
}
while(1){
//向左移動(dòng)三次表窘,每次間隔500ms
for(num=0;num<=3;num++) {
writeCmd(ALL_MOVE_LEFT); //發(fā)送命令:整屏左移
delay(500);
}
delay(1000); //暫停大約1s
//向右移動(dòng)三次典予,每次間隔500ms
for(num=0;num<=3;num++) {
writeCmd(ALL_MOVE_RIGHT); //發(fā)送命令:整屏右移
delay(500);
}
delay(3000); //暫停大約3s后開(kāi)始下一輪
}
}
效果
移屏只移3個(gè)字符距離,所以并沒(méi)有把第二行的
K
顯示出來(lái)乐严。
結(jié)語(yǔ)
這次單講LCD的入門(mén)應(yīng)用瘤袖,送給不愛(ài)看春晚的你。前兩天搞定了科二考試昂验,年后準(zhǔn)備科三了捂敌。《扯單》系列的一周目大概還差兩三篇就完結(jié)了既琴,下集預(yù)告:串口應(yīng)用占婉。好了,看完文章實(shí)踐實(shí)踐后就該打麻將打麻將甫恩,該放炮仗放炮仗吧逆济!總之大家吃好玩好。