鍍金池/ 教程/ 數(shù)據(jù)庫/ 13.2 1602 液晶整屏移動程序
18. RS485 通信與 Modbus 協(xié)議
17.5 A/D 差分輸入信號
15.8 C 語言復合數(shù)據(jù)類型(結(jié)構(gòu)體,共用體,枚舉類型)
16.3 NEC 協(xié)議紅外遙控器
13.1 單片機通信時序解析
14.4 單片機 EEPROM 單字節(jié)讀寫操作時序
13.3 多個 .c 文件的初步認識
18.2 Modbus 通信協(xié)議介紹
15.1 BCD 碼介紹
18.3 單片機 Modbus 多機通信程序設計
18.1 單片機 RS485 通信接口、控制線、原理圖及程序?qū)嵗?/span>
15. 實時時鐘 DS1302
14.7 單片機 I2C 和 EEPROM 的綜合編程
17. 模數(shù)轉(zhuǎn)換與數(shù)模轉(zhuǎn)換
16.2 紅外遙控通信原理
13.2 1602 液晶整屏移動程序
17.6 D/A 輸出
17.7 單片機信號發(fā)生器程序
16.4 溫度傳感器 DS18B20
14.6 單片機EEPROM的頁寫入
13.4 單片機計算器程序設計[詳細]
17.2 A/D(模數(shù)轉(zhuǎn)換)的主要指標
17.4 PCF8591 應用程序
17.1 A/D 和 D/A 的基本概念
17.3 PCF8591硬件接口(電路圖引腳圖)
14.3 單片機 EEPROM 簡介
13.5 單片機串口通信原理和控制程序
15.5 DS1302 寄存器介紹
15.2 單片機 SPI 通信接口
15.6 DS1302 通信時序介紹
14.5 單片機 EEPROM 多字節(jié)讀寫操作時序
16. 紅外通信與 DS18B20 溫度傳感器
14.1 單片機 I2C 時序介紹
15.3 實時時鐘芯片 DS1302 介紹
15.9 單片機電子時鐘程序設計
16.1 紅外光的基本原理
15.4 DS1302 的硬件信息
15.7 DS1302 的 BURST 模式
14.2 單片機 I2C 尋址模式
14. 單片機 I2C 總線與 EEPROM
13. 單片機 1602 液晶與串口的應用實例

13.2 1602 液晶整屏移動程序

我們前邊學第7章點陣 LED 的時候,可以實現(xiàn)上下移動,左右移動等。而對于 1602 液晶來說,也可以進行屏幕移動,實現(xiàn)我們想要的一些效果,那我們來用一個例程實現(xiàn)字符串在 1602 液晶上的左移。每個人都不要只瞪著眼看,一定要認真抄下來,甚至抄幾遍,邊抄邊理解,要想真正學好,一定要根據(jù)我的方法來做。

#include <reg52.h>

#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;

bit flag500ms = 0; //500ms 定時標志
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)
//待顯示的第一行字符串
unsigned char code str1[] = "Kingst Studio";
//待顯示的第二行字符串,需保持與第一行字符串等長,較短的行可用空格補齊
unsigned char code str2[] = "Let's move...";

void ConfigTimer0(unsigned int ms);
void InitLcd1602();
void LcdShowStr(unsigned char x, unsigned char y,
unsigned char *str, unsigned char len);

void main(){
    unsigned char i;
    unsigned char index = 0; //移動索引
    unsigned char pdata bufMove1[16+sizeof(str1)+16]; //移動顯示緩沖區(qū)1
    unsigned char pdata bufMove2[16+sizeof(str2)+16]; //移動顯示緩沖區(qū)2

    EA = 1; //開總中斷
    ConfigTimer0(10); //配置 T0 定時 10 ms
    InitLcd1602(); //初始化液晶

    //緩沖區(qū)開頭一段填充為空格
    for (i=0; i<16; i++){
        bufMove1[i] = ' ';
        bufMove2[i] = ' ';
    }
    //待顯示字符串拷貝到緩沖區(qū)中間位置
    for (i=0; i<(sizeof(str1)-1); i++){
        bufMove1[16+i] = str1[i];
        bufMove2[16+i] = str2[i];
    }
    //緩沖區(qū)結(jié)尾一段也填充為空格
    for (i=(16+sizeof(str1)-1); i<sizeof(bufMove1); i++){
        bufMove1[i] = ' ';
        bufMove2[i] = ' ';
    }

    while (1){
        if (flag500ms){ //每 500 ms 移動一次屏幕
            flag500ms = 0;
            //從緩沖區(qū)抽出需顯示的一段字符顯示到液晶上
            LcdShowStr(0, 0, bufMove1+index, 16);
            LcdShowStr(0, 1, bufMove2+index, 16);
            //移動索引遞增,實現(xiàn)左移
            index++;
            if (index >= (16+sizeof(str1)-1)){
                //起始位置達到字符串尾部后即返回從頭開始
                index = 0;
            }
        }
    }
}
/* 配置并啟動 T0,ms-T0 定時時間 */
void ConfigTimer0(unsigned int ms){
    unsigned long tmp; //臨時變量
    tmp = 11059200 / 12; //定時器計數(shù)頻率
    tmp = (tmp * ms) / 1000; //計算所需的計數(shù)值
    tmp = 65536 - tmp; //計算定時器重載值
    tmp = tmp + 12; //補償中斷響應延時造成的誤差
    T0RH = (unsigned char)(tmp>>8); //定時器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0; //清零 T0 的控制位
    TMOD |= 0x01; //配置 T0 為模式1
    TH0 = T0RH; //加載 T0 重載值
    TL0 = T0RL;
    ET0 = 1; //使能 T0 中斷
    TR0 = 1; //啟動 T0
}
/* 等待液晶準備好 */
void LcdWaitReady(){
    unsigned char sta;
    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do{
        LCD1602_E = 1;
        sta = LCD1602_DB; //讀取狀態(tài)字
        LCD1602_E = 0;
    //bit7 等于1表示液晶正忙,重復檢測直到其等于0為止
    }while (sta & 0x80);
}
/* 向 LCD1602 液晶寫入一字節(jié)命令,cmd-待寫入命令值 */
void LcdWriteCmd(unsigned char cmd){
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* 向 LCD1602 液晶寫入一字節(jié)數(shù)據(jù),dat-待寫入數(shù)據(jù)值 */
void LcdWriteDat(unsigned char dat){
    LcdWaitReady();
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DB = dat;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* 設置顯示 RAM 起始地址,亦即光標位置,(x,y)-對應屏幕上的字符坐標 */
void LcdSetCursor(unsigned char x, unsigned char y){
    unsigned char addr;
    if (y == 0){ //由輸入的屏幕坐標計算顯示 RAM 的地址
        addr = 0x00 + x; //第一行字符地址從 0x00 起始
    }else{
        addr = 0x40 + x; //第二行字符地址從 0x40 起始
    }
    LcdWriteCmd(addr | 0x80); //設置 RAM 地址
}
/* 在液晶上顯示字符串,(x,y)-對應屏幕上的起始坐標,
str-字符串指針,len-需顯示的字符長度 */
void LcdShowStr(unsigned char x, unsigned char y,
        unsigned char *str, unsigned char len){
    LcdSetCursor(x, y); //設置起始地址
    while (len--){ //連續(xù)寫入 len 個字符數(shù)據(jù)
        LcdWriteDat(*str++); //先取 str 指向的數(shù)據(jù),然后 str 自加1
    }
}
/* 初始化 1602 液晶 */
void InitLcd1602(){
    LcdWriteCmd(0x38); //16*2 顯示,5*7 點陣,8位數(shù)據(jù)接口
    LcdWriteCmd(0x0C); //顯示器開,光標關(guān)閉
    LcdWriteCmd(0x06); //文字不動,地址自動+1
    LcdWriteCmd(0x01); //清屏
}
/* T0 中斷服務函數(shù),定時 500 ms */
void InterruptTimer0() interrupt 1{
    static unsigned char tmr500ms = 0;

    TH0 = T0RH; //重新加載重載值
    TL0 = T0RL;
    tmr500ms++;
    if (tmr500ms >= 50){
        tmr500ms = 0;
        flag500ms = 1;
    }
}

通過這個程序,大家首先要學會 for 語句在數(shù)組中的靈活應用,這個其實在數(shù)碼管顯示有效位的例程中已經(jīng)有所體現(xiàn)了。其次,隨著我們后邊程序量的增大,大家得學會多個函數(shù)之間相互調(diào)用的靈活應用,體會其中的奧妙。