;This code is listed here for entertainment value only, it should ;not be used for anything important. Do not use this for a saleable product. ;Feel free to pass this on to others in it's entirety. along with this message. ;No Guarantees implicit or otherwise are implied, your mileage may vary. $RB(0) $RB(1) $NOMOD51 INCLUDE(87C752.pdf) $INCLUDE(common.inc) EXTRN CODE(PUTLCD) EXTRN CODE(PUTCHAR) EXTRN CODE(IPUTCHAR) EXTRN CODE(INTCHAR) EXTRN CODE(INITLCD) EXTRN CODE(HEXSTR) EXTRN CODE(CRH0) EXTRN CODE(LONGWAIT) PUBLIC CRV0 PUBLIC MYINT0 CSEG AT 0000H ; RESET VECTOR ADDRESS CRV0: AJMP CRH0 ; JMP TO RESET HANDLER CSEG AT (0 * 8 + 3) ; INTERRUPT NO. 0 VECTOR ADDRESS ; This interrupt line goes low on the start bit of a character and ; then the line gets looked at as an input for the rest of the character. MYINT0: AJMP INTCHAR ; All of the following infinite loops are to catch any buggy code which ends ; up at an interrupt and set the P3 output to the interrupt number for ; debugging. I'm not expecting this to get executed. CSEG AT (1 * 8 + 3) _tmp1: MOV P3,#1 SJMP _tmp1 CSEG AT (2 * 8 + 3) _tmp2: MOV P3,#2 SJMP _tmp2 CSEG AT (3 * 8 + 3) _tmp3: MOV P3,#3 SJMP _tmp3 CSEG AT (4 * 8 + 3) _tmp4: MOV P3,#4 SJMP _tmp4 CSEG AT (5 * 8 + 3) _tmp5: MOV P3,#5 SJMP _tmp5 CSEG AT (6 * 8 + 3) _tmp6: MOV P3,#6 SJMP _tmp6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Variable usage, is R6 and R7 are temporaries used for timings. ; DPTR is used for timings for INITIALIZATION of LCD. ; In Register bank 0 ; R0 is a pointer to the head of a circular character buffer, only looked ; at and modified by HANDLE_CHARACTER ; R1 is a pointer to the tail of a circular character buffer, only looked ; at and modified by INTCHAR character interrupt routine. ; Circular character buffer starts at IBUFSTART and ends just before IBUFEND ; In Register bank 1 ; R0 is a pointer to the head of a circular character buffer, only looked ; at and modified by HANDLE_CHARACTER ; R1 is a pointer to the tail of a circular character buffer, only looked ; at and modified by PUTCHAR character interrupt routine. ; Circular character buffer starts at IBUFSTART and ends just before IBUFEND ; R2 contains current Temperature. ; R3 is a temporary used by the read from the LCD ; R5 gets trashed at any time. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CLIBC SEGMENT CODE RSEG CLIBC HELLOSTR: DB 'Keypad on ....', 0 PUBLIC _MAIN PUBLIC NORMLCD ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; _MAIN ; Loops infinitely looking for stuff to do. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; _MAIN: ; Don't let the display get screwedup MOV P3,#LOW(NOT (EN OR EN2)) MOV B,#EN ; Initialize the first, up to, 40 x 2 ACALL INITLCD ; Initialize the LCD display. ; The commented out code is for a 40x4 display. ; MOV B,#EN2 ; Initialize the second 40 x 2 ; ACALL INITLCD ; Initialize the LCD display. MOV DPTR,#HELLOSTR ; Say hello. ACALL LCDSTR ACALL GETTEMP ; get the temperature initially TOPLOOP: JB CTS,NOTURN ; our turn to empty output buffer? ACALL IPUTCHAR ; call routine to actually put the character NOTURN: MOV A,R0 CLR C SUBB A,R1 ; Check input buffer (Character ready in *R0) JZ TRYTEMP ; Buffer is empty, try getting the temp. ACALL HANDLE_CHAR ; Deal with the character LJMP KEEPON TRYTEMP: ACALL GETTEMP ; Only get the temperature if buffer is empty. KEEPON: ACALL SCANKEYS ; Scan the keypad for key presses. XRL A,OLDCHAR ; If the character is the same as last time JZ TOPLOOP ; ignore it. XRL A,OLDCHAR ; otherwise fix the xor to get the character ; back MOV OLDCHAR,A ; Save character as old character JZ TOPLOOP ; If character is null, we aren't interested ACALL PUTCHAR ; dump character to serial line SJMP TOPLOOP ; Go back and do it all again. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; GETTEMP ; Does an A/D on the Temperature probe and puts it into R2 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GETTEMP: MOV A,ADCON ; Get status bits of A/D for validity test. JNB ACC.4,SAME_TEMP ; No valid data yet, Just bail. MOV R2,ADAT ; Get data into R2 ; Set up status for another round of sampling MOV ADCON,#(ENADC OR ADCS OR AADR1) MOV A,R2 ; Get data into A for processing. CLR C ; Only show difference if more than 1. SUBB A,OLDTEMP JZ SAME_TEMP JNB ACC.7,POS_DIFF XRL A,#0ffh ; Invert JZ SAME_TEMP SJMP LCD_TEMP POS_DIFF: SUBB A,#1 JZ SAME_TEMP LCD_TEMP: ; Display temp at location PUSH COL ; Save old column BUGBUG should save ROW too MOV COL,#12 ; temp is in format 100o starting at col 12 ACALL ROWCOL MOV A,R2 ; get temp. MOV OLDTEMP,A ; save it away. MOV DPTR,#TTABLE ; offset from Temp. Table MOVC A,@A+DPTR ; get Temperature value from table. ACALL DECLCD ; out temp to LCD MOV A,#223 ; output degree symbol ACALL NORMLCD POP COL ; recover old COL location, BUGBUG recover RET ; ROW information too once its saved. CH_ENQ: ; Alternate means to force a temp query. ; ^E was pressed. MOV A,#'T' ; Format is T=XX where XX are raw temperature ACALL PUTCHAR ; data NOT gone through the table. MOV A,#'=' ACALL PUTCHAR MOV A,R2 MOV DPTR,#TTABLE ; offset from Temp. Table MOVC A,@A+DPTR ; get Temperature value from table. ACALL HEXSTR SAME_TEMP: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; SCANKEYS ; Scanns the keypads once and sends Character to master if pressed. ; and sets ACC to 0 or the key pressed. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SCANKEYS: ; Leave only the scan lines. MOV A,#LOW(NOT (EN OR EN2)) MOV P3,A MOV R7,#0 ; R7 contains count of scan lines. MOV R6,#0 ; R6 contains count of row lines. TRYNEXT: CLR C ; Ensure E2 doesnt get shifted high RRC A MOV P3,A ORL A,#EN ; Ensure only one col low at a time JNB KP_R1,ROW_1 JNB KP_R2,ROW_2 JNB KP_R3,ROW_3 JNB KP_R4,ROW_4 INC R7 JC TRYNEXT MOV A,#0 ; No key pressed. RET ; Note fallthrough cases for the rows. Row 1 R6 := 0, .. Row 4, R6 := 3 ROW_4: INC R6 ROW_3: INC R6 ROW_2: INC R6 ROW_1: MOV A,R7 RL A RL A ORL A,R6 ; Now A contains 000cccrr MOV DPTR,#KEYTABLE MOVC A,@A+DPTR ; Translate from row/col to character. ; Leave only the scan lines. MOV P3,#LOW(NOT (EN OR EN2)) RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; HANDLE_CHAR ; Assumes a serial character has been received and either prints it to the ; LCD display, or in the case of a special character, executes an action. ; Any character below 32 ' ' is considered special. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Note assumption here is we've already verified there is a character in the ; input buffer. HANDLE_CHAR: MOV B,@R0 ; Get character INC R0 ; And updated input head pointer. CJNE R0,#IBUFEND,READBUFOK MOV R0,#IBUFSTART ; Adjust to wrap in the case where at end. READBUFOK: MOV A,MYSW ; Get status bits. JB ACC.RAW,CH_RAW ; In raw mode, don't interpret the character ; send it directly to the LCD ; Changing the color of the status led. JB ACC.COLOR,CH_COLOR MOV A,B ; If greater than 31 treat as a printing char. ANL A,#LOW(NOT 31) JNZ NORMCHAR MOV A,B ; Special character RL A INC A ; Address high byte is at char*2+1+SFJT MOV DPTR,#SFJT ; Set up jump table for special characters. MOVC A,@A+DPTR PUSH ACC ; Save 1/2 address for call(ret) on stack MOV A,B RL A ; Address low byte is at char*2+SFJT MOVC A,@A+DPTR PUSH ACC ; Save 1/2 address for call(ret) on stack RET ; And call the special routine by returning. ; Change the 3 color LED's color CH_COLOR: MOV A,B SWAP A ; move lower 4 bits as color control ANL A,#0f0h ; ignore small stuff. ; Note PWCM is being used to vary the duty cycle of the three color LED for ; status information. MOV PWCM,A ; Color mode, just reset color number MOV ACC,MYSW CPL ACC.COLOR ; And get out of color mode. MOV MYSW,ACC RET ; Drive the LCD directly WARNING only if you know what you are doing. CH_RAW: MOV A,B ; Escape mode, just drive LCD direct. ACALL PUTLCD MOV ACC,MYSW CLR ACC.RAW ; Force out of raw mode after one char. MOV MYSW,ACC RET ; Handle carriage return by setting column to 0 CH_CR: MOV COL,#0 RET ; Reboot the keypad CH_REBOOT: AJMP CRH0 ; Force a reboot. ; Increment the character position CH_INC: INC COL ; Bump the column number MOV A,COL CLR C SUBB A,#COLS ; If at the end of a row, auto cr/lf. JNZ NO_INC MOV COL,#0 ; force cr. SJMP CH_LF ; and a lf. NO_INC: RET ; Linefeed increment the line number CH_LF: INC ROW ; Increment the line number MOV A,ROW CLR C SUBB A,#ROWS JNZ NO_LF MOV ROW,#0 ; If line is off the display reset to top line. NO_LF: RET ; Reverse Linefeed, ignore if at the top of the display. ; Note this is not symmetric with linefeed on purpose. It makes getting to the ; top line possible, in a device independant way, where multiple keypads with ; differing heights are used. CH_CTRLK: MOV A,ROW JZ NO_BS DEC ROW RET ; BackSpace, ignore if at the beginning of a line. CH_BS: MOV A,COL JZ NO_BS DEC COL NO_BS: ; Also exit point for 'do nothing' characters. RET ; Put in raw mode, note currently next character will toggle back out of ; this mode. CH_ESC: MOV ACC,MYSW CPL ACC.RAW ;Toggle raw mode for my status word. MOV MYSW,ACC RET ; Put in color mode, note currently next character will toggle back out of ; this mode. CH_CTRLC: MOV ACC,MYSW CPL ACC.COLOR ;Toggle color mode for my status word. MOV MYSW,ACC RET ; Handle normal character, note any number of other routines can mess with ; what the LCD display thinks is the current location (like the temp routine.) NORMCHAR: PUSH B ; save character ACALL ROWCOL ; set row and column to proper location. ACALL CH_INC ; advance to next character location. POP ACC ; recover character ACALL NORMLCD ; and send it to the LCD. RET ;Special function jump table for special characters. backspace, cr, lf, etc. SFJT: DW NO_BS, NO_BS, NO_BS, CH_CTRLC, NO_BS, CH_ENQ, NO_BS, NO_BS DW CH_BS, NO_BS, CH_LF, CH_CTRLK, NO_BS, CH_CR, NO_BS, NO_BS DW NO_BS, NO_BS, CH_REBOOT, NO_BS, NO_BS, NO_BS, NO_BS, NO_BS DW NO_BS, NO_BS, NO_BS, CH_ESC, NO_BS, NO_BS, NO_BS, NO_BS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ROWCOL ; Sets the Row and Column on the display ; setable for 40x4 display. ; Currently set for 16x1 display. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Note, E(P3.6 and E2(P3.7) refer to enable lines on LCD display. ; Note, RW(P3.4 and RS(P3.5) refer to read/write and reset on LCD display. ; Note, the LCD display is being driven in 4 bit mode NOT 8 bit mode. ROWCOL: MOV A,ROW ORL A,#80h ; 80h is the base address for LCD ; MOV C,ACC.0 ; Odd rows for 40x4 start at C0h ; MOV ACC.6,C ANL A,#0F0h ; Mask off old row bits. ADD A,COL ; column offsed is just added MOV C,ACC.3 ; cols > 7 for 16x1 start at C0h MOV ACC.6,C ; cols > 7 for 16x1 start at C0h CLR ACC.3 ; cols > 7 for 16x1 start at C0h PUSH ACC ; Save away address. ANL A,#0f0h ; Blast low nibble. ORL A,ROW ; Get row into low nibble. MOV C,ACC.1 ; move high row bit into carry MOV ACC.3,C ; choose E2 if row 2 or 3 CPL C ; otherwise MOV ACC.2,C ; choose E ANL A,#LOW(NOT 3) ; blast lower 2 bits (eventually RW and RS) SWAP A ; Get bits where they belong. ACALL PUTLCD POP ACC ; Get address. ANL A,#0fh ; mask off high bits SWAP A ; move low data to high. ORL A,ROW ; move row data into low nibble MOV C,ACC.1 ; move high row bit into carry MOV ACC.3,C ; choose E2 if row 2 or 3 CPL C ; otherwise MOV ACC.2,C ; choose E ANL A,#LOW(NOT 3) ; blast lower two bits. SWAP A ; move low bits back into data position. ACALL PUTLCD RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; NORMLCD ; Outputs a character to the display. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NORMLCD: PUSH ACC ; optimally this would be the same code as ANL A,#0f0h ; above. ORL A,ROW MOV C,ACC.1 MOV ACC.3,C CPL C MOV ACC.2,C ANL A,#LOW(NOT 3) SWAP A ORL A,#20h ACALL PUTLCD POP ACC ANL A,#0fh SWAP A ORL A,ROW MOV C,ACC.1 MOV ACC.3,C CPL C MOV ACC.2,C ANL A,#LOW(NOT 3) SWAP A ORL A,#20h ACALL PUTLCD RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; LCDSTR ; Outputs a null terminated string at CS:DPTR to the display. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LCDSTR: ACALL ROWCOL ; Get to correct row and column ACALL CH_INC ; BUGBUG Shouldn't this be after character? CLR A MOVC A,@A+DPTR JZ DONESTR PUSH DPL PUSH DPH ACALL NORMLCD POP DPH POP DPL INC DPTR SJMP LCDSTR DONESTR: RET DECLCD: PUSH ACC CLR F0 ; Use F0 as leading zero blanking indicator MOV B,#100 DIV AB JZ NO_HUND SETB F0 NO_HUND: ACALL DECASC ACALL NORMLCD MOV A,#10 XCH A,B DIV AB JZ NO_TEN SETB F0 NO_TEN: ACALL DECASC ACALL NORMLCD SETB F0 ; Force at least one zero on output. XCH A,B ACALL DECASC ACALL NORMLCD POP ACC RET DECASC: JNB F0,BLANKZER ADD A,#'0' ; Get binary represenation offset to '0' char. RET BLANKZER: MOV A,#32 ; Use a space if zero blanking is on. RET ; This is the translation from row/column scan to character KEYTABLE: DB '369#' DB '2580' DB '147*' DB 'ouvF' DB 'fdts' DB 'rrkl' $INCLUDE(temp.src) $EJECT END