$ PW(96) PL(60) DATE(07-14-95) $ DEBUG ERRORPRINT XREF ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º SOFTWARE FOR 4x4 KEYPAD & 4x20 LCD º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; Original date = 04-28-93 ; Modified for a 4 line by 20 character LCD: TMB 7/15/95 ; NOTE: Print a HT character "CHR$(9)," to go to line 3 ; Print a VT character "CHR$(11)," to go to line 4 ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ PARAMETER DEFINITIONS ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; ; All registers, bits, addresses, and constants ; are declared below. DSEG AT 18H CLOCK1: DS 2 ; TIMER REGISTERS FOR REG_SEL: DS 1 ; SELECT COMMAND OR DATA TEMP_HOLD: DS 1 ; LCD BYTE, BUFFER PTR NotUsed: DS 2 ; Available to the user $IF NOT(NOKEY) TEMP_KEY: DS 1 ; TEMP, FOR DEBOUNCING SWITCH_BYTE: DS 1 ; LAST KEY PRESSED BSEG AT 0 NR1: DBIT 1 ; SET WHEN A NEW VALUE NR2: DBIT 1 ; IS DETECTED NR3: DBIT 1 NR4: DBIT 1 KEYS EQU 4800H ; STORE KEY READINGS DEBOUNCED EQU KEYS+100H ; STORE IF DEBOUNCED ROW1 EQU 1111$1110B ; MASK OFF EACH ROW ROW2 EQU 1111$1101B ROW3 EQU 1111$1011B ROW4 EQU 1111$0111B $ENDIF $ EJECT BEGINNING EQU 7E00H ; BEGINNING OF CODE ESC EQU 1BH ; ESCAPE Character SPACE EQU 20H ; SPACE Character CR EQU 0DH ; Carriage Return LF EQU 0AH ; Line Feed FF EQU 0CH ; Form Feed HT EQU 09H ; Horizontal tab VT EQU 0BH ; Vertical tab HLP EQU SPACE CLE EQU FF ENT EQU LF EXEC_TIME EQU 60 ; INSTRUCTION EXECUTION TIME RS EQU ACC.0 ; LCD Control Eclk EQU ACC.1 ; signals COMMAND EQU 0011$0000B ; ALL SIGNALS LOW RESET EQU 0011$0000B ; "RESET" BYTE FUNCTION_FIRST EQU 0010$0000B ; "FUNCTION SET" FUNCTION_SET EQU 0010$1000B ; "FUNCTION SET" DISPLAY_OFF EQU 0000$1000B ; "DISPLAY OFF" DISPLAY_ON EQU 0000$1100B ; "DISPLAY ON" ; NOTE: 0000$11cbB For "Display On," where ; c=1 for Cursor on, and b=1 for blinking cursor CLEAR_DISPLAY EQU 0000$0001B ; "CLEAR DISPLAY" MODE_SET EQU 0000$0110B ; "ENTRY MODE" LINE4 EQU 1101$0100B ; "GOTO LINE 4" *** NEW *** LINE3 EQU 1001$0100B ; "GOTO LINE 3" *** NEW *** LINE2 EQU 1100$0000B ; "GOTO LINE 2" LINE1 EQU 1000$0000B ; "GOTO LINE 1" SHIFT_RIGHT EQU 0001$1100B ; "SHIFT DISPLAY R" SHIFT_LEFT EQU 0001$1000B ; "SHIFT DISPLAY L" CURSOR_LEFT EQU 0001$0000B ; "SHIFT CURSOR L" CURSOR_RIGHT EQU 0001$0100B ; "SHIFT CURSOR R" ; Initialize the 82C55 Control Word as follows: ; PORT A = OUTPUT PORT C(0-3) = OUTPUT ; PORT B = OUTPUT PORT C(4-7) = INPUT ControlWord EQU 1000$1000B ; SELECT MODE 0 DefaultPortB EQU 0000$0110B ; Enable Serial Com PORT_A XDATA 0FD00H ; NEITHER A0 NOR A1 PORT_B XDATA 0FD01H ; SET A0 ONLY PORT_C XDATA 0FD02H ; SET A1 ONLY CONTROL XDATA 0FD03H ; SET BOTH A0 & A1 ; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ CSEG AT BEGINNING ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ BEGINNING OF INITIALIZATION ROUTINE ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; ; ----- INITIALIZE USER LCD OUTPUT ALGORITHM ------ ; The following code places a jump vector in memory ; and sets the protection bit that tells BASIC ; where it should direct the PRINT@ output to. Start: MOV DPTR,#403CH ; POINT TO JUMP LOCATION MOV A,#2 ; LOAD LJMP MOVX @DPTR,A INC DPTR MOV A,#HIGH CHAR_OUTPUT MOVX @DPTR,A INC DPTR MOV A,#LOW CHAR_OUTPUT MOVX @DPTR,A SETB 39 ; PROTECTION BIT ; ----------- INITIALIZE LCD MODULE ------------- ; INITIALIZE 82C55 PORTS MOV DPTR,#CONTROL ; Point to 82C55A Ctrl MOV A,#ControlWord ; All outputs MOVX @DPTR,A ; (Except PC4-7) MOV DPTR,#PORT_B ; Point to PortB MOV A,#DefaultPortB ; EEPROM OFF, MOVX @DPTR,A ; RS422 ON $ EJECT LCDSTART: ; LCD display initialization routine MOV REG_SEL,#0 ; OUTPUT TO CMD REG MOV DPTR,#PORT_A MOV A,#Reset ; CLEAR ALL CTRL LINES ACALL TOGGLE MOV CLOCK1,#30 ; WAIT FOR A LONG TIME CALL WAIT_LONG MOV A,#RESET ; WRITE BYTE ACALL TOGGLE ACALL LD_DEC ; WAIT AT LEAST 4.1 mS MOV A,#RESET ; WRITE BYTE ACALL CLOCK_N_WAIT MOV A,#RESET ; WRITE BYTE ACALL CLOCK_N_WAIT ; Output the byte that specifies a 4-bit interface MOV A,#FUNCTION_FIRST ; WRITE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN MOV A,#FUNCTION_SET ACALL OUTPUT ; FUNCTION SET MOV A,#DISPLAY_OFF ACALL OUTPUT ; TURN DISPLAY OFF ACALL Clear ; CLEAR DISPLAY MOV A,#MODE_SET ACALL OUTPUT ; SET ENTRY MODE MOV A,#DISPLAY_ON ACALL OUTPUT ; TURN DISPLAY ON $ IF (NOKEY) RET $ ELSE $ EJECT ; ------ SET UP TIMER0 FOR 5 mS INTERRUPT ------- MOV DPTR,#400BH ; Stuff "LJMP XXXXH" into MOV A,#2 ; mirrored Timer0 vector MOVX @DPTR,A ; at address 400BH. INC DPTR MOV A,#HIGH Timer0Int ; Location of Timer0 MOVX @DPTR,A ; interrupt routine. INC DPTR MOV A,#LOW Timer0Int MOVX @DPTR,A ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ CLR TR0 ; STOP TIMER0 FOR NOW MOV A,TL0 ; Reload Timer 0 for ADD A,#LOW -4998 ; 5 mS (Less 2uS for MOV TL0,A ; ADD & MOV latency.) MOV TH0,#HIGH -4998 ; Timer 0 is a 16 bit timer used to for a 5 mS int. ; The input to both Timers is the CPU clock ; The frequency is OSC/12 = 1 MHz. (1uS) MOV TMOD,#11H ; TIMER/CTR MODE CTRL REG SETB TR0 ; Start Timer SETB ET0 ; Timer0 interrupt enable SETB EA ; Global interrupt enable RET $ EJECT ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ TIMER0 INTERRUPT SERVICING ROUTINE ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; Timer0Int: PUSH ACC ; 5 mS interrupt PUSH PSW PUSH DPH PUSH DPL MOV A,TL0 ; Reload Timer 0 for ADD A,#LOW -4998 ; 5 mS (Less 2uS for MOV TL0,A ; ADD & MOV latency.) MOV TH0,#HIGH -4998 ; ----------- SCAN THE KEYPAD ------------- MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOV A,#ROW1 ; GET THE FIRST ROW MOVX @DPTR,A MOVX A,@DPTR ; READ COLUMNS ORL A,#0FH ; MASK OFF UNUSED BITS CPL A MOV TEMP_KEY,A ; SAVE THE BYTE MOV DPTR,#KEYS MOVX A,@DPTR ; GET PREVIOUS READING CJNE A,TEMP_KEY,NEW1 INC DPH ; POINT TO DEBOUNCED BYTE MOVX @DPTR,A CLR NR1 READ2: MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOV A,#ROW2 ; GET THE SECOND ROW MOVX @DPTR,A MOVX A,@DPTR ; READ COLUMNS ORL A,#0FH ; MASK OFF UNUSED BITS CPL A MOV TEMP_KEY,A ; SAVE THE BYTE MOV DPTR,#KEYS+1 MOVX A,@DPTR ; GET PREVIOUS READING CJNE A,TEMP_KEY,NEW2 INC DPH ; POINT TO DEBOUNCED BYTE MOVX @DPTR,A CLR NR2 READ3: MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOV A,#ROW3 ; GET THE THIRD ROW MOVX @DPTR,A MOVX A,@DPTR ; READ COLUMNS ORL A,#0FH ; MASK OFF UNUSED BITS CPL A MOV TEMP_KEY,A ; SAVE THE BYTE MOV DPTR,#KEYS+2 MOVX A,@DPTR ; GET PREVIOUS READING CJNE A,TEMP_KEY,NEW3 INC DPH ; POINT TO DEBOUNCED BYTE MOVX @DPTR,A CLR NR3 READ4: MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOV A,#ROW4 ; GET THE FOURTH ROW MOVX @DPTR,A MOVX A,@DPTR ; READ COLUMNS ORL A,#0FH ; MASK OFF UNUSED BITS CPL A MOV TEMP_KEY,A ; SAVE THE BYTE MOV DPTR,#KEYS+3 MOVX A,@DPTR ; GET PREVIOUS READING CJNE A,TEMP_KEY,NEW4 INC DPH ; POINT TO DEBOUNCED BYTE MOVX @DPTR,A CLR NR4 SJMP LoadKey NEW1: SETB NR1 ; NEW VALUE FOR ROW1 MOV A,TEMP_KEY MOVX @DPTR,A ; SAVE THE NEW VALUE INC DPH ; POINT TO DEBOUNCED BYTE CLR A MOVX @DPTR,A SJMP READ2 NEW2: SETB NR2 ; NEW VALUE FOR ROW2 MOV A,TEMP_KEY MOVX @DPTR,A ; SAVE THE NEW VALUE INC DPH ; POINT TO DEBOUNCED BYTE CLR A MOVX @DPTR,A SJMP READ3 NEW3: SETB NR3 ; NEW VALUE FOR ROW3 MOV A,TEMP_KEY MOVX @DPTR,A ; SAVE THE NEW VALUE INC DPH ; POINT TO DEBOUNCED BYTE CLR A MOVX @DPTR,A SJMP READ4 NEW4: SETB NR4 ; NEW VALUE FOR ROW4 MOV A,TEMP_KEY MOVX @DPTR,A ; SAVE THE NEW VALUE INC DPH ; POINT TO DEBOUNCED BYTE CLR A MOVX @DPTR,A $ EJECT LoadKey: ; UPDATE KEY READ REGISTER MOV SWITCH_BYTE,#0 JB NR1,Load2 ; STILL DEBOUNCING? MOV DPTR,#DEBOUNCED MOVX A,@DPTR ; GET KEY READING JZ Load2 ; NONE PRESSED MOV DPTR,#RC1-1 ; POINT TO CHAR STRINGS SJMP GET_C Load2: JB NR2,Load3 MOV DPTR,#DEBOUNCED+1 MOVX A,@DPTR ; GET KEY READING JZ Load3 ; NONE PRESSED MOV DPTR,#RC2-1 ; POINT TO CHAR STRINGS SJMP GET_C Load3: JB NR3,Load4 MOV DPTR,#DEBOUNCED+2 MOVX A,@DPTR ; GET KEY READING JZ Load4 ; NONE PRESSED MOV DPTR,#RC3-1 ; POINT TO CHAR STRINGS SJMP GET_C Load4: JB NR4,TIME1 MOV DPTR,#DEBOUNCED+3 MOVX A,@DPTR ; GET KEY READING JZ TIME1 ; NONE PRESSED MOV DPTR,#RC4-1 ; POINT TO CHAR STRINGS GET_C: SWAP A CJNE A,#10H,$+3 ; CHECK FOR OUT OF RANGE JNC TIME1 MOVC A,@A+DPTR ; GET BYTE MOV SWITCH_BYTE,A TIME1: ;* Any other function that needs to be executed *; ;* every 5 mS should be programmed here. *; AllDone: POP DPL ; Tail end of 5 mS POP DPH ; interrupt POP PSW POP ACC RETI $ENDIF $ EJECT ; ------------ LCD UPDATE -------------- CHAR_OUTPUT: MOV REG_SEL,#1 ; POINT TO DATA REGISTER CJNE A,#SPACE,$+3 ; CHECK FOR A JNC OUTPUT ; PRINTABLE CHAR MOV REG_SEL,#0 ; POINT TO CMD REGISTER ; Clear display if Form Feed. CJNE A,#FF,CR_Chk ; Form Feed? SJMP Clear1 ; Move the Cursor to the Home position if CR_Chk: CJNE A,#CR,LF_Chk ; Carriage return? MOV A,#LINE1 SJMP OUTPUT ; Move the Cursor to Line 2 if Line Feed LF_Chk: CJNE A,#LF,HT_Chk ; Line Feed? MOV A,#LINE2 SJMP OUTPUT ; Move the Cursor to Line 3 if Horizontal tab HT_Chk: CJNE A,#HT,VT_Chk ; Horizontal tab MOV A,#LINE3 SJMP OUTPUT ; Move the Cursor to Line 4 if Vertical tab VT_Chk: CJNE A,#VT,NoChar ; Vertical tab MOV A,#LINE4 ; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ $ EJECT ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ DISPLAY WRITING SUBROUTINES ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; ; ------ SUBROUTINE FOR SENDING A CHARACTER ------ ; SEND AN 8-BIT CHARACTER (OR COMMAND) TO THE LCD. OUTPUT: MOV TEMP_HOLD,A ; SAVE IT ; ---------- OUTPUT CHARACTER HALF ----------- ANL A,#0F0H ; GET RID OF LOWER HALF ORL A,REG_SEL ; SELECT COMMAND OR DATA MOV DPTR,#PORT_A ; POINT TO DATA LINES ACALL TOGGLE MOV A,TEMP_HOLD ; GET OUTPUT BYTE AGAIN ; ----------- OUTPUT SECOND HALF ---------- SWAP A ; NOW DO LOWER HALF ANL A,#0F0H ORL A,REG_SEL ; SELECT COMMAND OR DATA CLOCK_N_WAIT: ACALL TOGGLE ; OUTPUT THE BYTE MOV CLOCK1+1,#EXEC_TIME ; WAIT AT LEAST DJNZ CLOCK1+1,$ ; 120 uS NoChar: RET ; Back to BASIC TOGGLE: MOVX @DPTR,A ; OUTPUT THE BYTE SETB Eclk MOVX @DPTR,A ; CLOCK IT IN CLR Eclk MOVX @DPTR,A RET $ EJECT ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ CLEAR DISPLAY ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Clear: MOV REG_SEL,#0 ; OUTPUT TO COMMAND REG Clear1: MOV A,#CLEAR_DISPLAY ACALL OUTPUT LD_DEC: MOV CLOCK1,#10 ; WAIT AT LEAST 4.9 mS WAIT_LONG: MOV CLOCK1+1,#250 DJNZ CLOCK1+1,$ DJNZ CLOCK1,WAIT_LONG RET $IF NOT(NOKEY) RC1: DB '1223333',ESC,ESC,ESC,ESC,ESC,ESC,ESC,ESC RC2: DB '4556666',HLP,HLP,HLP,HLP,HLP,HLP,HLP,HLP RC3: DB '7889999',CLE,CLE,CLE,CLE,CLE,CLE,CLE,CLE RC4: DB '.00####',ENT,ENT,ENT,ENT,ENT,ENT,ENT,ENT $ENDIF ; END OF PROGRAM END ; Following is the HEX file derived from the source ; file. This can be used for loading the program ; via MONITOR-51 and almost any terminal program. :107E000090403C7402F0A3747FF0A3745BF0D2271F :107E100090FD037488F090FD017406F0751A0090CF :107E2000FD007430F1A075181E127FB27430F1A0FD :107E3000F1AF7430F1977430F1977420F197742892 :107E4000F1857408F185F1A87406F185740CF1854B :107E500090400B7402F0A3747EF0A37473F0C28C94 :107E6000E58A247AF58A758CEC758911D28CD2A9B1 :107E7000D2AF22C0E0C0D0C083C082E58A247AF5A8 :107E80008A758CEC90FD0274FEF0E0440FF4F51E50 :107E9000904800E0B51E4F0583F0C20090FD0274CB :107EA000FDF0E0440FF4F51E904801E0B51E4205D8 :107EB00083F0C20190FD0274FBF0E0440FF4F51E64 :107EC000904802E0B51E350583F0C20290FD0274B1 :107ED000F7F0E0440FF4F51E904803E0B51E2805C6 :107EE00083F0C203802AD200E51EF00583E4F0800F :107EF000ABD201E51EF00583E4F080B8D202E51EA6 :107F0000F00583E4F080C5D203E51EF00583E4F0BC :107F1000751F0020000B904900E06005907FBB803A :107F20002820010B904901E06005907FCA801A204B :107F3000020B904902E06005907FD9800C2003126B :107F4000904903E0600C907FE8C4B41000500393A4 :107F5000F51FD082D083D0D0D0E032751A01B42082 :107F6000005022751A00B40C028040B40D047480D5 :107F70008013B40A0474C0800CB40904749480059E :107F8000B40B1C74D4F51B54F0451A90FD00F1A0FD :107F9000E51BC454F0451AF1A075193CD519FD2212 :107FA000F0D2E1F0C2E1F022751A007401F185759A :107FB000180A7519FAD519FDD518F722313232335E :107FC0003333331B1B1B1B1B1B1B1B343535363636 :107FD00036362020202020202020373838393939E3 :107FE000390C0C0C0C0C0C0C0C2E303023232323DE :087FF0000A0A0A0A0A0A0A0A39 :00000001FF ; This HEX file is for the LCD ONLY (no keypad) version :107E000090403C7402F0A3747EF0A37451F0D2272A :107E100090FD037488F090FD017406F0751A0090CF :107E2000FD007430D19675181E127EA87430D1965C :107E3000D1A57430D18D7430D18D7420D18D74283A :107E4000D17B7408D17BD19E7406D17B740CD17B1D :107E500022751A01B420005022751A00B40C028059 :107E600040B40D0474808013B40A0474C0800CB450 :107E7000090474948005B40B1C74D4F51B54F045AC :107E80001A90FD00D196E51BC454F0451AD19675A1 :107E9000193CD519FD22F0D2E1F0C2E1F022751AA9 :107EA000007401D17B75180A7519FAD519FDD5181A :027EB000F722B7 :00000001FF