advertisement

2013年12月1日

89C51 LCD顯示控制

題目:

掃描minikey,將按鍵值依下列方式顯示於LCD上:
  1. '1'鍵,將'1'顯示於第一列奇數位置和第二列偶數位置
  2. '2'鍵,將'2'顯示於第一列偶數位置和第二列奇數位置
  3. '3'鍵,將'3'顯示於第一列奇數位置和第二列偶數位置
  4. '4'鍵,將'4'顯示於第一列偶數位置和第二列奇數位置

功能描述:


LCD顯示器,由S1~S4開關控制,各開關功能如下:

按下S1 LCD顯示:
按下S2 LCD顯示:

按下S3 LCD顯示:

按下S4 LCD顯示:

流程圖:

初步程式碼:

LCD_BUS EQU P0

LCD_RS EQU P2.1

LCD_EN EQU P3.7

OUT_S1S2 EQU P3.2;定義OUT_S1S2為89C51的P3.2腳

OUT_S3S4 EQU P3.3;定義OUT_S3S4為89C51的P3.3腳

IN_S1S3 EQU P3.4;定義IN_S1S3為89C51的P3.4腳

IN_S2S4 EQU P3.5;定義IN_S2S4為89C51的P3.5腳

KEYNUM EQU 35H;定義KEYNUM為35H

KEYFLAG EQU 20H.1;定義KEYFLAG為20H.1

FLAG_S1S2 EQU 20H.2;定義FLAG_S1S2為20H.2

FLAG_S3S4 EQU 20H.3;定義FLAG_S3S4為20H.3

MOV KEYNUM,#0

CLR KEYFLAG

CALL INITLCD

LOOP13: CALL KEY

JNB KEYFLAG,LOOP13

MOV R1,KEYNUM

CJNE R1,#1,LOOP9

MOV DPTR,#STR1

MOV B,#00H

JMP LOOP8

LOOP9: CJNE R1,#2,LOOP10

MOV DPTR,#STR2

MOV B,#01H

JMP LOOP8

LOOP10: CJNE R1,#3,LOOP11

MOV DPTR,#STR3

MOV B,#00H

JMP LOOP8

LOOP11: CJNE R1,#4,LOOP13

MOV DPTR,#STR4

MOV B,#01H

LOOP8: CALL INITLCD

MOV A,#1

LOOP14: MOV R0,#8;;;;;;;

LOOP5: ; MOV A,#1

PUSH ACC

CALL GOXY

CALL PRTSTR

MOV A,B

ADD A,#02H

MOV B,A

POP ACC

DJNZ R0,LOOP5

CJNE A,#1,LOOP13

INC A

MOV R2,B

CJNE R2,#11H,LOOP15;;;;

MOV B,#00H

JMP LOOP14

LOOP15: MOV B,#01H

JMP LOOP14

JMP LOOP13

STR1: DB '1'

STR2: DB '2'

STR3: DB '3'

STR4: DB '4'

PRTSTR: CLR A

MOVC A,@A+DPTR

CALL WRDR

RET


WRDR: SETB LCD_EN

SETB LCD_RS;;

MOV LCD_BUS,A

CLR LCD_EN

MOV R7,#1

CALL DELAY

RET


WRIR: SETB LCD_EN

CLR LCD_RS;;

MOV LCD_BUS,A

CLR LCD_EN

MOV R7,#1

CALL DELAY

RET


INITLCD: MOV A,#38H

CALL WRIR

MOV A,#0DH

CALL WRIR

MOV A,#06H

CALL WRIR

MOV A,#01H

CALL WRIR

MOV R7,#40

CALL DELAY

RET


GOXY: ANL B,#00001111B

CJNE A,#1,LOOP1

MOV A,B

ADD A,#10000000B

CALL WRIR

RET

LOOP1: MOV A,B

ADD A,#11000000B

CALL WRIR

RET


DELAY: MOV TMOD,#00000001B

LOOP: MOV TH0,#HIGH(65536-50)

MOV TL0,#LOW(65536-50)

SETB TR0

JNB TF0,$

CLR TR0

CLR TF0

DJNZ R7,LOOP

RET


KEY: CLR OUT_S1S2;將OUT_S1S2變為低0電位,

MOV R7,#1;設定DELAY時間

CALL DELAY;呼叫DELAY副程式,目的是為了使OUT_S1S2有時間變為低0電位

JB IN_S1S3,KEY2;若IN_S1S3為1高電位,則表示無按S1開關,並跳至KEY2,反之有按S1開關,成為低0電位,則往下執行

MOV KEYNUM,#1;將KEYNUM放入1值,表示有按S1開關

SETB KEYFLAG

JMP RMIN;跳至RMIN副程式

KEY2: JB IN_S2S4,KEY3;若IN_S2S4為1高電位,則表示無按S2開關,並跳至KEY3,反之有按S2開關成為低0電位,則往下執行

MOV KEYNUM,#2;將KEYNUM放入2值,表示有按S2開關

SETB KEYFLAG

JMP RMIN;跳至RMIN副程式

KEY3: SETB OUT_S1S2;將OUT_S1S2設為1,防止按下S1S2開關時使IN_S1S3 IN_S2S4變成0

CLR OUT_S3S4;將OUT_S1S2變為低0電位

MOV R7,#1;設定DELAY時間

CALL DELAY;呼叫DELAY副程式,目的是為了使OUT_S3S4有時間變為低0電位

JB IN_S1S3,KEY4;若IN_S1S3為1高電位,則表示無按S3開關,並跳至KEY4,反之有按S3開關成為低0電位,則往下執行

MOV KEYNUM,#3;將KEYNUM放入3值,表示有按S3開關

SETB KEYFLAG

JMP RMIN;跳至RMIN副程式

KEY4: JB IN_S2S4,KEY5;若IN_S2S4為1高電位,則表示無按S4開關,並跳至KEY5,反之有按S4開關成為低0電位,則往下執行

MOV KEYNUM,#4;將KEYNUM放入4值,表示有按S4開關

SETB KEYFLAG

JMP RMIN

KEY5: CLR KEYFLAG

RMIN: SETB OUT_S1S2;設回初值

SETB OUT_S3S4;設回初值

RET


END


修正程式碼:



;=============================參數定義=================================

LCD_BUS EQU P0;定義LCD_BUS為89C51的P0腳

LCD_RS EQU P2.1;定義OUT_S1S2為89C51的P3.2腳

LCD_EN EQU P3.7;定義OUT_S1S2為89C51的P3.2腳

OUT_S1S2 EQU P3.2;定義OUT_S1S2為89C51的P3.2腳

OUT_S3S4 EQU P3.3;定義OUT_S3S4為89C51的P3.3腳

IN_S1S3 EQU P3.4;定義IN_S1S3為89C51的P3.4腳

IN_S2S4 EQU P3.5;定義IN_S2S4為89C51的P3.5腳

KEYNUM EQU 35H;定義KEYNUM為35H

KEYFLAG EQU 20H.1;定義KEYFLAG為20H.1

;==============================主程式==================================

MOV KEYNUM,#4;設定KEYNUM初始值

CLR KEYFLAG;設定KEYFLAG初始值

CALL INITLCD;初始值LCD

LOOP13: CALL KEY;呼叫KEY副程式

JNB KEYFLAG,LOOP13;若無按按鍵,則繼續偵測按鍵

LOOP8: CALL INITLCD;初始值LCD

MOV A,#1;表示第一行

LOOP14: MOV R0,#8;一列有0~15行而我每行將顯示8個字元

LOOP5: PUSH ACC;暫存A之值,將A值放入堆疊區

CALL GOXY;呼叫GOXY副程式

MOV A,KEYNUM;將KEYNUM放入A,使DPTR+KEYNUM指向所要顯示的字元

CALL PRTSTR;呼叫PRTSTR副程式

MOV A,B;將B複製到A,為了把B值加2已指向另一行位址在顯示出字元

ADD A,#02H;增二

MOV B,A;A複製回B

POP ACC;從堆疊區取回

DJNZ R0,LOOP5;將R0減一不等於0,跳至LOOP5

CJNE A,#1,LOOP13;比較A=1往下執行,不相等跳至LOOP13表示第一行與第二行都已顯示完成

INC A;將A增一,表示第二行

MOV R2,B

CJNE R2,#11H,LOOP15;因B表第幾行,且一行只到0F,故若一開始B為01H時,加八次2時為01H,且顯示完後還會在執行到一次B+2,故為11H時往下執行

MOV B,#00H;第二行給為從00H開始顯示

JMP LOOP14

LOOP15: MOV B,#01H

JMP LOOP14

STR: DB '1234'

;-----------------------------顯示字元--------------------------------

PRTSTR: MOVC A,@A+DPTR

CALL WRDR

RET

;-------------------------寫入資料暫存器------------------------------

WRDR: SETB LCD_EN

SETB LCD_RS

MOV LCD_BUS,A

CLR LCD_EN

MOV R7,#1

CALL DELAY

RET

;-------------------------寫入指令暫存器------------------------------

WRIR: SETB LCD_EN

CLR LCD_RS

MOV LCD_BUS,A

CLR LCD_EN

MOV R7,#1

CALL DELAY

RET

;---------------------------初始LCD-----------------------------------

INITLCD: MOV A,#38H

CALL WRIR

MOV A,#0DH

CALL WRIR

MOV A,#06H

CALL WRIR

MOV A,#01H

CALL WRIR

MOV R7,#40

CALL DELAY

RET

;---------------------------移動游標----------------------------------

GOXY: ANL B,#00001111B

CJNE A,#1,LOOP1

MOV A,B

ADD A,#10000000B

CALL WRIR

RET

LOOP1: MOV A,B

ADD A,#11000000B

CALL WRIR

RET

;-----------------------------延遲------------------------------------

DELAY: MOV TMOD,#00000001B

LOOP: MOV TH0,#HIGH(65536-50)

MOV TL0,#LOW(65536-50)

SETB TR0

JNB TF0,$

CLR TR0

CLR TF0

DJNZ R7,LOOP

RET

;----------------------------按鍵偵測----------------------------------

KEY: CLR OUT_S1S2;將OUT_S1S2變為低0電位,

MOV R7,#1;設定DELAY時間

CALL DELAY;呼叫DELAY副程式,目的是為了使OUT_S1S2有時間變為低0電位

JB IN_S1S3,KEY2;若IN_S1S3為1高電位,則表示無按S1開關,並跳至KEY2,反之有按S1開關,成為低0電位,則往下執行

MOV KEYNUM,#0;將KEYNUM放入0值,表示有按S1開關,且為了指向DPTR位址,固原本設計為1改為0

MOV DPTR,#STR

MOV B,#00H

SETB KEYFLAG;用來判定是否有按開關

JMP RMIN;跳至RMIN副程式

KEY2: JB IN_S2S4,KEY3;若IN_S2S4為1高電位,則表示無按S2開關,並跳至KEY3,反之有按S2開關成為低0電位,則往下執行

MOV KEYNUM,#1;將KEYNUM放入1值,表示有按S2開關

MOV DPTR,#STR

MOV B,#01H

SETB KEYFLAG;用來判定是否有按開關

JMP RMIN;跳至RMIN副程式

KEY3: SETB OUT_S1S2;將OUT_S1S2設為1,防止按下S1S2開關時使IN_S1S3 IN_S2S4變成0

CLR OUT_S3S4;將OUT_S1S2變為低0電位

MOV R7,#1;設定DELAY時間

CALL DELAY;呼叫DELAY副程式,目的是為了使OUT_S3S4有時間變為低0電位

JB IN_S1S3,KEY4;若IN_S1S3為1高電位,則表示無按S3開關,並跳至KEY4,反之有按S3開關成為低0電位,則往下執行

MOV KEYNUM,#2;將KEYNUM放入2值,表示有按S3開關

MOV DPTR,#STR

MOV B,#00H

SETB KEYFLAG;用來判定是否有按開關

JMP RMIN;跳至RMIN副程式

KEY4: JB IN_S2S4,KEY5;若IN_S2S4為1高電位,則表示無按S4開關,並跳至KEY5,反之有按S4開關成為低0電位,則往下執行

MOV KEYNUM,#3;將KEYNUM放入3值,表示有按S4開關

MOV DPTR,#STR

MOV B,#01H

SETB KEYFLAG;用來判定是否有按開關

JMP RMIN

KEY5: CLR KEYFLAG;用來判定是否有按開關

RMIN: SETB OUT_S1S2;設回初值

SETB OUT_S3S4;設回初值

RET

END


結果:符合題目要求!!

由老師提示的副程式WRIRWRDRGOXYPRTSTRDELAY與使用KEY來判別是否有按開關,大約花半天的時間初步把主程式碼撰寫完成,在剛開始撰寫程式碼時,遇到第一個大問題就是如何有效率的執行迴圈與縮短程式碼,用簡單得方式來想,只要我在程式碼裡寫入' 1 1 1 1 1 1 1 1'共八行即可印出與題目要求,但從題目中慢慢觀察,我發現不論按下哪個開關,其顯示於LCD上必定都會空一個空格,也就是LCD每個列位置顯示完後,其位址剛好都會加2,或許我可以利用這點增加程式碼執行效率,再來遇到的問題,一開始想錯以為位址以用RL的方式來增加位址,應該以ADD的方式增加位址,另外當其位址超過9以後,其值為ABCDEF,而不是10 11等。
再來最後的程式碼精簡化,單我初步程式碼與流程圖撰寫完成後,發現我的程式碼,在判別按鍵開關時,並指定DPTR的位址,這部份的程式碼可以移動KEY副程式,便可減少很多無效程式迴圈,另外我還發現,我可以把KEYNUM全部設定的值減一,這樣我可以利用KEYNUM來作為指定DPTR所指向的位址,所以我可以把STR1~STR4,合併成一個STR而達到縮減程式碼目的。


沒有留言:

張貼留言

文章有誤或有問題麻煩您留言告知! 謝謝您~~