;16x2 LCD測試程式 ;在LCD上顯示時間 LCD_RS EQU P1.0 ;定義LCD接線位置 LCD_RW EQU P1.1 LCD_E EQU P1.2 LCD_DATA EQU P2 KB EQU P0 KB_BUF EQU 34H SUBSEC EQU 30H SECOND EQU 31H MINUTE EQU 32H HOUR EQU 33H RPT EQU 35H ;控制按鍵重複顯示時間 ORG 0H JMP START ORG 0BH JMP TIMER0_INT ;設定50ms 中斷一次 START: ; MOV R5,#1 ;如果電源關掉重開時,LCD無法正常顯示 ; CALL DELAY ;請加上此延遲程式 MOV TMOD,#00000001B ;設定TIMER0工作於MODE 1 MOV TH0,#(65536-49990) / 256 ;設定計數約50ms MOV TL0,#(65536-49990) MOD 256 SETB TR0 ;啟動計時器 MOV IE,#10000010B ;啟動TIMER0中斷 MOV SUBSEC,#20 ;設定中斷20次 1秒鐘 MOV SECOND,#0 MOV MINUTE,#0 MOV HOUR,#0 CLR LCD_RW ;習慣上直接將LCD的RW接腳直接接地, MOV A,#00111000B ;設定LCD介面為8位元, 2行, 5x7字型 CALL WRIR MOV A,#00001100B ;設定要LCD顯示, 不要有游標, 不要閃爍 CALL WRIR MOV A,#00000110B ;設定寫資料進入LCD時, 顯示器不要動, 游標向右移一格 CALL WRIR MOV A,#1 ;清除LCD CALL WRIR MOV R5,#1 ;延遲等清除LCD命令處理完畢 CALL DELAY MOV A,#10001000B ;游標移到第一行第8個字 CALL WRIR MOV DPTR,#str1 ;指標指到str1, 顯示初值00:00:00 CALL PRT_STR ;以下程式為鍵盤掃描 AGAIN: MOV R4,#4 MOV KB_BUF,#11111110B ;掃描訊號 NEXT: MOV KB,KB_BUF ;送至鍵盤 MOV A,KB ;讀回鍵盤訊號 ANL A,#11110000B ;將後4位元清為0 CJNE A,#11110000B,RECHECK ;判斷是否按鍵盤(前4位元有不為0的情況) MOV A,KB_BUF ;若無則將KB_BUF內的資料左旋 RL A ; MOV KB_BUF,A ;準備掃描下一行 DJNZ R4,NEXT ;R4內的資料減一,判斷是否掃瞄4次 JMP AGAIN ;重新開始掃描 RECHECK: MOV R5,#10 ;延遲,除彈跳 CALL DELAY MOV RPT,#16 ;設定按鍵第一次重複所需的時間,約0.8秒(0.05*16) ;中斷程式中每0.5秒減1 MOV A,KB ;重新讀回按鍵訊號 ANL A,#11110000B CJNE A,#11110000B,KEY_IN ;判斷是否按鍵盤 JMP AGAIN ;若無則為彈跳訊號, 重新開始掃描 KEY_IN: MOV A,KB ;讀回鍵盤訊號 CALL KEY ;執行判斷按鍵副程式 WAIT: MOV A,RPT JNZ WK ;判斷鍵盤重複計數器使否減到0 MOV RPT,#3 ;設定按鍵第二次以後重複所需時間 JMP KEY_IN WK: MOV A,KB ;重新讀回按鍵訊號 ANL A,#11110000B CJNE A,#11110000B,WAIT ;判斷按鍵是否放開 JMP AGAIN ;------ 判斷按鍵副程式, 並將該鍵對應的值顯示在LCD ----------- KEY: CJNE A,#01111110B,S13;判斷是否按0 MOV A,#10001110B ;游標移到'秒'的位置 CALL WRIR MOV SECOND,#0 ;秒歸0 MOV A,SECOND CALL DISPLAY RET S13: CJNE A,#01111101B,S14 ;判斷是否按1 INC MINUTE MOV A,MINUTE CJNE A,#60,NOT_OVER1 ;判斷是否加超過60 MOV MINUTE,#0 NOT_OVER1: MOV A,#10001011B ;游標移到'分鐘'的位置 CALL WRIR MOV A,MINUTE CALL DISPLAY RET S14: CJNE A,#01111011B,S15;判斷是否按2 INC HOUR MOV A,HOUR CJNE A,#24,NOT_OVER2 ;判斷是否加超過24 MOV HOUR,#0 NOT_OVER2: MOV A,#10001000B ;游標移到'小時'的位置 CALL WRIR MOV A,HOUR CALL DISPLAY RET S15: RET ;--------------------------------------------------------------------------- TIMER0_INT: ;計時中斷程式,每中斷20次為1秒鐘 PUSH ACC ;因中斷程式會改變鍵盤掃描時累加器acc的值, 所以要先存到堆疊,離開時再取回 MOV TH0,#(65536-49990) / 256 ;設定計數約50ms MOV TL0,#(65536-49990) MOD 256 DEC RPT ;每0.05秒, 鍵盤重複計數器減1 DJNZ SUBSEC,NOT_1SEC ;判斷是否已計數1秒鐘,若還沒1秒,離開中斷程式 MOV SUBSEC,#20 INC SECOND MOV A,SECOND CJNE A,#60,NOT_1MIN ;判斷是否已經1分鐘(second=60) MOV SECOND,#0 INC MINUTE MOV A,MINUTE CJNE A,#60,NOT_1HR ;判斷是否已經1小時(MINUTE=60) MOV MINUTE,#0 INC HOUR MOV A,HOUR CJNE A,#60,NOT_1DAY ;判斷是否已經1天(HOUR=60) MOV HOUR,#0 NOT_1DAY: MOV A,#10001000B ;游標移到'小時'的位置 CALL WRIR MOV A,HOUR CALL DISPLAY NOT_1HR: MOV A,#10001011B ;游標移到'分鐘'的位置 CALL WRIR MOV A,MINUTE CALL DISPLAY NOT_1MIN: MOV A,#10001110B ;游標移到'秒'的位置 CALL WRIR MOV A,SECOND CALL DISPLAY NOT_1SEC: POP ACC RETI ;-------------------------------------------------------------------------------------- PRT_STR: ;顯示字串副程式 CLR A ;清除累加器內資料 MOVC A,@A+DPTR ;讀取字型資料 JZ END_STR ;判斷抓到的資料是否為0,如果是0代表字串結束,跳至返回主程式 CALL WRDR INC DPTR ;指標內容加1, 指到下一個字 JMP PRT_STR END_STR: RET str1: DB "00:00:00",0 ;定義字串內容, 以數值0當結束碼 WRIR: CLR LCD_RS ;寫命令到LCD副程式 SETB LCD_E MOV LCD_DATA,A MOV R7,#50 ;延遲等訊號穩定 DJNZ R7,$ CLR LCD_E ;讓Enable接腳產生負緣訊號 MOV R7,#60 ;延遲等訊號處裡完畢 DJNZ R7,$ RET WRDR: SETB LCD_RS ;寫資料到LCD副程式 SETB LCD_E MOV LCD_DATA,A MOV R7,#50 ;延遲等訊號穩定 DJNZ R7,$ CLR LCD_E ;讓Enable接腳產生負緣訊號 MOV R7,#60 ;延遲等訊號處裡完畢 DJNZ R7,$ RET DISPLAY: ;將2位數數值顯示於LCD MOV B,#10 DIV AB ;數值除以10,十位數在A, 個位數在B ADD A,#30H ;轉換數字成ASCII碼 0~9 => 30H~39H CALL WRDR ;顯示於LCD MOV A,B ADD A,#30H ;轉換數字成ASCII碼 CALL WRDR ;顯示於LCD RET DELAY: MOV R6,#100 DEL: MOV R7,#100 DJNZ R7,$ DJNZ R6,DEL DJNZ R5,DELAY RET END