;*************************************************************************** ; ; AT Keyboard Interface with Morse Code V2.02 ; =========================================== ; ; written by Peter Luethi, 23.03.2003, Dresden, Germany ; http://www.electronic-engineering.ch ; last update: 11.04.2004 ; ; V2.02: Added LCDcflag (LCD command/data flag) to comply with ; latest LCD modules. ; (11.04.2004) ; ; V2.01: Improved AT keyboard and RS232 initialization, ; fixed RBIF/INTF interrupt initialization issue. ; Changed keyboard data pin to PORTA,4 (open-collector). ; Added special LCD treatment for 'tabulator' key. ; (16.08.2003) ; ; V2.00: Initial release (23.03.2003) ; ; This code and accompanying files may be distributed freely and ; modified, provided this header with my name and this notice remain ; intact. Ownership rights remain with me. ; You may not sell this software without my approval. ; ; This software comes with no guarantee or warranty except for my ; good intentions. By using this code you agree to indemnify me from ; any liability that might arise from its use. ; ; ; SPECIFICATIONS: ; =============== ; Processor: Microchip PIC 16F84 ; Clock Frequency: 4.00 MHz XT ; Throughput: 1 MIPS ; RS232 Baud Rate: 9600 baud (depends on the module included) ; Serial Output: 9600 baud, 8 bit, no parity, 1 stopbit ; Acquisition Methodology: Preemptive, interrupt-based ; keyboard scan pattern acquisition ; routine, with decoding to ASCII ; characters during normal operation ; (incl. LCD display and RS232 activities) ; Code Size of entire Program: 973 instruction words ; Required Hardware: AT Keyboard, MAX 232, ; HD44780 compatible dot matrix LCD ; (2x16, 2x20 or 2x40 characters) ; Optional Hardware: Piezo beeper with decoupling capacitor ; Required Software: RS232 terminal ; ; ; ABSTRACT: ; ========= ; This routine converts AT keyboard scan patterns to ASCII characters ; and transmits them afterwards to the target device by using the ; RS232 transmission protocol. Further, there is a Morse port where ; typed characters are transmitted in Morse code. ; Support of english (QWERTY) and modified swiss-german (QWERTZ) ; 'codepages'. This implementation features an LCD display as visual ; interface, but only for transmitted characters typed on the local ; keyboard (unidirectional data flow). ; ; ; DESCRIPTION: ; ============ ; Developed and tested on PIC 16F84. ; Any key stroke on the keyboard connected to the PIC will send the ; corresponding scan code from the keyboard to the microcontroller. ; Afterwards, the microcontroller converts the keyboard scan code to ; ASCII characters and sends them to the computer terminal via the ; RS232 connection. ; ; The keyboard scan pattern capture and decoding is done by an ; interrupt service routine. The event, which triggers the interrupt ; is a falling edge on the keyboard clock line at the KBDclkpin ; (PORTB,0). The keyboard data (scan code) will be fetched at the ; KBDdatapin (PORTA,4). ; There is only RS232 transmission, so only the TXport (PORTA,0) is ; connected. Since PORTB,0 is already used by the keyboard clock line, ; there exists no possibility to use it also for RS232 reception. ; The configuration of the KBDclkpin interrupt is done by the ; KEYBOARDinit macro. ; ; For the AT keyboard layout, English and modified Swiss-German ; 'codepages' are supported: ; QWERTY and QWERTZ keyboard layout ; ; Morse code feature: ; All legal ASCII characters specified within the Morse alphabet ; are translated to a 16 bit pattern through a look-up table. ; Within these bit patterns, two consecutive ones followed by a ; zero indicate a dash, a dot is encoded as single one followed ; by a zero. ; The Morse code has different lengths for encoding, e.g. ; e = . b = -... 1 = .---- ? = ..--.. ; therefore 16 bits are necessary to support all of them. ; For instance, 'b' is encoded in 16 bits as b'1101_0101_0000_0000' ; Subsequent zeros at the end within a pattern are considered as ; termination and are ignored. The Morse pattern is signaled as ; pulse-width modulated stream at the Morse port, active high. ; ; Acoustic feedback of Morse code output through additional ; Piezo beeper possible. ; ; ; IMPLEMENTED FEATURES: ; ===================== ; - Uni-directional communication between microcontroller application ; and remote RS232 client. ; - Uni-directional communication between microcontroller and keyboard. ; - Support for all keyboard characters typed with shift button active ; and inactive. ; - English and modified Swiss-German 'codepages' available ; (QWERTY and QWERTZ) ; Include the desired keyboard lookup tables at the correct location. ; - Caps Lock implemented ; - Num Lock always active ; - Possibility to implement short-cuts or user defined characters ; for 'Esc', 'Num Lock', 'Scroll Lock' and 'F1' - 'F12' keys ; ; - Further enhancement, not implemented: Support of ASCII conversion ; from direct ALT-DEC and CTRL-HEX entries, e.g. ALT + 6 + 4 = @ ; or CTRL + 3 + F = ? ; ; - Morse code translation and PWM output at Morse port, active high ; - Acoustic feedback through Piezo beeper, default enabled ; ; ; LIMITATIONS: ; ============ ; - No support for ALT-GR characters. ; - Minimized keyboard routine with support for only uni-directional ; communication from keyboard to controller, therefore no control ; over keyboard status LEDs. ; - No support for arrow buttons, 'Home', 'Del', 'PageUp', 'PageDown', ; 'Insert', 'End' because there exists no character/command ; corresponding to the ASCII character map. (But it is possible to ; use them for short-cuts or user defined characters, if the special ; code routine (0xE0) is altered.) ; ; ; NOTE: ; ===== ; This program needs 'ORG' directives to locate tables within entire ; memory pages. To allow for slight modifications, the code has not ; been optimized to the utmost extent regarding program memory ; placement. This can be carried out using the program memory window ; of MPLAB showing the hexadecimal representation of the code. ; ; ; CREDITS: ; ======== ; - Craig Peacock, the author of the excellent page about keyboards ; "Interfacing the PC's Keyboard" available at his website: ; http://www.beyondlogic.org/keyboard/keybrd.htm ; ;*************************************************************************** ;***** COMPILATION MESSAGES & WARNINGS ***** ERRORLEVEL -207 ; found label after column 1 ERRORLEVEL -302 ; register in operand not in bank 0 ;***** PROCESSOR DECLARATION & CONFIGURATION ***** PROCESSOR 16F84 #include "p16f84.inc" ; embed Configuration Data within .asm File. __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC ;***** MEMORY STRUCTURE ***** ;ORG 0x00 processor reset vector, declared below ;ORG 0x04 interrupt service routine, declared below ;***** PARAMETERIZATION ***** CONSTANT BEEP_ENABLE = d'1' ; Piezo beeper, default: enabled ;***** PORT DECLARATION ***** #define TXport PORTA,0x00 ; RS232 output port, could be #define TXtris TRISA,0x00 ; any active push/pull port #define KBDdatapin PORTA,0x04 ; keyboard data input port #define KBDdatatris TRISA,0x04 ; (open-collector pin) #define KBDclkpin PORTB,0x00 ; keyboard clock input port (IntB) #define KBDclktris TRISB,0x00 ; @ INTF interrupt source (RB0) #define MRSport PORTA,0x01 ; Morse code PWM output port #define MRStris TRISA,0x01 LCDtris equ TRISB ; LCD port (B1-B7) LCDport equ PORTB ; LCD port (B1-B7) #define LCDwidth d'40' ; define character width of LCD ; display #define BEEPport PORTA,0x03 ; Piezo beeper output port #define BEEPtris TRISA,0x03 ;***** CONSTANT DECLARATION ***** CONSTANT BASE = 0x0C ; Base address of user file registers CONSTANT LF = d'10' ; line feed CONSTANT CR = d'13' ; carriage return CONSTANT TAB = d'9' ; tabulator CONSTANT BS = d'8' ; backspace ; Morse output - wait constants CONSTANT M_PRESC = d'6' ; prescaler for all Morse waits CONSTANT INTER_W = 0x06 ; inter dash-dot wait CONSTANT CHAR_W = 0x08 ; inter character wait (add. to INTER_W) ; ### IF PIEZO BEEPER IS DISABLED (BEEP_ENABLE = d'0') ### CONSTANT DOT_W = 0x05 ; duration related, short wait, dot CONSTANT DASH_W = 0x11 ; duration related, long wait, dash ; ### ELSE PIEZO BEEPER ENABLED (BEEP_ENABLE = d'1') ### CONSTANT DOT_FREQ = d'145' ; frequency related, for dot CONSTANT DOT_PRESC = d'1' ; duration related, for dot CONSTANT DASH_FREQ = d'250' ; frequency related, for dash CONSTANT DASH_PRESC = d'5' ; duration related, for dash ;***** REGISTER DECLARATION ***** TEMP1 set BASE+d'0' ; Universal temporary register TEMP2 set BASE+d'1' ; ATTENTION !!! TEMP3 set BASE+d'2' ; They are used by various modules. TEMP4 set BASE+d'3' ; If you use them, make sure not to use TEMP5 set BASE+d'4' ; them concurrently ! TEMP6 set BASE+d'5' TEMP7 set BASE+d'6' FLAGreg equ BASE+d'7' ; register containing various flags FLAGreg2 equ BASE+d'8' ; RS232 registers TXD equ BASE+d'9' ; RS232 TX-Data register RXD equ BASE+d'10' ; RS232 RX-Data register (not used here, ; but m_rs232 requires its declaration) ; AT keyboard registers and flags #define RELflag FLAGreg,0x00 ; release flag (0xF0) #define SHIflag FLAGreg,0x01 ; shift flag (0x12 / 0x59) #define SPEflag FLAGreg,0x02 ; special code flag (0xE0) #define CAPflag FLAGreg,0x03 ; caps lock flag (0x0D) #define ALTflag FLAGreg,0x04 ; ALT flag (0x11) #define CTRLflag FLAGreg,0x05 ; CTRL flag (0x14) #define KBDflag FLAGreg,0x06 ; keyboard data reception flag KBDcnt equ BASE+d'11' ; IRQ based keyboard scan pattern counter KBD equ BASE+d'12' ; keyboard scan code & ascii data register KBDcopy equ BASE+d'13' ; keyboard scan code register ; interrupt context save/restore W_TEMP equ BASE+d'14' ; context register (ISR) STATUS_TEMP equ BASE+d'15' ; context register (ISR) PCLATH_TEMP equ BASE+d'16' ; context register (ISR) FSR_TEMP equ BASE+d'17' ; context register (ISR) ; Morse code transmit registers #define MRSflag FLAGreg,0x07 ; Morse flag, valid buffer MRStmp0 equ BASE+d'18' ; temporary register MRStmp1 equ BASE+d'19' ; temporary register MRStmp2 equ BASE+d'20' ; temporary register MRS_pnt set BASE+d'21' ; table pointer MRS_cnt set BASE+d'22' ; one's counter MRS_lbuf equ BASE+d'23' ; buffer, low byte MRS_hbuf equ BASE+d'24' ; buffer, high byte MRS_lo equ BASE+d'25' ; TX register, low byte MRS_hi equ BASE+d'26' ; TX register, high byte ; LCD registers and flags #define LCDbusy FLAGreg2,0x00 ; LCD busy flag #define LCDcflag FLAGreg2,0x01 ; LCD command/data flag #define LCD_ln FLAGreg2,0x02 ; LCD line flag: 0 = line 1, 1 = line 2 LCDpos equ BASE+d'27' ; LCD output position counter ;***** INCLUDE FILES ***** ORG 0x220 #include "..\..\m_bank.asm" #include "..\..\m_wait.asm" #include "..\..\m_rs096.asm" ; 9600 baud @ 4 MHz #include "..\..\m_lcd_bf.asm" IF BEEP_ENABLE == 1 ; conditional assembly #include "..\..\m_beep.asm" ; Piezo beeper ENDIF ;***** MACROS ***** KEYBOARDinit macro BANK1 bsf KBDclktris ; set keyboard clock line to input explicitely bsf KBDdatatris ; set keyboard data line to input explicitely bcf OPTION_REG,INTEDG ; keyboard interrupt on falling edge BANK0 bsf INTCON,INTE ; enable RB0/INT interrupts endm ;***** SUBROUTINES ***** LCDchgln ; change alternating between LCD line 1 and 2 movlw LCDwidth ; get LCD character width movwf LCDpos ; and store in position counter btfss LCD_ln ; check LCD line flag goto _line2 ; if cleared, goto line 2 _line1 LCD_DDAdr 0x00 ; move cursor to beginning of first line bcf LCD_ln ; clear flag _cl_1 LCDchar ' ' ; put subsequent blanks on LCD to clear line decfsz LCDpos,F ; decrement counter goto _cl_1 LCD_DDAdr 0x00 ; reset cursor to beginning of line clrf LCDpos ; reset LCD position counter RETURN _line2 LCD_DDAdr 0x40 ; move cursor to beginning of second line bsf LCD_ln ; set flag _cl_2 LCDchar ' ' ; put subsequent blanks on LCD to clear line decfsz LCDpos,F ; decrement counter goto _cl_2 LCD_DDAdr 0x40 ; reset cursor to beginning of line clrf LCDpos ; reset LCD position counter RETURN ;***** INTERRUPT SERVICE ROUTINE ***** ORG 0x04 ; interrupt vector location ISR ;************************ ;*** ISR CONTEXT SAVE *** ;************************ bcf INTCON,GIE ; disable all interrupts btfsc INTCON,GIE ; assure interrupts are disabled goto ISR movwf W_TEMP ; context save: W swapf STATUS,W ; context save: STATUS movwf STATUS_TEMP ; context save clrf STATUS ; bank 0, regardless of current bank movfw PCLATH ; context save: PCLATH movwf PCLATH_TEMP ; context save clrf PCLATH ; page zero, regardless of current page bcf STATUS,IRP ; return to bank 0 movfw FSR ; context save: FSR movwf FSR_TEMP ; context save ;*** context save done *** ;************************** ;*** ISR MAIN EXECUTION *** ;************************** ;***************************************** ;*** KEYBOARD SCAN PATTERN ACQUISITION *** ;***************************************** ;*** check start bit *** tstf KBDcnt ; check bnz _KBDdat ; branch on no zero btfsc KBDdatapin ; test start bit of keyboard data input goto _KBDabort ; no valid start bit, abort goto _INCF ; exit ;*** keyboard scan pattern acquisition *** _KBDdat movfw KBDcnt ; get kbd scan pattern acquisition counter sublw d'8' ; w = d'8' - KBDcnt (*) bnc _KBDpari ; branch if negative (carry == 0) btfss KBDdatapin ; get keyboard data input bcf KBD,0x07 ; (bit operations do not alter zero flag) btfsc KBDdatapin bsf KBD,0x07 bz _INCF ; exit on zero (zero flag still valid from (*)) rrf KBD,F ; do this only 7 times goto _INCF ; exit ;*** ignore parity bit *** _KBDpari movfw KBDcnt ; get kbd scan pattern acquisition counter sublw d'9' ; w = d'9' - KBDcnt bnc _KBDstp ; branch if negative (carry == 0) goto _INCF ; exit ;*** check stop bit *** _KBDstp btfss KBDdatapin ; check if stop bit is valid goto _KBDabort ; if not set, abort bsf KBDflag ; else set reception flag to decode KBD ;*** stall keyboard *** ; to prevent the arrival of more data before having finished decoding BANK1 ; hold keyboard (with kbd clk low): bcf KBDclktris ; set clk line to output BANK0 bcf KBDclkpin ; set keyboard clk line low (stall) ;*** disable RB0/INT interrupt line *** bcf INTCON,INTE ; disable RB0/INT interrupt goto _KBDterm ; terminate successfully _KBDabort clrf KBD ; abort / invalid data _KBDterm clrf KBDcnt ; reset kbd scan pattern acquisition counter goto _KBDend ; terminate execution of keyboard ISR ;*********************************** ;*** CLEARING OF INTERRUPT FLAGS *** ;*********************************** ; NOTE: Below, I only clear the interrupt flags! This does not ; necessarily mean, that the interrupts are already re-enabled. ; Basically, interrupt re-enabling is carried out at the end of ; the corresponding service routine in normal operation mode. ; The flag responsible for the current ISR call has to be cleared ; to prevent recursive ISR calls. Other interrupt flags, activated ; during execution of this ISR, will immediately be served upon ; termination of the current ISR run. _INCF incf KBDcnt,F ; increment acquisition counter _ISR_RS232error _KBDend bcf INTCON,INTF ; clear RB0/INT interrupt flag ;goto ISRend ; terminate execution of ISR ;***************************************** ;*** ISR TERMINATION (CONTEXT RESTORE) *** ;***************************************** ISRend movfw FSR_TEMP ; context restore movwf FSR ; context restore movfw PCLATH_TEMP ; context restore movwf PCLATH ; context restore swapf STATUS_TEMP,W ; context restore movwf STATUS ; context restore swapf W_TEMP,F ; context restore swapf W_TEMP,W ; context restore RETFIE ; enable global interrupt (INTCON,GIE) ;***** END OF INTERRUPT SERVICE ROUTINE ***** ;***** KEYBOARD SCAN PATTERN DECODING SUBROUTINE ***** KBDdecode ;********************************************************** ;*** KEYBOARD SCAN CODE PRE-DECODING, SET / CLEAR FLAGS *** ;********************************************************** ;*** check key release scan code (F0) *** movfw KBD ; get scan code movwf KBDcopy ; make backup of scan code for later use sublw 0xF0 ; check if FO has been sent: bnz _KBD_1 ; branch if no 'release' scan code occured bsf RELflag ; set key release flag if 'release' occured bcf SPEflag ; clear special code flag always on release goto _ClrStall ; abort with nothing to display _KBD_1 btfss RELflag ; check release flag, exit if cleared: goto _KBD_2 ; release flag has not been set, branch ;*** release flag has been set / key release is in progress: *** bcf RELflag ; clear key release flag ;*** if release of SHIFT key, clear shift flag: *** movfw KBD ; check left shift button (0x12): sublw 0x12 ; subtract, check if zero bz _clrSHI ; if zero, branch movfw KBD ; check right shift button (0x59): sublw 0x59 ; subtract, check if zero skpnz ; skip on non zero _clrSHI bcf SHIflag ; clear shift flag ;*** check for CAPS LOCK activity: *** movfw KBD ; check caps lock key release: sublw 0x58 ; subtract, check if zero bnz _clrALT ; if not zero, branch btfss CAPflag ; check flag, clear flag if set: goto _setCAP ; flag has not been set, branch bcf CAPflag ; clear flag goto _ClrStall ; abort with nothing to display _setCAP bsf CAPflag ; set flag if cleared goto _ClrStall ; abort with nothing to display ;*** check for ALT activity: *** _clrALT movfw KBD ; check ALT key release: sublw 0x11 ; subtract, check if zero bnz _clrCTRL ; if not zero, branch (to next check) bcf ALTflag ; clear flag goto _ALTdec ; goto ALT-DEC-Entry conversion/display routine ; /not implemented, enhancement/ ;*** check for CTRL activity: *** _clrCTRL movfw KBD ; check CTRL key release: sublw 0x14 ; subtract, check if zero bnz _ClrStall ; if not zero, branch / exit bcf CTRLflag ; clear flag goto _CTRLhex ; goto CTRL-HEX-Entry conversion/display routine ; /not implemented, enhancement/ ;**************************************************** ;* The following table has to be located within one * ;* page to allow for correct lookup functionality. * ;* Therefore, additional ISR code has been moved * ;* further down. * ;**************************************************** ;********************************************************************* ;* LOOKUP TABLE FOR KEYBOARD-SCAN-CODE TO ASCII-CHARACTER CONVERSION * ;********************************************************************* ORG 0x361 ; if necessary, move table ;#include "..\tables\eng_main.asm" ; English 'codepage' #include "..\tables\ger_main.asm" ; modified Swiss-German 'codepage' ;**************************************************** ;* The following code belongs also to the interrupt * ;* service routine and has been moved down to this * ;* place to allow the main keyboard decode lookup * ;* table to be located within one page. * ;* The code below may be arranged on another page, * ;* but that doesn't matter. * ;**************************************************** ORG 0x078 ; move, if necessary ;******************************** ;*** SCAN CODE RANGE CHECKING *** ;******************************** _KBD_2 ;*** check if special code (0xE0) has been submitted previously *** btfss SPEflag goto _KBD_3 ;*** decoding of scan code with preceeding special code (0xE0) *** ; (decoding currently only necessary for 'E0 4A' = '/') movfw KBD sublw 0x4A ; 0x4A - w bnz _NOSUP ; branch on non-zero movlw '/' ; store '/' in KBD movwf KBD goto _OUTP _NOSUP ;*** check if scan code 0x5A or smaller has occurred *** movfw KBD sublw 0x5A ; 0x5A - w bc _KBD_3 ; carry if result positive or zero, branch ;*** range exceeded (above 0x5A) *** ; it's one of the following keys: arrow button, 'Home', 'Del', ; 'PageUp', 'PageDown', 'Insert', 'End' ; these keys are currently not used, so goto _ClrStall _KBD_3 ;*** check if scan code 0x61 or smaller has occurred *** movfw KBD sublw 0x61 ; 0x61 - w bc KBD_dec ; carry if result positive or zero, goto table movlw d'14' subwf KBD,F ; KBD = KBD - d'14' ;*** check if scan code 0x61 (0x6F-d'14') or smaller has occurred *** movfw KBD sublw 0x61 ; 0x61 - w bnc _KBD_4 ; no carry if result negative, goto _KBD_4 movlw d'25' addwf KBD,F ; KBD = KBD + d'25' goto KBD_dec _KBD_4 ;*** check if scan code 0x78 (0x86 - d'14') or higher has occurred *** movfw KBD sublw 0x77 ; 0x77 - w bc KBD_dec ; carry if result zero or positive, branch ;*** no character to display: *** ;*** check for special code (0xE0): 0xD2 = 0xE0 - d'14' *** movfw KBD sublw 0xD2 ; 0xD2 - w skpnz ; skip if not zero bsf SPEflag ; special code occurred, set flag goto _ClrStall ; abort with nothing to display ;******************************************************* ;*** SCAN CODE DECODING & ASCII CHARACTER CONVERSION *** ;******************************************************* ;*** DECODE SCAN CODE *** KBD_dec movlw HIGH KBDtable ; get correct page for PCLATH movwf PCLATH ; prepare right page bits for table read movfw KBD call KBDtable ; GET CORRESPONDING KEYBOARD CHAR IN LOOKUP TABLE movwf KBD ; save result to KBD tstf KBD ; test KBD to get the zero flag bz _ClrStall ; abort if KBD is zero / invalid entry, nothing to display ;*** check for ALT-DEC-Entry *** ; /not implemented, enhancement/ ;btfsc ALTflag ; check flag ;goto _ALTstr ; jump if set ;*** check for CTRL-HEX-Entry *** ; /not implemented, enhancement/ ;btfsc CTRLflag ; check flag ;goto _CTRLstr ; jump if set ;*** convert only LETTERS to capital letters: *** ; a letter in KBD value has to be in range from 0x61 to 0x7A movfw KBD sublw 0x60 ; 0x60 - w bc _SHICHR ; carry if result greater or equal zero = no letter, exit movfw KBD sublw 0x7A ; 0x7A - w bnc _SHICHR ; no carry if result negative = no letter, exit ;*** there is now a letter in KBD, check conversion to capital letter: *** movfw KBD btfsc CAPflag ; check caps lock flag goto _SHIset ; flag has been set btfss SHIflag ; check shift flag goto _OUTP ; no flag, exit goto _cnvCAP ; flag has been set, convert to capital letter _SHIset btfsc SHIflag ; check shift flag goto _OUTP ; flag has been set, exit _cnvCAP addlw d'224' ; convert to capital letter (+ d'224') movwf KBD ;goto _OUTP ; (uncomment in case _OUTP will be moved) ;*********************************************************************** ;*** KEYBOARD DATA OUTPUT TO RS232, LCD DISPLAY & MORSE CODE BUFFERS *** ;*********************************************************************** _OUTP ;*** RS232 *** movfw KBD movwf MRStmp0 ; local copy for Morse code support SENDw ; send actual pressed keyboard character ;*** LCD: treat 'backspace' as special case *** movfw KBD ; check for backspace (d'8') BNEQ d'8', _TAB ; branch if KBD != 8 ;*** it is now a 'backspace' *** movfw LCDpos ; load actual LCD cursor position skpz ; check for position zero, skip if so decf LCDpos,F ; decrement LCD cursor position movfw LCDpos ; move cursor position to w btfss LCD_ln ; check LCD line flag goto _LCDmsk ; if currently at line 1, goto mask addlw 0x40 ; else, add offset (LCD line 2) _LCDmsk iorlw b'10000000' ; mask movwf TEMP6 ; store LCD DDRAM address pattern call LCDcomd ; call LCD command subroutine LCDchar ' ' ; overwrite displayed character with blank movfw TEMP6 ; read back LCD DDRAM address pattern call LCDcomd ; call LCD command subroutine goto _ClrStall ; exit _TAB ;*** LCD: treat 'tabulator' as special case *** movfw KBD ; check for tabulator (d'9') BNEQ d'9', _chkLn ; branch if KBD != 9 ;*** it is now a 'tabulator', just add one extra space *** movfw LCDpos ; get LCD position counter BREG LCDwidth-0x1, _TAB2 ; check for 'near EOL' LCDchar ' ' ; add extra space, if not near EOL incf LCDpos,F ; and increment LCD position counter _TAB2 movlw a' ' movwf KBD ; store second space in KBD ;*** check for necessary change of LCD line *** _chkLn movfw LCDpos ; get LCD position counter BRS LCDwidth, _LCDout ; branch if w < LCDwidth call LCDchgln ; call LCD change line subroutine _LCDout incf LCDpos,F ; increment LCD position counter movfw KBD LCDw ; display keyboard character on LCD ;*** Morse Code *** ;check for proper Morse code value (a-z, 0-9, special chars) ;*** A-Z, a-z => 0x41 - 0x5A, 0x61 - 0x7A *** movfw MRStmp0 BRG 0x7A, _ClrStall ; macro, exit if above 0x7A movfw MRStmp0 BRS 0x41, _MRSnum ; branch on w < 0x41 ; w >= 41, must be alphabetic character or invalid bcf MRStmp0,0x5 ; x - 0x20 if x in [0x60-0x7A] movfw MRStmp0 BRS 0x41, _ClrStall ; branch on w < 0x41 ;now in 0x41 - 0x5A, calculate table pointer rlf MRStmp0,W ; get ASCII character value, multiply 2 addlw d'146' ; subtract (wrap-around addition) movwf MRS_pnt ; store calculated pointer goto _MRS_dec ; goto decoding _MRSnum ;*** 0-9 => 0x30 - 0x39 *** movfw MRStmp0 BRS 0x30, _MRSspc1 ; branch on w < 0x30 movfw MRStmp0 BRG 0x39, _MRSspc2 ; branch on w > 0x39 ;now in 0x30 - 0x39, calculate table pointer rlf MRStmp0,W ; get ASCII character value, multiply 2 addlw d'159' ; subtract (wrap-around addition) movwf MRS_pnt ; store calculated pointer goto _MRS_dec ; goto decoding _MRSspc1 ;*** special chars < 0x30 *** ;" 0x22, ' 0x27, ( 0x28, ) 0x29, , 0x2C, ; - 0x2D, . 0x2E movfw MRStmp0 BNEQ 0x22, _MRSspc1a ; wasn't ", try next one movlw d'72' ; load discrete pointer goto _MRSspc3 _MRSspc1a movfw MRStmp0 BNEQ 0x27, _MRSspc1b ; wasn't ', try next one movlw d'74' ; load discrete pointer goto _MRSspc3 _MRSspc1b movfw MRStmp0 BNEQ 0x28, _MRSspc1c ; wasn't '(', try next one movlw d'76' ; load discrete pointer goto _MRSspc3 _MRSspc1c movfw MRStmp0 BNEQ 0x29, _MRSspc1d ; wasn't ')', try next one movlw d'76' ; load discrete pointer goto _MRSspc3 _MRSspc1d movfw MRStmp0 BNEQ 0x2C, _MRSspc1e ; wasn't ',', try next one movlw d'78' ; load discrete pointer goto _MRSspc3 _MRSspc1e movfw MRStmp0 BNEQ 0x2D, _MRSspc1f ; wasn't '-', try next one movlw d'80' ; load discrete pointer goto _MRSspc3 _MRSspc1f movfw MRStmp0 BNEQ 0x2E, _ClrStall ; wasn't '.' - exit movlw d'82' ; load discrete pointer goto _MRSspc3 _MRSspc2 ;*** special chars > 0x39: : 0x3A, ? 0x3F *** movfw MRStmp0 BNEQ 0x3A, _MRSspc2a ; wasn't ':', try next one movlw d'84' ; load discrete pointer goto _MRSspc3 _MRSspc2a movfw MRStmp0 BNEQ 0x3F, _ClrStall ; wasn't '?', invalid, exit movlw d'86' ; load discrete pointer ;goto _MRSspc3 _MRSspc3 movwf MRS_pnt ; store calculated pointer goto _MRS_dec ; goto decoding ;prepare decoding _MRS_dec bsf MRSflag ; if valid, set flag movwf MRS_pnt ; store value in pointer ;get corresponding Morse code lookup-table entry ;and store entries in morse buffer (high & low) movlw HIGH MORSEtable ; get correct page for PCLATH movwf PCLATH ; prepare right page bits for table read movfw MRS_pnt ; get pointer for high value call MORSEtable ; call lookup-table movwf MRS_hbuf ; store in high buffer incf MRS_pnt,f ; increment pointer for low value movfw MRS_pnt ; get pointer call MORSEtable ; call lookup-table movwf MRS_lbuf ; store in low buffer goto _ClrStall ; (uncomment in case _ClrStall will be moved) ;************************************************ ;*** SPECIAL COMMANDS I (with special output) *** ;************************************************ _CRLF call LCDchgln ; call LCD change line subroutine SEND CR ; on "Enter", send CR and LF to RS232 SEND LF RETLW 0 ; clear w to obtain invalid entry ;********************************************************** ;*** STALL RELEASE & CLEAR KEYBOARD DATA RECEPTION FLAG *** ;********************************************************** _ClrStall BANK1 bsf KBDclktris ; set clk line back to input (and goes high) BANK0 ; (release stall) bcf KBDflag ; clear keyboard data reception flag bsf INTCON,INTE ; re-enable interrupt RB0/INT RETURN ;**************************************************** ;*** SPECIAL COMMANDS II (without special output) *** ;**************************************************** _SHIFT bsf SHIflag ; set shift flag RETLW 0 ; clear w to obtain invalid entry _ALT bsf ALTflag ; set ALT flag RETLW 0 ; clear w to obtain invalid entry _CTRL bsf CTRLflag ; set CTRL flag RETLW 0 ; clear w to obtain invalid entry ;*********************************************** ;*** ALT-DEC & CTRL-HEX STORING & CONVERSION *** ;*********************************************** ; store typed numbers in CTRLreg1 - CTRLreg3 _CTRLstr ; /not implemented, enhancement/ _ALTstr ; /not implemented, enhancement/ _ALTdec ; PRE: ALT + [1..3] numbers (e.g. ALT + 6 + 4 = @) in CTRLreg1 - 3 ; POST: convert ALT-DEC-Entry after release of ALT key, return ; ascii value converted from numbers ; /not implemented, enhancement/ _CTRLhex ; PRE: CTRL + [1..2] letters/numbers (e.g. CTRL + 3 + F = ?) ; in CTRLreg1 - 2 ; POST: convert ALT-DEC-Entry after release of ALT key, return ; ascii value as concatenated hex value from numbers ; /not implemented, enhancement/ ; catch all handler for non-implemented features: goto _ClrStall ; abort & exit (nothing to display/send) ;***************************************************************** ;*** SCAN CODE DECODING & ASCII CONVERSION FOR 'SHIFT' ENTRIES *** ;***************************************************************** _SHICHR ;*** special character decoding typed with shift button active *** ; check for active shift button, if not active, branch btfss SHIflag goto _OUTP ; branch ; check for 'backspace', 'tab', 'linefeed' and 'carriage return' : ; (here, KBD has already been converted to ASCII character values) movfw KBD sublw d'13' ; d'13' - w bc _OUTP ; carry if result zero or positive, branch ;*** range check: abort if KBDcopy greater than 0x61 *** ; (KBDcopy has the value of the original keyboard scan code) movfw KBDcopy sublw 0x61 ; 0x61 - w bnc _OUTP ; no carry if result negative, branch ;*** check if KBDcopy greater than 0x3C *** movfw KBDcopy sublw 0x3C ; 0x3C - w bc _SHICH1 ; carry if result zero or positive, branch movlw d'61' subwf KBDcopy,F ; KBDcopy = KBDcopy - d'61' goto _SHICH3 ;*** check if KBDcopy greater than 0x24 *** _SHICH1 movfw KBDcopy sublw 0x24 ; 0x24 - w bc _SHICH2 ; carry if result zero or positive, branch movlw d'35' subwf KBDcopy,F ; KBDcopy = KBDcopy - d'35' goto _SHICH3 ;*** else *** _SHICH2 movlw d'4' addwf KBDcopy,F ; KBDcopy = KBDcopy + d'4' _SHICH3 movlw HIGH KBDSHIFTtable ; get correct page for PCLATH movwf PCLATH ; prepare right page bits for table read movfw KBDcopy call KBDSHIFTtable ; GET CORRESPONDING KEYBOARD CHAR IN LOOKUP TABLE movwf KBD ; save result to KBD goto _OUTP ;******************************************************* ;* The following table has also to be located within * ;* one page to allow for correct lookup functionality. * ;******************************************************* ;********************************************************************** ;* LOOKUP TABLE FOR SPECIAL CHARACTERS TYPED WITH SHIFT BUTTON ACTIVE * ;********************************************************************** ORG 0x3DA ; if necessary, move table ;#include "..\tables\eng_shif.asm" ; English 'codepage' #include "..\tables\ger_shif.asm" ; modified Swiss-German 'codepage' ;***** END OF SCAN PATTERN DECODING SUBROUTINE ***** ORG 0x1E0 ;***** MORSE OUTPUT SUBROUTINE ***** MORSEout movfw MRS_lbuf ; copy buffers to output registers movwf MRS_lo movfw MRS_hbuf movwf MRS_hi bcf MRSflag ; reset Morse flag clrf MRS_cnt ; clear one's counter movlw 0x2 movwf MRStmp0 ; set up outer loop counter _MRSloop1 movlw 0x8 movwf MRStmp1 ; set up inner loop counter ; in first pass, take high value, in second pass, take low value btfsc MRStmp0,1 ; check for first outer loop pass movfw MRS_hi ; get high value btfss MRStmp0,1 ; check for second outer loop pass movfw MRS_lo ; get low value movwf MRStmp2 ; store in temp register _MRSloop2 btfss MRStmp2,0x7 ; check for 1 goto _MRSout ; it has been a zero, signal now incf MRS_cnt,F ; increment one's counter goto _MRSnext ; continue loop _MRSout ;*** check for next dash/dot to signal *** tstf MRS_cnt ; test register skpnz ; skip on not zero goto _MRSnext ; if zero, goto next loop ;*** put now dash/dot to Morse output port *** bsf MRSport ; set Morse port movfw MRS_cnt ; move counter to w BREG 0x2, _MRSdash ; if >= 2, jump to dash IF BEEP_ENABLE == 1 ; conditional assembly BEEP DOT_FREQ,DOT_PRESC ; (short beep, dot) ELSE WAITX DOT_W,M_PRESC ; (short wait, dot) ENDIF goto _MRSclr _MRSdash IF BEEP_ENABLE == 1 ; conditional assembly BEEP DASH_FREQ,DASH_PRESC ; (long beep, dash) ELSE WAITX DASH_W,M_PRESC ; (long wait, dash) ENDIF _MRSclr bcf MRSport ; clear Morse port WAITX INTER_W,M_PRESC ; (inter dot-dash wait) clrf MRS_cnt ; clear one's counter _MRSnext rlf MRStmp2,F ; rotate left decfsz MRStmp1,F goto _MRSloop2 ; INNER LOOP decfsz MRStmp0,F goto _MRSloop1 ; OUTER LOOP WAITX CHAR_W,M_PRESC ; (inter character wait) RETURN ;***** END OF MORSE OUTPUT SUBROUTINE ***** ;************** MAIN ************** MAIN ORG 0x00 BANK1 clrf OPTION_REG ; PORTB pull-ups enabled goto _MAIN ORG 0x180 _MAIN ;*** RS232 INITIALIZATION *** ; Do not call RS232init, since we have no RS232 reception and ; PORTB,0 is already used by the keyboard. ; (Note: KEYBOARDinit and RS232init are almost equal) ; Initialize only RS232 transmission (TXport): bcf TXtris ; set RS232 output bcf MRStris ; set Morse port to output IF BEEP_ENABLE == 1 ; conditional assembly bcf BEEPtris ; set Piezo beeper port to output ENDIF BANK0 bsf TXport ; set default state: logical 1 bcf MRSport ; set default state: logical 0 ;*** AT KEYBOARD INITIALIZATION *** KEYBOARDinit ; keyboard initialization clrf KBDcnt ; clear IRQ based scan pattern counter clrf FLAGreg ; clear all flags (keyboard & other) ;*** LCD INITIALIZATION *** LCDinit ; LCD display initialization movlw LCDwidth movwf LCDpos ; init LCD output position counter ;*** ENABLE ALL INTERRUPTS *** movlw b'11111000' andwf INTCON,F ; clear all interrupt flags bsf INTCON,GIE ; enable global interrupt ;*** define amount of table items for startup message *** #define tab_items d'39' movlw tab_items ; store amount of table items in counter movwf TEMP6 ;*** transmit startup message *** _ILOOP movlw HIGH WelcomeTable ; get correct page for PCLATH movwf PCLATH ; prepare right page bits for table read movfw TEMP6 ; get actual count-down value sublw tab_items ; table offset: w = tab_items - TEMP3 call WelcomeTable ; call lookup table movwf TEMP7 ; create backup of fetched item SENDw ; RS232 output movfw TEMP7 LCDw ; LCD output decfsz TEMP6,F ; decrement counter goto _ILOOP SEND CR ; carriage return SEND LF ; line feed SEND LF if BEEP_ENABLE == 1 ; conditional assembly ;*** acoustic introduction *** BEEP 0xFF, 0x02 BEEP 0x90, 0x03 BEEP 0xC0, 0x03 endif ;****************************** _MLOOP btfsc KBDflag ; check scan pattern reception flag call KBDdecode ; if set, call decoding routine btfsc MRSflag ; check for new Morse buffer content call MORSEout ; if set, call Morse output subroutine goto _MLOOP ; endless loop ;****************************** ORG 0x2D8 ; align table within page WelcomeTable addwf PCL,F ; add offset to table base pointer DT "PIC 16F84 AT Keyboard Decoder connecte" ; create table WTableEND DT "d" IF (high (WelcomeTable) != high (WTableEND)) ERROR "WelcomeTable hits page boundary!" ENDIF ORG 0x307 ; align table within page MORSEtable addwf PCL,1 retlw 0xDB ; 0, Hi-Byte retlw 0x6C ; Lo-Byte retlw 0xB6 ; 1 retlw 0xD8 retlw 0xAD ; 2 retlw 0xB0 retlw 0xAB ; 3 retlw 0x60 retlw 0xAA ; 4 retlw 0xC0 retlw 0xAA ; 5 retlw 0x80 retlw 0xD5 ; 6 retlw 0x40 retlw 0xDA ; 7 retlw 0xA0 retlw 0xDB ; 8 retlw 0x50 retlw 0xDB ; 9 retlw 0x68 retlw 0xB0 ; A retlw 0x0 retlw 0xD5 ; B retlw 0x0 retlw 0xD6 ; C retlw 0x80 retlw 0xD4 ; D retlw 0x0 retlw 0x80 ; E retlw 0x0 retlw 0xAD ; F retlw 0x0 retlw 0xDA ; G retlw 0x0 retlw 0xAA ; H retlw 0x0 retlw 0xA0 ; I retlw 0x0 retlw 0xB6 ; J retlw 0xC0 retlw 0xD6 ; K retlw 0x0 retlw 0xB5 ; L retlw 0x0 retlw 0xD8 ; M retlw 0x0 retlw 0xD0 ; N retlw 0x0 retlw 0xDB ; O retlw 0x0 retlw 0xB6 ; P retlw 0x80 retlw 0xDA ; Q retlw 0xC0 retlw 0xB4 ; R retlw 0x0 retlw 0xA8 ; S retlw 0x0 retlw 0xC0 ; T retlw 0x0 retlw 0xAC ; U retlw 0x0 retlw 0xAB ; V retlw 0x0 retlw 0xB6 ; W retlw 0x0 retlw 0xD5 ; X retlw 0x80 retlw 0xD6 ; Y retlw 0xC0 retlw 0xDA ; Z retlw 0x80 retlw 0xB5 ; " retlw 0x68 retlw 0xB6 ; ' retlw 0xDA retlw 0xD6 ; () retlw 0xD6 retlw 0xDA ; , retlw 0xB6 retlw 0xD5 ; - retlw 0x58 retlw 0xB5 ; . retlw 0xAC retlw 0xDB ; : retlw 0x54 retlw 0xAD ; ? MORSEtableEND retlw 0xA8 IF (high (MORSEtable) != high (MORSEtableEND)) ERROR "Morse table hits page boundary!" ENDIF END