;read PS2 keyboard using pic12f675
#include p12f675.inc ;12f675 has analogue
__CONFIG 0x0184
errorlevel -302 ;prevent bank select warnings
#define kbd_clock GPIO,0 ;pic pin 7 clock to PC keyboard
#define kbd_data GPIO,1 ;pic pin 6 data to/from PC keyboard
#define serialin GPIO,2 ;pic pin 5 serial input
#define irout GPIO,4 ;pic pin 3 infra red output
#define serialout GPIO,5 ;pic pin 2 serial output
;pictail pinout for PICKIT1
;pin 8pin 14pin
; 1 2 gp5 2 ra5
; 2 3 gp4 3 ra4
; 3 4 gp3 4 ra3 VPP
; 4 5 rc5
; 5 6 rc4
; 6 7 rc3
; 7 7 gp0 13 ra0 ICSPDAT
; 8 6 gp1 12 ra1 ICSPCLK
; 9 5 gp2 11 ra2
;10 10 rc0
;11 9 rc1
;12 8 rc2
;13 1 +5v 1 +5v VDD
;14 8 0v 14 0v VSS
cblock 0x20
timer1 ;shared by delay routines
timer2 ;shared by delay routines
timer3 ;shared by delay routines
irbyte1 ;IR byte in or out
irbyte2 ;IR byte in or out
irbyte3 ;IR byte in or out
irbyte4 ;IR byte in or out
bitcount ;count bits in or out
inchc
outchc
trail ;value of trail bit (happens to count messages)
hextemp
asciil
asciih
parity
endc
org 0 ;reset vector
goto start
org 4 ;interrupt vector
goto start
;initilisation starts here
start
bcf STATUS,RP0 ;bank 0
clrf GPIO ;init GPIO
movlw 0x07 ;set I/O bits
movwf CMCON ;digital I/O
bsf STATUS,RP0 ;bank1
clrf ANSEL ;digital I/O (pic12f675 only)
; bcf OPTION_REG,7 ;pullups enabled
; movlw 0x20 ;bit 5 only
; movwf WPU ;weak pull up register
movlw 0x1f ;set I/O bits, 1=input
movwf TRISIO ;note GP3 pin4 can only be input
bcf STATUS,RP0 ;bank 0
movlw 0x20 ;serial high
movwf GPIO
main
call crlf
movlw 'P'
call chout
movlw 'i'
call chout
movlw 'c'
call chout
movlw '>'
call chout
goto monitor
wait_clk_low
btfsc kbd_clock
goto wait_clk_low
wait_clk_hi
btfsc kbd_clock
goto wait_clk_hi
btfsc kbd_data
goto found_1
movlw '0'
call chout
goto wait_clk_low
found_1
movlw '1'
call chout
goto wait_clk_low
monitor
call chin
call chout
movfw inchc
sublw 'c'
btfsc STATUS,Z
goto cmd_c
movfw inchc
sublw 'C'
btfsc STATUS,Z
goto cmd_c_u
movfw inchc
sublw 'd'
btfsc STATUS,Z
goto cmd_d
movfw inchc
sublw 'D'
btfsc STATUS,Z
goto cmd_d_u
movfw inchc
sublw 'i'
btfsc STATUS,Z
goto cmd_i
movfw inchc
sublw 'I'
btfsc STATUS,Z
goto cmd_i_u
movfw inchc
sublw 'n'
btfsc STATUS,Z
goto cmd_n
movfw inchc
sublw 'N'
btfsc STATUS,Z
goto cmd_n_u
movfw inchc
sublw 'r'
btfsc STATUS,Z
goto start
movlw '?'
call chout
goto main
; command c
cmd_c
call clk_is_out
bcf kbd_clock
goto main
; command C
cmd_c_u
call clk_is_out
bsf kbd_clock
goto main
; command d
cmd_d
call data_is_out
bcf kbd_data
goto main
; command D
cmd_d_u
call data_is_out
bsf kbd_data
goto main
; command i
cmd_i
cmd_i_u
movfw GPIO
call phex
goto main
; command k - wait for keyboard byte
cmd_k
cmd_k_u
call kbd_byte_in ;wait for a keyboard byte to arrive
call phex ;print w as hex
goto main
;send a keyboard byte
kbd_byte_out
movwf outchc ;save send value
bsf kbd_data ;keep high before taking control
call clk_is_out ;take control of bus
call data_is_out
bcf kbd_clock ;clock low for > 60us to quieten kbd
movlw 60 ;20 for about 60uS delay
call del_x
bcf kbd_data ;data line low will start kbd clock
call clk_is_in ;release clock for kbd to supply
btfsc kbd_clock ;wait for kbd to lower clock
goto $-1
movlw 8 ;8 bits to receive
movwf bitcount
movlw 1 ;initialise parity for odd
kbdoutloop
btfss kbd_clock ;wait for kbd to raise clock
goto $-1
btfss outchc,0 ;lsb first
goto kbdout0
bsf kbd_data
incf parity,f ;count 1s for odd parity
goto kbdout1
kbdout0
bcf kbd_data
kbdout1
btfsc kbd_clock ;wait for kbd to lower clock
goto $-1
decf bitcount,f
btfss STATUS,Z ;8 bits done?
goto kbdoutloop
;send parity
btfss kbd_clock ;wait for kbd to raise clock
goto $-1
btfss parity,0 ;lsb first
goto kbdpout0
bsf kbd_data
goto kbdpout1
kbdpout0
bcf kbd_data
kbdpout1
btfsc kbd_clock ;wait for kbd to lower clock
goto $-1
goto data_is_in
;wait for a keyboard byte to arrive
kbd_byte_in
return
;set keyboard clock to be an input
clk_is_in
bsf STATUS,RP0 ;bank1
bsf TRISIO,0
bcf STATUS,RP0 ;bank0
return
;set keyboard clock to be an output
clk_is_out
bsf STATUS,RP0 ;bank1
bcf TRISIO,0
bcf STATUS,RP0 ;bank0
return
;set keyboard data to be an input
data_is_in
bsf STATUS,RP0 ;bank1
bsf TRISIO,1
bcf STATUS,RP0 ;bank0
return
;set keyboard clock to be an output
data_is_out
bsf STATUS,RP0 ;bank1
bcf TRISIO,1
bcf STATUS,RP0 ;bank0
return
; command n - turn off num lock
cmd_n
movlw 0xff ;reset kbd
call kbd_byte_out
goto main
; command N - turn on num lock
cmd_n_u
movlw 0xff ;reset kbd
call kbd_byte_out
goto main
;get a byte from the serial port
chin
movlw 0x08 ;8 bits
movwf bitcount
btfsc serialin ;wait for start bit
goto chin
call delay_1plus ;wait 1.5 bit times for mid bit 0
chin_loop
bsf STATUS,C
btfss serialin
bcf STATUS,C
rrf inchc,f
call del2400
decf bitcount,f
btfss STATUS,Z
goto chin_loop
movfw inchc
return
;send cr, lf to serial port
crlf
movlw 0x0d
call chout
movlw 0x0a
goto chout
;send a byte to the serial port
chout
movwf irbyte1 ;borrow IR memory
movlw d'8' ;8 bits
movwf bitcount
movfw irbyte1
goto s_byte_out
;serial byte to send in w
s_byte_out
movwf outchc
call s_bit_out_0 ;start bit
sbo_loop
rrf outchc,f ;send lsb first
call s_bit_out_c
decf bitcount,f
btfss STATUS,Z
goto sbo_loop
call s_bit_out_1 ;stop bit
movlw d'8' ;exit with bit count set to 8
movwf bitcount
return
;output an RC6 bit according to carry bit
s_bit_out_c
btfsc STATUS,C
goto s_bit_out_1
s_bit_out_0
bcf serialout
goto del2400
s_bit_out_1
bsf serialout
goto del2400
;send pet DVD up command - RC6 mode 0 a=04, d=58, toggle=0
pet_test
movlw 0x04 ;address
movwf irbyte1
movfw trail ;data (trail borrowed for demo)
goto rc6_out
;send rc6 mode 0 message to infra-red
;put first byte in irbyte1, second byte in w reg
rc6_out
movwf irbyte2
movlw d'96' ;6t mark for leader
call pulse_ir_w
movlw d'32' ;2t space for leader
call wait_ir_w
;send start bit of one and three mode bits - put in high nibble
movlw d'4' ;start bit + 3 bits for mode
movwf bitcount
movlw 0x80 ;start bit 1 + 3 mode bits (hi nibble)
call ir_byte_out ;send bit 7, 6, 5, 4
;trail bit toggles between key presses and is double the size
rrf trail,w ;get bit 0 to carry
call ir_trail_out_c
incf trail,f
movfw irbyte1
call ir_byte_out
movfw irbyte2
call ir_byte_out
movlw d'96' ;6t space for signal free time
goto wait_ir_w
;output a byte (or less) to IR in RC6 mode 0 protocol
;number of bits in bitcount (this routine sets 8 on exit)
;byte to send in w
ir_byte_out
movwf inchc
irbo_loop
rlf inchc,f
call ir_bit_out_c
decf bitcount,f
btfss STATUS,Z
goto irbo_loop
movlw d'8' ;exit with bit count set to 8
movwf bitcount
return
;output an RC6 bit according to carry bit
ir_bit_out_c
btfsc STATUS,C
goto ir_bit_out_1
ir_bit_out_0
movlw d'16' ;1t space
call wait_ir_w
movlw d'16' ;1t mark
goto pulse_ir_w
ir_bit_out_1
movlw d'16' ;1t mark
call pulse_ir_w
movlw d'16' ;1t space
goto wait_ir_w
;output an RC6 trail bit according to carry bit
ir_trail_out_c
btfsc STATUS,C
goto ir_trail_out_1
ir_trail_out_0
movlw d'32' ;2t space
call wait_ir_w
movlw d'32' ;2t mark
goto pulse_ir_w
ir_trail_out_1
movlw d'32' ;2t mark
call pulse_ir_w
movlw d'32' ;2t space
goto wait_ir_w
; send w pulses of 38 Khz to IR LED
pulse_ir_w
movwf timer3 ;save the value
movwf timer2
pulse_loop
bsf irout ;IR on
movlw 0x02
movwf timer1
pulse1
decfsz timer1,F ;.2us of 26ms
goto pulse1 ;.2us
nop
nop
nop
bcf irout ;IR off
movlw 0x04
movwf timer1
pulse2
decfsz timer1,F
goto pulse2
decfsz timer2,F
goto pulse_loop ;loop w times
movfw timer3 ;restore the value
return
;wait w times 38 KHz - no output, but same code as above to get same timing
wait_ir_w
movwf timer3 ;save the value
movwf timer2
wait_loop
bcf irout ;IR off
movlw 0x02
movwf timer1
wait1
decfsz timer1,F
goto wait1
nop
nop
nop
nop
bcf irout ;IR off
movlw 0x04
movwf timer1
wait2
decfsz timer1,F
goto wait2
decfsz timer2,F
goto wait_loop ;loop w times
movfw timer3 ;restore the value
return
;delay routine about 2 seconds
delay
movlw D'60'
movwf timer3
del_l3
movlw D'200'
movwf timer2
del_l2
movlw D'200'
movwf timer1
del_l1
decfsz timer1,F
goto del_l1
decfsz timer2,F
goto del_l2
decfsz timer3,F
goto del_l3
return
;delay a bit more than 1 bit time to get past start bit
delay_1plus
movlw D'200' ;146=2400 baud
goto del_x
;delay routine for 2400 baud
del2400
movlw D'136' ;146=2400 baud
del_x
movwf timer1
del24_1
decfsz timer1,F
goto del24_1
return
;convert hex to ascii
hex2asc
movwf hextemp
andlw 0x0F ; get low nibble
call dechex
movwf asciil
swapf hextemp,F
movf hextemp,W
andlw 0x0F ; get high nibble
call dechex
movwf asciih
return
;********************************************************************
dechex
sublw 0x09 ; 9-WREG
btfss STATUS,C ; is nibble Dec?
goto HexC ; no, convert hex
Dec
movf hextemp,W ; convert DEC nibble to ASCII
andlw 0x0F
addlw A'0'
return
HexC
movf hextemp,W ; convert HEX nibble to ASCII
andlw 0x0F
addlw 0x37
return
phex
call hex2asc
movfw asciih
call chout
movfw asciil
goto chout
end