; File: LCD_KeyP.A51 Date: 08/25/92 ; ; /* (c) Copyright BLUE EARTH RESEARCH, MANKATO, MN 1992. */ ; /* All rights reserved. */ ; $ TITLE(LCD AND KEYPAD SOFTWARE FOR I/O-24) $ PW(93) PL(60) DATE(08-25-92) $ DEBUG ERRORPRINT XREF ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º SOFTWARE FOR INTERFACING TO 4x4 KEYPAD & 2x16 LCD º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ Written By: Thomas Bachmann ³ ; ³ Blue Earth Research, Mankato, MN 56001 ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; ; NOTES: ; The following program can be used to initialize and write ; messages to most standard LCD Modules using the Micro-440 ; and I/O-24 module. It was tested using a Sharp LM16X21A ; 2 line by 16 character display and a Grayhill 4x4 keypad. ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; BASIC program interface: ; The I/O-24 is initialized by executing the following BASIC ; Statement: "CALL 7A00H" ; Messages are then displayed by calling the proper address, ; beginning with 7B00H for MSG1, 7B05H for MSG2, 7B0AH for ; MSG3, etc. This can be demonstrated by the following BASIC ; Program: ; 100 CALL 7A00H : GOSUB 200 : REM WAIT FOR KEY PRESS ; 110 CALL 7B00H : GOSUB 200 : REM DISPLAY MSG1 ; 120 CALL 7B05H : GOSUB 200 : REM DISPLAY MSG2 ; 130 CALL 7B1EH : GOSUB 200 : REM DISPLAY MSG7 ; 140 XBY(4700H)=A : CALL 7B23H : GOSUB 200 : GOTO 140 ; 200 DO : A=DBY(1FH) : WHILE A ; 210 DO : A=DBY(1FH) : UNTIL A : RETURN ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ $ EJECT ; Assembly language program interface: ; The LCD is addressed as it would be in BASIC, except that ; program labels can be used in the source file. For example, ; CALL BEGINNING ; Initialize LCD Module ; CALL STR1 ; Display first message ; ... ; Any other code goes here ; CALL STR2 ; Repeat for all other messages ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Note that the sample program uses a single internal memory ; location for reading characters from the keypad. Since it is ; possible for more than one key to be pressed at a time, the ; keypad scan is stored in memory locations 4900H-4903H for ; row1-row4, respectively. These memory locations will be zero ; if no key in the corresponding row is being pressed. ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; All software that has to do with reading the 4x4 keypad is ; enclosed within conditional assembly brackets. This allows ; the program to be pared down considerably when it is used to ; drive an LCD with no keypad. To assemble the software for ; LCD only operation, the assembler invocation line should be: ; "A51 LCD_KEYP.A51 SET(NOKEY)" ; Note that this does not change the BASIC LCD software at all. ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Troubleshooting hint: ; If your LCD module appears to reset properly but does not ; display characters, make sure that the contrast adjust ; potentiometer on the I/O-24 module is properly positioned. ; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ $ EJECT ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ DEFINITIONS SPECIFIC TO THE SYSTEM WIRING ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; The following software assume that the LCD and Keypad are ; wired with all inputs to Port A, B, and C as follows: ; SIGNAL I/O-24 SIGNAL ; -------- ----------- -------- ; 13 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ 7 ; DB7ÄÄÄÄÄÄÄÄÄÄÄ´ PA7 PB3 ÃÄÄÄÄÄÄÄÄÄÄCOL4 ; 25 ³ ³ 19 ; DB6ÄÄÄÄÄÄÄÄÄÄÄ´ PA6 PB2 ÃÄÄÄÄÄÄÄÄÄÄCOL3 ; 12 ³ ³ 6 ; DB5ÄÄÄÄÄÄÄÄÄÄÄ´ PA5 PB1 ÃÄÄÄÄÄÄÄÄÄÄCOL2 ; 24 ³ ³ 18 ; DB4ÄÄÄÄÄÄÄÄÄÄÄ´ PA4 PB0 ÃÄÄÄÄÄÄÄÄÄÄCOL1 ; 11 ³ ³ 5 ; DB3ÄÄÄÄÄÄÄÄÄÄÄ´ PA3 PC7 ÃÄÄÄÄÄÄÄÄÄÄROW4 ; 23 ³ ³ 17 ; DB2ÄÄÄÄÄÄÄÄÄÄÄ´ PA2 PC6 ÃÄÄÄÄÄÄÄÄÄÄROW3 ; 10 ³ ³ 4 ; DB1ÄÄÄÄÄÄÄÄÄÄÄ´ PA1 PC5 ÃÄÄÄÄÄÄÄÄÄÄROW2 ; 22 ³ ³ 16 ; DB0ÄÄÄÄÄÄÄÄÄÄÄ´ PA0 PC4 ÃÄÄÄÄÄÄÄÄÄÄROW1 ; 9 ³ ³ 2 ; EÄÄÄÄÄÄÄÄÄÄÄÄ´ PB7 0-5 ÃÄÄÄÄÄÄÄÄÄÄÄVee ; 21 ³ ³ 14 ; R/WÄÄÄÄÄÄÄÄÄÄÄ´ PB6 +5V ÃÄÄÄÄÄÄÄÄÄÄÄVcc ; 8 ³ ³ 1 ; RSÄÄÄÄÄÄÄÄÄÄÄ´ PB5 GND ÃÄÄÄÄÄÄÄÄÄÄÄVss ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; Keypad Layout: ; ÉÍÍÍÍÍÑÍÍÍÍÍÑÍÍÍÍÍÑÍÍÍÍÍ» ; º 1 ³ 2 ³ 3 ³ A º ; ÇÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄĶ ; º 4 ³ 5 ³ 6 ³ B º ; ÇÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄĶ ; º 7 ³ 8 ³ 9 ³ C º ; ÇÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄĶ ; º * ³ 0 ³ # ³ D º ; ÈÍÍÍÍÍÏÍÍÍÍÍÏÍÍÍÍÍÏÍÍÍÍͼ $ EJECT ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ PARAMETER DEFINITIONS ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; ; All registers, bits, addresses, and constants are declared ; below. The program can be adapted to system wiring or ; parameter changes by modifying the corresponding definition ; located in the following code block. DSEG AT 18H CLOCK1: DS 2 ; TIMER REGISTERS FOR WAITING REG_SEL: DS 1 ; SELECT COMMAND OR DATA REG. LCD_POINTER: DS 1 ; POINT TO LAST CHARACTER SENT TEMP_HOLD: DS 1 ; LCD BYTE, BUFFER POINTER STRING_DP: DS 1 ; HOLD LOW BYTE OF STRING DPTR $IF NOT(NOKEY) TEMP_KEY: DS 1 ; TEMP BYTE FOR DEBOUNCING SWITCH_BYTE: DS 1 ; LAST KEY PRESSED $ENDIF BSEG AT 0 LCD_DATA: DBIT 1 ; SET WHEN MORE LCD DATA READY DOING_LCD: DBIT 1 ; SET WHEN COPYING CHARACTERS $IF NOT(NOKEY) NR1: DBIT 1 ; SET WHEN NEW VALUE DETECTED NR2: DBIT 1 NR3: DBIT 1 NR4: DBIT 1 $ENDIF ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ BEGINNING EQU 7A00H ; BEGINNING OF PROGRAM LCD_BUFFER EQU 4600H ; ADDRESS OF LCD BUFFER LCD_OUTPUT EQU 4700H ; USER DEFINED CHARS $IF NOT(NOKEY) KEYS EQU 4800H ; STORE KEY READINGS DEBOUNCED EQU KEYS+100H ; STORE DEBOUNCED KEYS ENT EQU 0DH ; ENTER - "ENT" DEL EQU 7FH ; DELETE - "DEL" RST EQU 2 ; RESET - "RST" ROW4 EQU 1000$0001B ; MASK OFF EACH ROW ROW3 EQU 1000$0010B ROW2 EQU 1000$0100B ROW1 EQU 1000$1000B $ENDIF ESC EQU 1BH ; ESCAPE Character EXEC_TIME EQU 60 ; EXECUTION TIME ; FOR INSTRUCTIONS CMD EQU 0010$0000B CLOCK EQU 1100$0000B CLOCK_IT EQU 0100$0000B COMMAND EQU 1110$0000B ; ALL SIGNALS LOW CLOCK_COMMAND EQU 0110$0000B ; TOGGLE 'E' SIGNAL RESET EQU 0011$0000B ; "RESET" BYTE FUNCTION_SET EQU 0011$1000B ; "FUNCTION SET" BYTE DISPLAY_OFF EQU 0000$1000B ; "DISPLAY OFF" BYTE DISPLAY_ON EQU 0000$1100B ; "DISPLAY ON" BYTE ; 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" BYTE MODE_SET EQU 0000$0110B ; "ENTRY MODE" BYTE LINE2_0 EQU 1100$0000B ; "GOTO LINE 2" BYTE LINE1 EQU 1000$0000B ; "GOTO LINE 1" BYTE SHIFT_RIGHT EQU 0001$1100B ; "SHIFT DISPLAY RIGHT" SHIFT_LEFT EQU 0001$1000B ; "SHIFT DISPLAY LEFT" CURSOR_LEFT EQU 0001$0000B ; "SHIFT CURSOR LEFT" CURSOR_RIGHT EQU 0001$0100B ; "SHIFT CURSOR RIGHT" ; Initialize the 82C55 Control Word as follows: ; PORT A = OUTPUT PORT C(0-3) = OUTPUT ; PORT B = OUTPUT PORT C(4-7) = INPUT ; Note: For more information, see Page 3-107 in Intel Data ; Book #296467-002, "1991 Peripheral Components" CONTROL_WORD EQU 1000$1000B ; SELECT MODE 0 CONTROL EQU 0FB00H ; SET BOTH A0 & A1 PORT_C EQU 0FA00H ; SET A1 ONLY PORT_B EQU 0F900H ; SET A0 ONLY PORT_A EQU 0F800H ; NEITHER A0 NOR A1 RESET_HIGH EQU 0E200H ; RESET SIGNAL HIGH RESET_LOW EQU 0E000H ; RESET SIGNAL LOW $ EJECT ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ BEGINNING OF INITIALIZATION ROUTINE ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; CSEG AT BEGINNING CLR A MOV LCD_POINTER,A ; NO CHARS IN BUFFER MOV STRING_DP,A CLR LCD_DATA ; NO CHARS READY FOR LCD CLR DOING_LCD ; NOT DOING LCD NOW MOV DPTR,#400BH ; Stuff "LJMP XXXXH" into MOV A,#2 ; mirrored Timer 0 overflow MOVX @DPTR,A ; vector at address 400BH. INC DPTR MOV A,#HIGH Timer0Int ; Location of Timer 0 MOVX @DPTR,A ; interrupt routine. INC DPTR MOV A,#LOW Timer0Int MOVX @DPTR,A MOV DPTR,#LCD_OUTPUT+1 ; INITIALIZE CHAR OUTPUT MOV A,#'"' MOVX @DPTR,A ; ------------------- INITIALIZE LCD MODULE ------------------- MOV DPTR,#RESET_HIGH MOVX @DPTR,A ; HOLD RESET LINE HIGH MOV CLOCK1,#25 ; LOAD 50 uS LOOP COUNT DJNZ CLOCK1,$ MOV DPTR,#RESET_LOW MOVX @DPTR,A ; INITIALIZE 82C55 PORTS A & B AS OUTPUTS MOV DPTR,#CONTROL MOV A,#CONTROL_WORD MOVX @DPTR,A $ EJECT LCDSTART: ; LCD display initialization routine MOV DPTR,#PORT_B MOV A,#COMMAND ; CLEAR ALL CONTROL LINES MOVX @DPTR,A MOV CLOCK1,#30 ; WAIT FOR A LONG TIME CALL WAIT_LONG MOV DPTR,#PORT_A MOV A,#RESET ; WRITE BYTE MOVX @DPTR,A MOV DPTR,#PORT_B MOV A,#CLOCK_COMMAND MOVX @DPTR,A ; CLOCK IT IN SETB ACC.7 MOVX @DPTR,A 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 MOV A,#FUNCTION_SET ; WRITE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN ; Output the byte that specifies an 8-bit interface MOV DPTR,#PORT_B ; SET CONTROL LINES MOV A,#COMMAND MOVX @DPTR,A ; SELECT COMMAND MOV A,#FUNCTION_SET ; GET THE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN ; Turn the display off MOV DPTR,#PORT_B ; SET CONTROL LINES MOV A,#COMMAND MOVX @DPTR,A ; SELECT COMMAND MOV A,#DISPLAY_OFF ; GET THE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN ACALL CLEAR ; CLEAR THE DISPLAY $ EJECT ; SET THE DISPLAY ENTRY MODE MOV DPTR,#PORT_B ; SET CONTROL LINES MOV A,#COMMAND MOVX @DPTR,A ; SELECT COMMAND MOV A,#MODE_SET ; GET THE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN ; TURN THE DISPLAY BACK ON MOV DPTR,#PORT_B ; SET CONTROL LINES MOV A,#COMMAND MOVX @DPTR,A ; SELECT COMMAND MOV A,#DISPLAY_ON ; GET THE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN ; MAKE SURE THAT THE CONTRAST ADJUST WILL WORK MOV DPTR,#PORT_C MOV A,#0F0H ; WRITE 1's TO INPUT PORT MOVX @DPTR,A ACALL CLEAR ; CLEAR DISPLAY $ EJECT ; ------------- SET UP TIMER0 FOR 5 mS INTERRUPT -------------- CLR TR0 ; STOP TIMER0 FOR NOW MOV A,TL0 ; Reload Timer 0 for 5 mS ADD A,#LOW -4998 ; (Less 2uS to compensate MOV TL0,A ; for ADD & MOV latency.) MOV TH0,#HIGH -4998 ; Timer 0 is a 16 bit timer used to generate a 5 mS interrupt. ; The input to both Timers is the internal system clock ; The frequency is OSC/12 = 1 MHz. (1uS) ; TMOD: ; Timer 1 Timer 0 ; ÃÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÅÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄ´ ; ³ GAT ³ C/T ³ M1 ³ M0 ³ GAT ³ C/T ³ M1 ³ M0 ³ ; ÀÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÙ ; 0 0 0 1 0 0 0 1 MOV TMOD,#11H ; TIMER/CNTR MODE CTRL REGISTER ; TCON: ; ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄ¿ ; ³ TF1 ³ TR1 ³ TF0 ³ TR0 ³ IE1 ³ IT1 ³ IE0 ³ IT0 ³ ; ÀÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÙ ; 0 0 0 1 0 0 0 0 SETB TR0 ; Start Timer ; IE: ; ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄ¿ ; ³ EA ³ EC ³ ET2 ³ ES ³ ET1 ³ EX1 ³ ET0 ³ EX0 ³ ; ÀÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÙ ; 1 0 0 0 0 0 1 0 SETB ET0 ; Timer 0 interrupt enable SETB EA ; Global interrupt enable RET ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ CLEAR TIMER0 INTERRUPT ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; STOP: CLR TR0 ; Stop Timer CLR ET0 ; Timer 0 interrupt enable CLR EA ; Global interrupt enable RET $ EJECT ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ DISPLAY WRITING SUBROUTINES ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; Output the command byte in the Accumulator and wait. CLOCK_N_WAIT: MOV DPTR,#PORT_A ; POINT TO DATA LINES MOVX @DPTR,A ; OUTPUT THE BYTE MOV DPTR,#PORT_B ; POINT TO CONTROL LINES MOV A,#CLOCK_COMMAND ; SET 'E' HIGH MOVX @DPTR,A SETB ACC.7 MOVX @DPTR,A ; AND CLEAR 'E' MOV CLOCK1+1,#EXEC_TIME ; WAIT AT LEAST 120 uS DJNZ CLOCK1+1,$ RET ; Clear the LCD display CLEAR: MOV DPTR,#PORT_B ; SET CONTROL LINES MOV A,#COMMAND MOVX @DPTR,A ; SELECT COMMAND MOV A,#CLEAR_DISPLAY ; GET THE BYTE ACALL CLOCK_N_WAIT ; CLOCK IT IN 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 $ EJECT CSEG AT BEGINNING+100H ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ DISPLAY SYSTEM MESSAGES ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; ; Note that these lines may be duplicated for adding other ; messages that are to be displayed. STR1: MOV DPTR,#MSG1 ; SIGN ON MESSAGE SJMP DUPE_STRING STR2: MOV DPTR,#MSG2 ; DEMO DISPLAY MESSAGE SJMP DUPE_STRING STR3: MOV DPTR,#MSG3 ; NEXT DISPLAY MESSAGE SJMP DUPE_STRING STR4: MOV DPTR,#MSG4 ; NEXT DISPLAY MESSAGE SJMP DUPE_STRING STR5: MOV DPTR,#MSG5 ; NEXT DISPLAY MESSAGE SJMP DUPE_STRING STR6: MOV DPTR,#MSG6 ; NEXT DISPLAY MESSAGE SJMP DUPE_STRING STR7: MOV DPTR,#MSG7 ; NEXT DISPLAY MESSAGE SJMP DUPE_STRING STR8: MOV DPTR,#LCD_OUTPUT ; OUTPUT SPECIFIED CHARACTER ; COPY THE SPECIFIED CHARACTER STRING TO THE LCD OUTPUT BUFFER. DUPE_STRING: SETB DOING_LCD ; NO INTERRUPTS MOV STRING_DP,DPL ; SAVE LAST STRING OUTPUT JNB LCD_DATA,LOAD_IT ; NO CHARS WAITING ; THERE ARE CHARACTERS WAITING TO BE SENT TO THE LCD - CHECK ; IF THE FIRST CHARACTERS ARE AN AND CLEAR_DISPLAY MOVX A,@DPTR ; GET FIRST BYTE CJNE A,#ESC,FIND_END ; NOT A ZERO INC DPTR ; POINT TO INSTRUCTION BYTE MOVX A,@DPTR XCH A,DPL ; DECREMENT THE DPTR JNZ DECDP DEC DPH DECDP: DEC A XCH A,DPL CJNE A,#CLEAR_DISPLAY,FIND_END LOAD_IT: PUSH DPH PUSH DPL MOV DPTR,#LCD_BUFFER MOV DPL,LCD_POINTER INC DPL MOV TEMP_HOLD,DPL ; SAVE THE LCD BUFFER POINTER NEXT_CHAR: POP DPL POP DPH MOVX A,@DPTR ; GET NEXT CHAR TO SEND CJNE A,#'"',MORE_CHARS SJMP ONE_MORE MORE_CHARS: INC DPTR ; SET UP FOR NEXT CHARACTER PUSH DPH PUSH DPL ONE_MORE: MOV DPTR,#LCD_BUFFER MOV DPL,TEMP_HOLD MOVX @DPTR,A INC TEMP_HOLD CJNE A,#'"',NEXT_CHAR SETB LCD_DATA ; CHARS READY FOR LCD CLR DOING_LCD RET FIND_END: ; FIND THE LAST CHARACTER IN THE OUTPUT BUFFER PUSH DPH PUSH DPL MOV DPTR,#LCD_BUFFER MOV DPL,LCD_POINTER ; LOAD THE CURRENT POINTER GET_END_CHAR: INC DPL MOVX A,@DPTR CJNE A,#'"',GET_END_CHAR ; FOUND THE END OF THE OUTPUT BUFFER MOV TEMP_HOLD,DPL ; SAVE THE LCD BUFFER POINTER SJMP NEXT_CHAR $ EJECT ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ TIMER0 INTERRUPT SERVICING ROUTINE ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; Timer0Int: PUSH ACC ; 5 mS interrupt PUSH PSW PUSH DPH PUSH DPL MOV A,TL0 ; Reload Timer 0 for 5 mS ADD A,#LOW -4998 ; (Less 2uS to compensate MOV TL0,A ; for ADD & MOV latency.) MOV TH0,#HIGH -4998 INC CLOCK1 ; UPDATE TIMER FOR BASIC ; ---------------------- LCD UPDATE --------------------------- DO_LCD: JB DOING_LCD,ReadKeys ; Wait until next loop JNB LCD_DATA,ReadKeys ; Check for LCD Chars MOV DPTR,#LCD_BUFFER ; POINT TO BUFFER INC LCD_POINTER ; POINT TO NEXT CHARACTER MOV DPL,LCD_POINTER MOVX A,@DPTR ; GET IT CJNE A,#'"',VALID_CHAR ; DONE YET? CLR LCD_DATA ; NO MORE CHARACTERS NOW SJMP ReadKeys VALID_CHAR: MOV REG_SEL,#0 ; POINT TO DATA REGISTER CJNE A,#ESC,WRITE_DATA ; CONTROL CHARACTER? MOV REG_SEL,#CMD ; OUTPUT TO COMMAND REGISTER INC LCD_POINTER MOV DPL,LCD_POINTER MOVX A,@DPTR ; GET IT WRITE_DATA: MOV TEMP_HOLD,A ; SAVE IT $ EJECT ; --------------------- OUTPUT CHARACTER ---------------------- MOV DPTR,#PORT_B ; SET CONTROL LINES MOV A,#CLOCK ORL A,REG_SEL MOVX @DPTR,A ; SELECT COMMAND MOV A,TEMP_HOLD ; GET THE BYTE CLOCK_THE_DATA: MOV DPTR,#PORT_A ; POINT TO DATA LINES MOVX @DPTR,A ; OUTPUT THE BYTE MOV DPTR,#PORT_B ; POINT TO CONTROL LINES MOV A,#CLOCK_IT ; SET 'E' HIGH ORL A,REG_SEL MOVX @DPTR,A SETB ACC.7 MOVX @DPTR,A ; AND CLEAR 'E' ReadKeys: ; -------------------- SCAN THE KEYPAD ------------------------ $IF NOT(NOKEY) MOV DPTR,#PORT_B ; POINT TO KEYPAD COLUMNS MOV A,#ROW1 ; GET THE FIRST ROW MOVX @DPTR,A MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOVX A,@DPTR ; READ THEM 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 LOCATION MOVX @DPTR,A CLR NR1 $ EJECT READ2: MOV DPTR,#PORT_B ; POINT TO KEYPAD COLUMNS MOV A,#ROW2 ; GET THE SECOND ROW MOVX @DPTR,A MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOVX A,@DPTR ; READ THEM 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 LOCATION MOVX @DPTR,A CLR NR2 READ3: MOV DPTR,#PORT_B ; POINT TO KEYPAD COLUMNS MOV A,#ROW3 ; GET THE THIRD ROW MOVX @DPTR,A MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOVX A,@DPTR ; READ THEM 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 LOCATION MOVX @DPTR,A CLR NR3 READ4: MOV DPTR,#PORT_B ; POINT TO KEYPAD COLUMNS MOV A,#ROW4 ; GET THE FOURTH ROW MOVX @DPTR,A MOV DPTR,#PORT_C ; POINT TO KEYPAD ROWS MOVX A,@DPTR ; READ THEM 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 LOCATION MOVX @DPTR,A CLR NR4 SJMP LoadKey $ EJECT NEW1: SETB NR1 ; NEW VALUE FOR ROW1 MOV A,TEMP_KEY MOVX @DPTR,A ; SAVE THE NEW VALUE INC DPH ; POINT TO DEBOUNCED LOCATION 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 LOCATION 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 LOCATION 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 LOCATION CLR A MOVX @DPTR,A $ EJECT LoadKey: ; UPDATE KEY READING 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 $ENDIF TIME1: ;* Any other function that needs to be executed every *; ;* 5 mS should be programmed here. *; AllDone: POP DPL ; Tail end of 5 mS interrupt POP DPH POP PSW POP ACC RETI $ EJECT ; ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸ ; ³ CHARACTER STRING DEFINITIONS ³ ; ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ; MSG1: DB 'Blue Earth' DB ESC,LINE2_0,'Research"' MSG2: DB ESC,CLEAR_DISPLAY,' Micro-440' DB ESC,LINE2_0,'Demonstration"' MSG3: DB ESC,CLEAR_DISPLAY,'Thomas Bachmann' DB ESC,LINE2_0,'Programmer"' MSG4: DB ESC,CLEAR_DISPLAY,' Put YOUR' DB ESC,LINE2_0,'Message HERE"' MSG5: DB ESC,SHIFT_RIGHT,'"' MSG6: DB ESC,SHIFT_LEFT,'"' MSG7: DB ESC,CLEAR_DISPLAY,'Press keys' DB ESC,LINE2_0,'Now "' MSG8: ; Any number of messages may be placed here for MSG9: ; displaying on the LCD. Note that commands ; are preceeded by an ESC, and each message ; must end with a quote character ("). $IF NOT(NOKEY) RC1: DB 'A33222211111111' RC2: DB 'B66555544444444' RC3: DB 'C99888877777777' RC4: DB 'D##0000********' $ENDIF ; END OF PROGRAM END ; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ