There is an large amount of data in the public domain and on the internet describing the use of PC keyboards with microprocessors so I will not duplicate the information here, if you need more information try to search for “pc keyboard data” then refine the search with “pic”

Examples of keyboard Wiring
Do not accept everything you read without question as I found some of the information incorrect or out of date and some of the available programmes do not appear to work.
There are several programmes available written in assembler and (one in STAMP basic which shows how to use a PIC to send information to a PC keyboard port)
I have used some of the available information to help develop this project in PROTON+ for the user group. I wanted as much of it as possible in basic so that it can be modified easily by PROTON users for other PICs.
One of the first articles I read described the keyboards as having three internal code sets. Set 1 for XT compatibility, set 2 for AT and PS2 compatibility and set 3 which does not produce the “key release code” and was probably used on computer terminals. Code set 3 would have been ideal for our purpose as it reduces the amount of coding required but of the seven keyboards I tested ony one with the 5 pin DIN plug and one with a PS2 plug supported set 3. All of them of course had set 2.
Some of the available programmes use critical timing to check the keyboard but as the keyboard generates a clock signal it is better to use this and detect the clock pulse using the PIC interrupt, in this way the PICs own clock frequency is not critical.
Just about everything is described in the programme or can be found from the two links at the top of this page. The LDATA sets are for the UK keyboard layout but can easily be modified. F1 turns on all three keyboard LEDs and F2 turns them off. I have used the caps lock LED for its intended purpose and the scroll lock LED to indicate the left shift key. In the F877 version I have left in most of the BASIC lines used for the original tests, including the interrupt handler which I then converted to assembler to remove the page swapping code generated by the PROTON compiler. I have also left in the display lines I used to check the key codes produced by the keyboard, this might help if you need the other keys which I have not included.
The 16F877 version uses a parallel LCD display and works at 4MHz and 20MHz without modification. The 16F84 version uses the RSOUT command to drive the Crownhill serial display as does the 12F629 version. If the LCD display does not initialise with the 12F629 on its internal oscillator then use an external crystal or resonator, preferably at 20MHz.
The programme does not test for error codes from the keyboard and more protection is needed for timeout errors, but as it is now it can be used as a starting place for interfacing a PC keyboard to your own project.
For the 16F877 Parallel LCD
' 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
' uses parallel display on PORTD, keyboard clock on B.0 and keyboard data on B.1
Device = 16F877
XTAL 20
' ----------------------------------- LCD used for tests
Declare LCD_TYPE 0
Declare LCD_INTERFACE 4
Declare LCD_LINES 2
Declare LCD_DTPIN PORTD.4
Declare LCD_RSPIN PORTD.3
Declare LCD_ENPIN PORTD.2
' -----------------------------------
' -------------------------------------------- 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 Flags As Byte
Dim Enter As Byte ' count enter press
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:
' IF TX_Count = 0 THEN GOTO RX_Int ' goto receive interrupt
'TX0: IF TX_Count = 9 THEN TX_Data = Parity ' send Parity
'TX1: IF TX_Count = 10 THEN
' TRISB = %00000011 ' B.0 to input
' GOTO TX4
' ENDIF
' PortB.1 = 1 ' send a high bit
' @COMF PARITY,F ' compliment parity bit
' GOTO TX4
'TX3: PortB.1 = 0 ' send a low bit
'TX4: INC TX_Count ' next bit
' IF TX_Count = 12 THEN TX_Count = 0 ' done with TX
'TX5: GOTO Int_End
' -----------------------------------------------
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
ENDASM
' -------------------------------------------- receive interrupt
RX_Int:
' INC RX_Count ' next receive bit
' IF RX_Count = 11 THEN ' last bit
' RX_Byte = RX_Temp ' move temp to RX_Byte
' RX_OK = 1 ' signal byte received OK
' RX_Count = 0 ' reset bit counter
' GOTO Int_End ' exit
' ENDIF
'RX1: IF RX_Count = 10 THEN GOTO Int_End
'RX2: IF PortB.1 = 1 THEN
' @BSF STATUS,C ' move a high bit into temp
' @RRF RX_Temp,F
' GOTO Int_End
' ENDIF
'RX3: @BCF STATUS,C ' move a low bit into temp
' @RRF RX_Temp,F
'Int_End:INTF = 0 ' clear B.0 interrupt flag
' TMR0 = 0 ' reset timer
' CONTEXT RESTORE ' return
' ------------------------------------------------
ASM
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 ' might need this at 20MHz
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
PORTB = 0 ' keyboard
While GIE = 1 : GIE = 0 : Wend
ALL_DIGITAL = TRUE
Clear
PORTD = 0 ' test LEDs
TRISA = 0 ' all output
TRISD = 0 ' all output
TMR0 = 0
RBPU = 0 ' pullups ON
INTEDG = 0
T0CS = 0
PS0 = 1 ' prescaler bits
PS1 = 0
PS2 = 1
PSA = 0
' ---------------------------------------------
DelayMS 100
Print Cls,"Press escape to" ' test display
Print 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.' This was used during bootloader programming. 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
' PRINT 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
Print 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 presses
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
' Print 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 Print ASCII
' PRINT 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
-=",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
_+",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
[Get Code]
For the 16F84 RSOUT
' 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
-=",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
_+",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
[Get Code]
For the 12F629 Serial LCD
' 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_2. NOTE change of clock and data pins
' uses serial display on GPIO.0, keyboard data on GPIO.1 and keyboard clock on GPIO.2
' If display does not come on, first change RSOUT_PACE to 250 and try again
' if there is still a problem set RSOUT_PACE to 5, change INTRC_OSC_NOCLKOUT to
' XT_OSC and use a 4MHz or 20 MHz crystal or resonator on pins 2 and 3
Device = 12F629
'XTAL 4
' watch dog timer = on, Internal oscillator = on, power up timer = on
' brownout detect enable = off, master clear = off, code protect = on
Config WDT_Off,INTRC_OSC_NOCLKOUT, PWRTE_On,Boden_off,MCLRE_off,CP_ON
' -----------------------------------
RSOUT_PIN = GPIO.0
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 GPIF = INTCON.0 ' Port change flag
Symbol INTF = INTCON.1 ' GPIO.2 interrupt flag
Symbol T0IF = INTCON.2 ' Timer 0 overflow flag
Symbol GPIE = INTCON.3 ' Port interrupt enable
Symbol INTE = INTCON.4 ' GPIO.2 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 NOT_GPPU = OPTION_REG.7 ' 0 = Port pullup resistors = ON
' ------------------------------------------
'SYMBOL Clock GPIO.2
'SYMBOL Dat GPIO.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 ' count enter keys, for test only
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 ; so 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 62 ; set all inputs except GPIO.0
bsf STATUS,RP0 ; select page 1
movwf TRISIO ; 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 GPIO,1 ; send a high bit
comf Parity,F ; compliment parity bit
GoTo TX4
TX3 bcf GPIO,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 GPIO,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
; ---------------------------------------------- end of interrupt routine
TX_Start: ' send start bit
While GIE = 1 : GIE = 0 : Wend ' turn off interrupts
' DELAYUS 50 ' might need this at 20MHz
GPIO = %000001 ' keep high on GPIO.0
TRISIO = 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
TRISIO = %000100 ' release clock line
INTF = 0
GIE = 1
Return
' -------------------------------------------- start of main prog
Start:
DelayMS 100
CMCON = 7 ' Disable comparator
GPIO = %000001 ' set I/O 4 and 5 high
TRISIO = %001110
While GIE = 1 : GIE = 0 : Wend
Clear
TMR0 = 0
WPU = %000111 ' pullups on GPIO.0 to GPIO.2
NOT_GPPU = 0 ' enable pullups
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
' 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 ' number of times pressed
RSOut 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
GPIO.2 = 0 ' key data
TRISIO = $000010 ' 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," "
TRISIO = %000110 ' 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
-=",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
_+",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
' 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
' uses parallel display on PORTD, keyboard clock on B.0 and keyboard data on B.1
Device = 16F877
XTAL 20
' ----------------------------------- LCD used for tests
Declare LCD_TYPE 0
Declare LCD_INTERFACE 4
Declare LCD_LINES 2
Declare LCD_DTPIN PORTD.4
Declare LCD_RSPIN PORTD.3
Declare LCD_ENPIN PORTD.2
' -----------------------------------
' -------------------------------------------- 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 Flags As Byte
Dim Enter As Byte ' count enter press
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:
' IF TX_Count = 0 THEN GOTO RX_Int ' goto receive interrupt
'TX0: IF TX_Count = 9 THEN TX_Data = Parity ' send Parity
'TX1: IF TX_Count = 10 THEN
' TRISB = %00000011 ' B.0 to input
' GOTO TX4
' ENDIF
' PortB.1 = 1 ' send a high bit
' @COMF PARITY,F ' compliment parity bit
' GOTO TX4
'TX3: PortB.1 = 0 ' send a low bit
'TX4: INC TX_Count ' next bit
' IF TX_Count = 12 THEN TX_Count = 0 ' done with TX
'TX5: GOTO Int_End
' -----------------------------------------------
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
ENDASM
' -------------------------------------------- receive interrupt
RX_Int:
' INC RX_Count ' next receive bit
' IF RX_Count = 11 THEN ' last bit
' RX_Byte = RX_Temp ' move temp to RX_Byte
' RX_OK = 1 ' signal byte received OK
' RX_Count = 0 ' reset bit counter
' GOTO Int_End ' exit
' ENDIF
'RX1: IF RX_Count = 10 THEN GOTO Int_End
'RX2: IF PortB.1 = 1 THEN
' @BSF STATUS,C ' move a high bit into temp
' @RRF RX_Temp,F
' GOTO Int_End
' ENDIF
'RX3: @BCF STATUS,C ' move a low bit into temp
' @RRF RX_Temp,F
'Int_End:INTF = 0 ' clear B.0 interrupt flag
' TMR0 = 0 ' reset timer
' CONTEXT RESTORE ' return
' ------------------------------------------------
ASM
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 ' might need this at 20MHz
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
PORTB = 0 ' keyboard
While GIE = 1 : GIE = 0 : Wend
ALL_DIGITAL = TRUE
Clear
PORTD = 0 ' test LEDs
TRISA = 0 ' all output
TRISD = 0 ' all output
TMR0 = 0
RBPU = 0 ' pullups ON
INTEDG = 0
T0CS = 0
PS0 = 1 ' prescaler bits
PS1 = 0
PS2 = 1
PSA = 0
' ---------------------------------------------
DelayMS 100
Print Cls,"Press escape to" ' test display
Print 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.' This was used during bootloader programming. 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
' PRINT 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
Print 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 presses
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
' Print 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 Print ASCII
' PRINT 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
-=",0SET2: 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
_+",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
[Get Code]
For the 16F84 RSOUT
' 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
-=",0SET2: 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
_+",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
[Get Code]
For the 12F629 Serial LCD
' 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_2. NOTE change of clock and data pins
' uses serial display on GPIO.0, keyboard data on GPIO.1 and keyboard clock on GPIO.2
' If display does not come on, first change RSOUT_PACE to 250 and try again
' if there is still a problem set RSOUT_PACE to 5, change INTRC_OSC_NOCLKOUT to
' XT_OSC and use a 4MHz or 20 MHz crystal or resonator on pins 2 and 3
Device = 12F629
'XTAL 4
' watch dog timer = on, Internal oscillator = on, power up timer = on
' brownout detect enable = off, master clear = off, code protect = on
Config WDT_Off,INTRC_OSC_NOCLKOUT, PWRTE_On,Boden_off,MCLRE_off,CP_ON
' -----------------------------------
RSOUT_PIN = GPIO.0
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 GPIF = INTCON.0 ' Port change flag
Symbol INTF = INTCON.1 ' GPIO.2 interrupt flag
Symbol T0IF = INTCON.2 ' Timer 0 overflow flag
Symbol GPIE = INTCON.3 ' Port interrupt enable
Symbol INTE = INTCON.4 ' GPIO.2 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 NOT_GPPU = OPTION_REG.7 ' 0 = Port pullup resistors = ON
' ------------------------------------------
'SYMBOL Clock GPIO.2
'SYMBOL Dat GPIO.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 ' count enter keys, for test only
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 ; so 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 62 ; set all inputs except GPIO.0
bsf STATUS,RP0 ; select page 1
movwf TRISIO ; 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 GPIO,1 ; send a high bit
comf Parity,F ; compliment parity bit
GoTo TX4
TX3 bcf GPIO,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 GPIO,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
; ---------------------------------------------- end of interrupt routine
TX_Start: ' send start bit
While GIE = 1 : GIE = 0 : Wend ' turn off interrupts
' DELAYUS 50 ' might need this at 20MHz
GPIO = %000001 ' keep high on GPIO.0
TRISIO = 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
TRISIO = %000100 ' release clock line
INTF = 0
GIE = 1
Return
' -------------------------------------------- start of main prog
Start:
DelayMS 100
CMCON = 7 ' Disable comparator
GPIO = %000001 ' set I/O 4 and 5 high
TRISIO = %001110
While GIE = 1 : GIE = 0 : Wend
Clear
TMR0 = 0
WPU = %000111 ' pullups on GPIO.0 to GPIO.2
NOT_GPPU = 0 ' enable pullups
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
' 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 ' number of times pressed
RSOut 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
GPIO.2 = 0 ' key data
TRISIO = $000010 ' 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," "
TRISIO = %000110 ' 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
-=",0SET2: 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
_+",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
I'm currently working on the PIC keyboard simulator and noticed a few lines missing from KEYBD_1.bas. As these are remmed out they do not stop the programme working but they were there to show how the original was done in BASIC so here is the complete section of code with the missing lines.
Key_Int:
If TX_Count = 0 Then GoTo RX_Int ; goto receive interrupt
TX0: If TX_Count = 9 Then TX_Data = Parity ; send Parity
TX1: If TX_Count = 10 Then
TRISB = %00000011 ; B.0 to input
GoTo TX4
EndIf
TX2: ;@RRF TX_Data,F ; rotate next bit into carry
;TX_Data = TX_Data >>1
ASM
clrc
rrf TX_Data,F
btfss STATUS,C
GoTo TX3
ENDASM
PORTB.1 = 1 ; send a high bit
@comf Parity,F ; compliment parity bit
GoTo TX4
TX3: PORTB.1 = 0 ; send a low bit
TX4: Inc TX_Count ; next bit
If TX_Count = 12 Then TX_Count = 0 ; done with TX
TX5: GoTo Int_End
If TX_Count = 0 Then GoTo RX_Int ; goto receive interrupt
TX0: If TX_Count = 9 Then TX_Data = Parity ; send Parity
TX1: If TX_Count = 10 Then
TRISB = %00000011 ; B.0 to input
GoTo TX4
EndIf
TX2: ;@RRF TX_Data,F ; rotate next bit into carry
;TX_Data = TX_Data >>1
ASM
clrc
rrf TX_Data,F
btfss STATUS,C
GoTo TX3
ENDASM
PORTB.1 = 1 ; send a high bit
@comf Parity,F ; compliment parity bit
GoTo TX4
TX3: PORTB.1 = 0 ; send a low bit
TX4: Inc TX_Count ; next bit
If TX_Count = 12 Then TX_Count = 0 ; done with TX
TX5: GoTo Int_End
http://www.computer-engineering.org/
contributed by Trevor Wilson.


Menu
Recent Articles


Using PDS with SPI GLCD based on ST7565R Controller
Graphic LCDs based on the ST7565 are cheaper then GLCDs with other controllers. SPI requires only four pins. If the circuit