學完51單片機再來學AVR,感覺很容易上手,LCD1602在學51的時候學過,所以可以直接修改相關的IO口操作即可。現在順便也復習一下。1602可以顯示兩行字符,每行可以顯示16個字符,可惜的是不能顯示中文,在我調試的時候它幫我不少的忙。
一、主要技術參數:
顯示容量: 16X2個字符(兩行,每行16個字符)
模塊工作電源: 4.5~5.5V
模塊工作電流: 2.0mA (5.0V)
模塊最佳工作電源: 5.0V
字符尺寸: 2.95X4.35(WXH)mm
二、IO引腳功能
LCD1602A模塊引腳功能 |
|||||
編號 |
符號 |
引腳說明 |
編號 |
符號 |
引腳說明 |
1 |
VSS |
電源地(模塊供電) |
9 |
D2 |
Data I/O |
2 |
VDD |
電源正極(模塊供電) |
10 |
D3 |
Data I/O |
3 |
VL |
接在滑動電阻可以調節對比度 |
11 |
D4 |
Data I/O |
4 |
RS |
數據/命令選擇端 (H/L) |
12 |
D5 |
Data I/O |
5 |
R/W |
讀/寫選擇端 (H/L) |
13 |
D6 |
Data I/O |
6 |
E |
使能信號(通知芯片讀取數據) |
14 |
D7 |
Data I/O |
7 |
D0 |
Data I/O |
15 |
BLA |
模塊背光燈正極 |
8 |
D1 |
Data I/O |
16 |
BLK |
模塊背光燈負極 |
三、基本操作時序:
讀狀態:輸入:RS=L、RW=H、E=H 模塊輸出:狀態字=D0~D7
寫指令:輸入:RS=L、RW=L、D0~D7=指令碼、E=H 模塊輸出:無
讀數據:輸入:RS=H、RW=H、E=H 模塊輸出:數據=D0~D7
寫數據:輸入:RS=H、RW=L、D0~D7=數據、E=H 模塊輸出:無
狀態字說明(因為單片機的速度可能快過1602,所以需要判斷當1602是否在忙,或者延時。) |
|||||||
STA7 |
STA6 |
STA5 |
STA4 |
STA3 |
STA2 |
STA1 |
STA0 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
STA0-6:當前數據地址指針的數值 |
|||||||
STA7 |
主要用來檢查1602模塊的是否可以寫入或讀出操作 |
1:禁止 0:允許 |
|||||
四、指令說明(這指令是用來設置1602顯示的方式)
顯示模式設置 |
||||||||
指令碼 |
功能 |
|||||||
0 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
設置16X2顯示,5X7點陣,8位數據接口 |
顯示開/關及光標設置 |
||||||||
指令碼 |
功能 |
|||||||
0 |
0 |
0 |
0 |
1 |
D |
C |
B |
D=1 開顯示 D=0 關顯示 |
0 |
0 |
0 |
0 |
0 |
1 |
N |
S |
N=1 當讀或寫一個字符后地址指針加1,且光 |
五、時序圖
寫指令時序,RS拉低表示寫指令,R/W拉低表示寫操作,給DB0-DB7賦值指令數據并保持>40ns時間,將E拉高表示數據已經準備好保持>150ns讓1602讀取,將E拉低,并延時>10ns一次寫操作完畢。
寫數據時序,RS拉高表示寫數據,R/W拉低表示寫操作,給DB0-DB7賦值指令數據并保持>40ns時間,將E拉高表示數據已經準備好保持>150ns讓1602讀取,將E拉低,并延時>10ns一次寫操作完畢。
六、電路圖
由于只是寫操作,所以R/W直接接地即可。
PORTA接1602的8位數據引腳
PORTD5接1602的RS引腳 區分數據或命令:H數據,L命令
PORTD4接1602的E引腳 高脈沖使能,使能脈寬最小150ns
七、完整代碼。
// 代碼功能:驅動LCD1602顯示屏
// 平臺:DB-51 Ver2.1開發板
// 控制芯片:ATmega16A
// 時鐘:片內時鐘
// 設計者:L、QQ:1007566569
// 2013-8-10
//*************************************** 包含必備文件 *******************************************************
#ifndef _STRING_H
#include "string.h"
#endif
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//*************************************** 移植必須修改 *******************************************************
// 共占用 PORTA一組IO 、PORTD中第4、5 IO 由于只是寫操作,所以R/W直接接地即可。
#define _1602_DATA PORTA // 接1602的8位數據引腳
#define _1602_CMDIO PORTD // 控制線所對應的IO口
#define _1602_CMDIO_RS 5 // 接1602的RS引腳 區分數據或命令:H數據,L命令
#define _1602_CMDIO_E 4 // 接1602的E引腳 高脈沖使能,使能脈寬最小150ns
/**************************************************
設計者:L、QQ:1007566569
函數:自定義函數 void My_SetBit(unsigned char *Date, unsigned char Wei, unsigned char ms)
參數:Date:寄存器地址 Wei:更改指定位 ms:若為1則將指定位置1 若為0則將指定位置0
返回值:無返回值,函數里面會更改寄存器值
例子:My_SetBit((unsigned char*)&PORTB, 2, 1); 將PORTB寄存器中的第2位置1
函數描述:
修改指定寄存器中的指定位,不影響其他位。
**************************************************/
/*** 第二版本 ***/
void My_SetBit(unsigned char *Date, unsigned char Wei, unsigned char ms)
{
(((ms) == (0)) ? (*Date &= ~(0x1<<Wei)) : (*Date |= (0x1<<Wei)));
}
/* 延時函數 */
void delay_50us_1602(unsigned int x)
{
unsigned char y;
for(;x>0;x--)
for(y=19;y>0;y--);
}
/* 寫入指定命令 由于是寫操作 R/W 可以直接接地保持低電平即可*/
void lcdwrcom_1602(unsigned char cdat)
{
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0); // E置0,即將改變數據。不允許1602讀取數據(IO口狀態)
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_RS, 0); // RS拉低 表示是命令
_1602_DATA = cdat; // 將命令送至與1602的8位數據口相連接的IO口
delay_50us_1602(4); // 數據建立時間要 >40ns
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 1); // E使能,命令已經準備好,通知1602讀取命令(IO口狀態)
delay_50us_1602(8); // E高電平保持時間要 >150ns,讓1602有時間讀取完整
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0); // E置0,不允許1602讀取數據(IO口狀態)可能改變。
delay_50us_1602(2); // 根據時序圖,讓數據再保持一小段時間 >10ns
}
/* 將指定數據寫入顯示屏 由于是寫操作 R/W 可以直接接地保持低電平即可*/
void lcdwrdata_1602(unsigned char dat)
{
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0); // E置0,即將改變數據。不允許1602讀取數據(IO口狀態)
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_RS, 1); // RS拉高 表示是數據
_1602_DATA = dat; // 將命令送至與1602的8位數據口相連接的IO口
delay_50us_1602(4); // 數據建立時間要 >40ns
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 1); // E使能,命令已經準備好,通知1602讀取命令(IO口狀態)
delay_50us_1602(8); // E高電平保持時間要 >150ns,讓1602有時間讀取完整
My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0); // E置0,不允許1602讀取數據(IO口狀態)可能改變。
delay_50us_1602(2); // 根據時序圖,讓數據再保持一小段時間 >10ns // 根據時序圖,讓數據保持一小段時間
}
/* 初始化 1602 */
void lcd_init_1602()
{ // 具體含義請參考 1602官方使用手冊
lcdwrcom_1602(0x38);//0x38設置顯示模式為:16X2 顯示,5X7 點陣,8 位數據接口
lcdwrcom_1602(0x0C);//打開顯示光標閃爍
lcdwrcom_1602(0x06);//顯示光標移動設置
lcdwrcom_1602(0x01);//顯示清屏
}
/* 綜合函數 API */
/* 參數:Show_1602(第一行顯示的數據, 第二行顯示的數據, 每個字符顯示的時間)*/
void Show_1602(unsigned char Text_1[], unsigned char Text_2[], unsigned int Time)
{
unsigned char n;
unsigned int Tmp;
unsigned char TmpA, TmpC, TmpD;
// 備份IO口原來的配置以備復原,避免影響其他模塊代碼
TmpA = DDRA;
TmpC = DDRC;
TmpD = DDRD;
// 端口初始化
DDRA = 0xFF;
DDRC = 0xC0;
DDRD = 0x30;
// 1602 初始化
lcd_init_1602();
lcdwrcom_1602(0x80+0x00); // 設置要顯示(寫入)的位置 0x80-0x8F 第一行的位置
Tmp = strlen(Text_1);
if(Tmp)
{
for(n=0; n<Tmp; n++)
{
lcdwrdata_1602(Text_1[n]); // 一個字節一個字節寫入
delay_50us_1602(Time); // 延時指定時間 可以達到一個字符一個字符慢慢依次顯示的效果
}
}
lcdwrcom_1602(0x80+0x40); // 設置要顯示(寫入)的位置 0xC0-0xCF 第二行的位置
Tmp = strlen(Text_2);
if(Tmp)
{
for(n=0; n<Tmp; n++)
{
lcdwrdata_1602(Text_2[n]); // 一個字節一個字節寫入
delay_50us_1602(Time); // 延時指定時間 可以達到一個字符一個字符慢慢依次顯示的效果
}
}
// 恢復端口原來的設置避免影響其他模塊的使用。
DDRA = TmpA;
DDRC = TmpC;
DDRD = TmpD;
}
/* 寫完這個函數后,靈光一現!擦,被誤導了!霖鋒老師和郭天祥老師是不是都寫懵
了,搞那么復雜,靠 一個 sprintf 就能搞定。
void Show_1602_Int(unsigned int Shuzi, unsigned int Time)
{
unsigned char Tdate[]="0123456789";
char Text[33] = {0};
unsigned int Tmp;
unsigned int Weishu = 10;
char Textlen = 0;
// 判斷位數 ↓
if(Shuzi < 10 ) // 如果是一位數 則不必拆分 直接引用
{
Text[Textlen] = Tdate[Shuzi];
Show_1602_Char(Text, "", Time);
return ;
}
while(1) // 如果是兩位數以上 則先判斷幾位
{
if(Shuzi>=Weishu && Shuzi<Weishu*10)
break;
Weishu*=10;
}
// 判斷位數 ↑
// 將每一位拆分翻譯 ↓
for(Textlen=0; Weishu; Textlen++)
{
Tmp = Shuzi/Weishu;
Text[Textlen] = Tdate[Tmp];
Shuzi = Shuzi % Weishu;
Weishu /= 10;
}
// 將每一位拆分翻譯 ↑
Show_1602_Char(Text, "", Time);
}
*/