' Test and control a PC keyboard with a PIC ' for UK layout standard PC keyboard but easily modified for other layout ' F1 turns all keyboard LEDs on, F2 turns them off ' Escape key clears display ' modified from KYBD_1 ' uses serial display on PORTA.4, keyboard clock on B.0 and keyboard data on B.1 Device = 16F84 Xtal 4 ' ----------------------------------- RSOUT_PIN = PORTA.4 RSOUT_MODE = TRUE RSOUT_PACE = 1 ' -------------------------------------------- Constants for control DIM Control AS $ED ' tell keyboard to expect data DIM Echo AS $EE ' request echo from keyboard DIM Code_Sel AS $F0 ' select code set DIM Get_ID AS $F2 ' Keyboard returns $AB, $83 DIM ACK AS $FA ' returned if keyboard receives good data DIM Err AS $FE ' returned if data not good DIM RST AS $FF ' send reset to keyboard DIM Boot_OK AS $AA ' returned after power up DIM Overrun_2 AS $00 ' buffer overrun for set 2 and 3 DIM Overrun_1 AS $FF ' buffer overrun for set 1 DIM Key_Up AS $F0 ' key up break code '--------------------------------------------- Alias INTERRUPT_REG bits SYMBOL RBIF = INTCON.0 ' PortB change flag SYMBOL INTF = INTCON.1 ' B.0 interrupt flag SYMBOL T0IF = INTCON.2 ' Timer 0 overflow flag SYMBOL RBIE = INTCON.3 ' Port B interrupt enable SYMBOL INTE = INTCON.4 ' Port B.0 interrupt enable SYMBOL T0IE = INTCON.5 ' Timer 0 interrupt enable SYMBOL PEIE = INTCON.6 ' Peripheral interrupt enable SYMBOL GIE = INTCON.7 ' All interrupts ON or OFF ' ------------------------------------------ Alias OPTION_REG bits SYMBOL PS0 = OPTION_REG.0 ' Prescaler select bit 0 SYMBOL PS1 = OPTION_REG.1 ' Prescaler select bit 1 SYMBOL PS2 = OPTION_REG.2 ' Prescaler select bit 2 SYMBOL PSA = OPTION_REG.3 ' Prescaler source, 1 = WDT, 0 = xtal/4 SYMBOL T0SE = OPTION_REG.4 ' Timer 0 edge select SYMBOL T0CS = OPTION_REG.5 ' Timer 0 clock source SYMBOL INTEDG = OPTION_REG.6 ' 1 = rising edge of Port B.0 SYMBOL RBPU = OPTION_REG.7 ' 0 = Port B pullup resistors = ON ' ------------------------------------------ SYMBOL Clock PortB.0 SYMBOL Dat PortB.1 ' ------------------------------------------ Variables DIM ASCII AS BYTE ' converted key data DIM TX_Count AS BYTE ' TX bit count DIM TX_Data AS BYTE ' Data byte to send to keyboard DIM Parity AS BYTE ' parity bit, is transferred to TX_Byte DIM RX_Count AS BYTE ' RX bit count DIM RX_Byte AS BYTE ' Received byte (complete) DIM RX_Temp AS BYTE ' Received bits DIM Leds AS BYTE ' keyboard Leds DIM Temp AS BYTE DIM Enter AS BYTE ' enter key counter, just for test DIM Flags AS BYTE DIM RX_OK AS Flags.0 ' 1 = received packet complete DIM Make AS Flags.1 ' used by keys with make and brake code DIM Char_Cnt AS Flags.2 ' used to porcess key up DIM TRUE AS 1 DIM FALSE AS 0 ' ------------------------------------------- ON_INTERRUPT GOTO Key_Int GOTO Start ' ------------------------------------------- Interrupt routines Key_Int: ASM MOVF TX_Count,W ; if TX_Count = 0 send is not needed BTFSC STATUS,Z GOTO RX_Int ; goto receive interrupt ' ----------------------------------------------- send data to keyboard TX0 MOVF TX_Count,W ; send interrupt SUBLW 9 ; is this bit 9 BTFSS STATUS,Z GOTO TX1 ; no - skip MOVF Parity,W ; yes this is parity bit MOVWF TX_Data ; move parity into TX_Data TX1 MOVF TX_Count,W ; is this bit 10 SUBLW 10 BTFSS STATUS,Z GOTO TX2 ; no - skip MOVLW 255 ; set all inputs BSF STATUS,RP0 ; select page 1 MOVWF TRISB ; set B.1 as input BCF STATUS,RP0 ; select page 0 TX2 CLRC RRF TX_Data,F ; rotate next bit into carry BTFSS STATUS,C GOTO TX3 BSF PORTB,1 ; send a high bit COMF Parity,F ; compliment parity bit GOTO TX4 TX3 BCF PORTB,1 ; send a low bit TX4 INCF TX_Count ; next bit MOVF TX_Count,W SUBLW 12 ; is this bit 11 + 1 BTFSC STATUS,Z CLRF TX_Count ; yes so end of send, clear bit counter TX5 GOTO Int_End ; -------------------------------------------- receive interrupt RX_Int RX0 INCF RX_Count ; next bit MOVF RX_Count,W SUBLW 11 ; is this bit 11 BTFSS STATUS,Z GOTO RX1 ; no MOVF RX_Temp,W ; yes so copy RX_Temp to MOVWF RX_Byte ; RX_Byte BSF RX_OK ; and set flag for main loop CLRF RX_Count ; clear used variables CLRF RX_Temp GOTO Int_End ; and exit RX1 MOVF RX_Count,W SUBLW 10 ; is this bit 10 BTFSC STATUS,Z ; ignore parity bit GOTO Int_End ; yes so exit INT RX2 BTFSS PORTB,1 ; no so test B.1 data line GOTO RX3 BSF STATUS,C ; data bit is high RRF RX_Temp,F ; rotate into RX_Temp GOTO Int_End ; and skip next part RX3 BCF STATUS,C ; data bit is low RRF RX_Temp,F ; and rotate into RX_Temp Int_End BCF INTF ; clear B.0 interrupt flag CLRF TMR0 ; reset timer ENDASM CONTEXT RESTORE ; return ' ---------------------------------------------- TX_Start: ' send start bit WHILE GIE = 1 : GIE = 0 : WEND ' turn off interrupts DELAYUS 50 PORTB = 0 TRISB = 0 DELAYUS 100 ' hold data and clock low to signal start of send TX_Count = 1 ' that was start bit or bit 1 Parity = 1 TRISB = %00000001 ' release clock line INTF = 0 GIE = 1 RETURN ' -------------------------------------------- start of main prog Start: TRISB = 3 ' B.0 and B.1 input for PORTB = 0 ' keyboard WHILE GIE = 1 : GIE = 0 : WEND CLEAR PORTA = %10000 ' high A.4, serial LCD data line TRISA = 0 ' all output TMR0 = 0 RBPU = 0 ' pullups ON INTEDG = 0 T0CS = 0 PS0 = 1 ' prescaler bits PS1 = 0 PS2 = 1 PSA = 0 ' --------------------------------------------- DELAYMS 1000 RSOUT CLS,"Press escape to" ' test display RSOUT AT 2,1,"clear display " ' --------------------------------------------- T0IE = 0 INTF = 0 INTE = 1 GIE = 1 ' --------------------------------------------- for test only ' ----------- Send commands to select set 3, not available on all keyboards ' TX_Data = $F0 ' code set select command ' GOSUB TX_Start ' start sending to keyboard ' WHILE TX_Count > 0 : WEND ' wait for end of send ' TX_Data = 3 ' set 3 ' GOSUB TX_Start ' start sending to keyboard ' WHILE TX_Count > 0 : WEND ' wait for end of send ' --------------------------------------------- reset keyboard. ' The keyboard LED status cannot be read ' so we have to reset to a known state each time the PIC is reset TX_Data = $ED ' LED control command GOSUB TX_Start ' send start bit to control keyboard LEDs = 0 ' clear LED variable ready for sending to keyboard ' --------------------------------------------- Main loop M1: IF RX_OK = FALSE THEN GOTO M5 ' receive incomplete or nothing to do RX_OK = FALSE ' clear flag ' RSOUT HEX RX_Byte," " ' show received byte on display IF RX_Byte = $12 THEN GOTO M4 ' left shift is special case ' all of the GOTO M5 in the following code shorten the loop by not processing ' tests that are not needed ' --------------------------------------------- following code used with SET 2 ' to eliminate key release codes IF RX_Byte = Key_Up THEN ' ignore key up Make = 1 GOTO M5 ENDIF IF Make = 1 THEN ' ignore character after key up Make = 0 GOTO M5 ENDIF ' ---------------------- if a byte was sent to keyboard expect $FA to be returned IF RX_Byte = $FA THEN ' packet received by KYBD OK TX_Data = LEDs ' so send LED data GOSUB TX_Start GOTO M5 ENDIF ' ----------------------------------------- now check for function keys M2: IF RX_Byte = $05 THEN ' was received byte 'F1' TX_Data = $ED ' keyboard control command GOSUB TX_Start ' start sending to keyboard LEDs = 7 ' turn all three leds on GOTO M5 ENDIF M3: IF RX_Byte = $06 THEN ' was received byte 'F2' TX_Data = $ED GOSUB TX_Start LEDS = 0 GOTO M5 ENDIF IF RX_Byte = $76 THEN ' 'Esc' key set 2 (default) ' IF RX_Byte = $08 THEN ' 'Esc' key set 3 RSOUT CLS ' clear display if 'ESC' received GOTO M5 ENDIF IF RX_Byte = $58 THEN ' 'caps lock' set 2 ' IF RX_Byte = $14 THEN ' 'Caps Lock' set 3 LEDs = LEDs ^ %00000100 TX_Data = $ED GOSUB TX_Start GOTO M5 ENDIF ' ----------------------------------- enter key down = $E0,5A enter key up = $E0,F0,5A IF RX_Byte = $5A THEN ' test for enter key Enter = Enter + 1 ' count number of times PRINT CLS,"Enter key ",DEC Enter GOTO M5 ENDIF M4: ' -------------------------------------------- shift keys IF RX_Byte = $12 THEN ' $12 = 'left shift' set 2 and set 3 = dec 18 ' $89 = 'right shift' set 2 and set 3 LEDs = LEDs | %00000001 ' indicate on 'scroll lock' LED IF Make = TRUE THEN LEDs =LEDS ^ %00000001 MAKE = FALSE ENDIF TX_Data = $ED GOSUB TX_Start GOTO M5 ENDIF ' ------------------------------- all other tests done so call ASCII conversion GOSUB M7 M5: ' ------------------------------------------- end of main IF T0IF = 0 THEN GOTO M1 IF TX_Count = 0 THEN GOTO M1 RX_Count = 0 RX_Byte = 0 T0IF = 0 GOTO M1 M6: ' -------------------------------------------- M7: ' -------------------------------------------- decode keyboard to ASCII WHILE GIE = 1 : GIE = 0 : WEND ' turn off interrupt while processing PORTB.0 = 0 ' key data TRISB = $00000010 ' low clock to stop KYBD output TMR0 = 0 ' RSOUT HEX RX_byte," " IF RX_Byte = 85 THEN ' UK keyboard + and = Temp = 58 ' add to end of list GOTO M8 ' skip next bit ENDIF Temp = RX_Byte - 21 IF Temp > 57 THEN ASCII = 0 GOTO M9 ENDIF M8: ' ------------------------------------------- IF LEDs = 0 THEN ' caps = off, shift = off ASCII = LREAD SET1 + Temp GOTO M9 ENDIF IF LEDs = 1 THEN ' caps = off, shift = on ASCII = LREAD SET2 + Temp GOTO M9 ENDIF IF LEDs = 4 THEN ' Caps = on, shift = off ASCII = LREAD SET3 + Temp GOTO M9 ENDIF ASCII = LREAD SET4 + Temp ' LEDS = 5, caps = on, shift = on M9: ' ------------------------------------------- IF ASCII > 0 THEN RSOUT ASCII ' IF ASCII > 0 THEN RSOUT ASCII ' RSOUT AT 2,1,DEC LEDs," " TRISB = $00000011 ' return clock pin to input TMR0 = 0 INTF = 0 GIE = 1 RETURN STOP END SET1: LDATA "q1",0,0,0,"zsaw2",0,0,"cxde43",0,0," vftr5",0,0,"nbhgy6",0,0,",mju78",0,0,",kio09",0,0,".?l:p-=",0 SET2: LDATA "Q!",0,0,0,"ZSAW",34,0,0,"CXDE$#",0,0," VFTR%",0,0,"NBHGY^",0,0,",MJU&*",0,0,",KIO)(",0,0,"./L;P_+",0 SET3: LDATA "Q1",0,0,0,"ZSAW2",0,0,"CXDE43",0,0," VFTR5",0,0,"NBHGY6",0,0,",MJU78",0,0,",KIO09",0,0,"./L;P-=",0 SET4: LDATA "q!",0,0,0,"zsaw",34,0,0,"cxde$#",0,0," vftr%",0,0,"nbhgy^",0,0,",mju&*",0,0,",kio)(",0,0,".?l:p_+",0 ' SET1 = normal ' SET2 = shift. DEC 34 is ASCII for " key ' SET3 = caps lock on ' SET4 = Caps lock on, shift key down, is same as SET1 except number keys