;;================================================= ;; PS2controller.asm ;; ;; part of the GPL'ed DIY 744 MCP ;; ;; This is the program that talks 2 the PS/2 mice that are used as cheap ;; rotary encoders. ;; ;; ;; Copyright (c) 2003 by Manuel Bessler ;; ;; The full text of the legal notices is contained in the file called ;; COPYING, included with this distribution. ;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License ;; as published by the Free Software Foundation; either version 2 ;; of the License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;; ;;================================================= ;;================================================= ;; modifications: ;; added PIC-TO-PIC sender ;;================================================= ;;================================================= ;; Credits: ;; The UART Test Program of ;; Lars Petersen, oz1bxm@qsl.net ;; www.qsl.net/oz1bxm/PIC/pic.htm ;;------------------------------------------------- ;; Microchip Application Note TB055 ;; The PS/2 mouse communications routines used ;; in this program are inspired by those from ;; this App Note ;;================================================= LIST P=16F628, R=DEC ; use a PIC16F628, use decimal system as standard #include "p16f628.inc" ; include appropriate processor definitions ; set config bits: __config _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON ERRORLEVEL -302 ; disable 302 assembler warning messages ; define SRAM data areas/variables/file registers CBLOCK 0x20 dataL tmp delaycounter_inner,delaycounter_outer p2pdata,p2pbitcounter PS2DATA, PS2DATASEND,bitcounter,PS2STATUS MOUSEREPORT1,MOUSEREPORT2,MOUSEREPORT3 ENDC ;;------------------------------- ;; program start point ;;------------------------------- ORG 0x000 ; program start point at 0x000 GOTO init ;;------------------------------- ;; interrupt vector ;;------------------------------- ORG 0x0004 ; interrupt vector interrupt: ; global interrupts automatically disabled on entry! RETFIE ;;------------------------------- ;; generic initialization ;;------------------------------- init: CLRF PORTA ; set portA pins low CLRF PORTB ; set portB pins low MOVLW 0x07 MOVWF CMCON ; turn off comparators, all pins digital BCF STATUS,RP1 BSF STATUS,RP0 ; sel bank 1 CLRF TRISA ; set portA direction registers to all input CLRF TRISB ; set portB direction registers to all input BCF STATUS,RP0 ; sel bank 0 ;;------------------------------- ;; application specific initialization ;;------------------------------- BSF PORTB,2 ; set TX=1 BSF STATUS,RP0 ; sel bank 1 MOVLW b'00111100' ; RA6,7 outputs, RA0,1 outputs initially to keep PS/2 wires low(inhibit for mouse) MOVWF TRISA MOVLW b'11111011' ; RB1/RX input, RB2/TX output MOVWF TRISB CLRF MOUSEREPORT1 CLRF MOUSEREPORT2 CLRF MOUSEREPORT3 ;;------------------------------------ ;; RS-232 Serial Port initialization ;;------------------------------------ ;; serial port Parameters: 19200bps, 8N1 ;;------------------------------------ MOVLW 0x0C ; 0x19=9600bps (0x0C=19200bps) MOVWF SPBRG ; set baud rate CLRF TXSTA BSF TXSTA,BRGH ; set BRGH high baud rates BSF TXSTA,TXEN ; enable async send BCF STATUS,RP0 ; sel bank 0 CLRF RCSTA BSF RCSTA,SPEN ; enable async serial port BSF RCSTA,CREN ; enable continuous receive ;;------------------------------- ;; let serial port stabilize ;;------------------------------- ;; clrf dataL ;;settle decfsz dataL,F ;; goto settle CALL delay250ms CALL delay250ms CALL delay250ms CALL delay250ms MOVF RCREG,W MOVF RCREG,W MOVF RCREG,W ; flush receive register ;;------------------------------- ;; main loop ;;------------------------------- CALL send_alive ;send recognition string to host CALL delay250ms CALL send_test1 CALL init_mouse CALL send_test2 main: CALL ps2_recv ; stream mode, receive byte1 MOVWF MOUSEREPORT1 CALL ps2_recv ; stream mode, receive byte2 MOVWF MOUSEREPORT2 CALL ps2_recv ; stream mode, receive byte3 MOVWF MOUSEREPORT3 CALL ps2_inhibit ; need some time to send off data via RS-232, therefore inhibit mouse CALL send_test3 CALL convert2hex_send ; send off debug packet via RS-232 CALL send_test4 CALL ps2_UNinhibit ; enable mouse again ("de-inhibit") GOTO main convert2hex_send: ;first send hex representation of lower nibble of PS2DATA ... SWAPF MOUSEREPORT1,W ; put a copy of PS2DATA with swapped nibbles into W ANDLW b'00001111' ; clip out the unwanted part CALL convert2hex CALL rs232_send ; ... then do the same with the upper nibble MOVF MOUSEREPORT1,W ANDLW b'00001111' ; clip out the unwanted part CALL convert2hex CALL rs232_send SWAPF MOUSEREPORT2,W ; put a copy of PS2DATA with swapped nibbles into W ANDLW b'00001111' ; clip out the unwanted part CALL convert2hex CALL rs232_send ; ... then do the same with the upper nibble MOVF MOUSEREPORT2,W ANDLW b'00001111' ; clip out the unwanted part CALL convert2hex CALL rs232_send SWAPF MOUSEREPORT3,W ; put a copy of PS2DATA with swapped nibbles into W ANDLW b'00001111' ; clip out the unwanted part CALL convert2hex CALL rs232_send ; ... then do the same with the upper nibble MOVF MOUSEREPORT3,W ANDLW b'00001111' ; clip out the unwanted part CALL convert2hex CALL rs232_send RETURN convert2hex: MOVWF tmp SUBLW 9 ; see if less than 10 BTFSC STATUS,C ; carry bit is set if (k-W)>=0 GOTO less_than_10 ; <10: GOTO greater_eq_10 ; >= 10: hex uses letters a-f for 10-16 less_than_10: MOVF tmp,W ; get saved nibble ADDLW '0' ; add ascii value of "0" GOTO convert2hex_end greater_eq_10: MOVF tmp,w ; get saved nibble ADDLW 'a'-10 ; add ascii value of "a" convert2hex_end: RETURN ;;------------------------------- ;; init_mouse routine ;;------------------------------- init_mouse: ; wait for 0xAA, 0x00 then send 0xF4 BSF STATUS,RP0 ; sel bank 1 BSF TRISA,1 ; let DATA go high first by setting to input BSF TRISA,0 ; then CLK by setting to input BCF STATUS,RP0 ; sel bank 0 CALL ps2_recv ; wait for 0xAA XORLW 0xAA BTFSS STATUS,Z GOTO init_mouse ; dummy endless loop, don't know what to do yet CALL ps2_recv ; wait for 0x00 XORLW 0x00 BTFSS STATUS,Z GOTO init_mouse ; dummy endless loop, don't know what to do yet MOVLW 0xF4 CALL ps2_send ; send Mouse Enable for stream mode RETURN wait_clk_hi: BTFSS PORTA,0 ; wait for CLK to be high GOTO wait_clk_hi RETURN wait_clk_lo: BTFSC PORTA,0 ; wait for CLK to be low GOTO wait_clk_lo RETURN parity_upd: ; effectively toggle each call BTFSC PS2DATA,0 GOTO parity_upd_clr parity_upd_set: BSF PS2DATA,0 GOTO parity_upd_end parity_upd_clr: BCF PS2DATA,0 GOTO parity_upd_end parity_upd_end: RETURN ps2_inhibit: BCF PORTA,0 ; CLK pin, clear output latch RA0 BSF STATUS,RP0 ; sel bank 1 BCF TRISA,0 ; set CLK pin to output temporarily to inhibit mouse from sending BCF STATUS,RP0 ; sel bank 0 RETURN ps2_UNinhibit: BSF STATUS,RP0 ; sel bank 1 BSF TRISA,0 ; set CLK pin to input BCF STATUS,RP0 ; sel bank 0 RETURN ;;------------------------------- ;; PS/2 Mouse receive byte ;;------------------------------- ;; code inspired by Microchip Appnote TB055 ;;------------------------------- ps2_recv: ; RA0 = CLK, RA1 = DATA BTFSC PORTA,1 ; wait for start bit (low) GOTO ps2_recv ; data line not low ? then continue waiting clock_start: BTFSC PORTA,0 ; wait for CLK to be low GOTO clock_start MOVLW 8 ; 8 bits in a PS/2 data transmission MOVWF bitcounter ; keep counter of bits in variable bit_recv: CALL wait_clk_hi ; wait for next clock cycle: HI->LOW CALL wait_clk_lo ; cont'd BCF STATUS,C ; clear carry before rotating BTFSC PORTA,1 ; read received data bit from pin BSF STATUS,C ; if Data line was high, set carry for rotate RRF PS2DATA,F ; rotate carry into PS2DATA register DECFSZ bitcounter,F ; see whether we got all eight data bits GOTO bit_recv ; else next bit get_parity: CALL wait_clk_hi CALL wait_clk_lo BSF PS2STATUS,0 ; prepare partity bit in parity register BTFSS PORTA,1 ; read data line for parity bit BCF PS2STATUS,0 check_parity: ; calculate odd parity of PS2DATA and compare to parity bit (PS2STATUS,0) ;; don't yet care for this really... ; From FOLDOC: ; Odd parity means that the parity bit is set so that ; there are an odd number of one bits in the ; word, including the parity bit. get_stopbit: CALL wait_clk_hi CALL wait_clk_lo BTFSS PORTA,1 ; read data line for stop bit NOP ; no stop bit found ?? something's wrong, but for now, don't care MOVF PS2DATA,W ; leave received byte also in W before return RETURN ;;------------------------------- ;; PS/2 Mouse send byte ;;------------------------------- ;; code taken from Microchip Appnote TB055 ;;------------------------------- ps2_send: ; byte in W is sent to mouse device MOVWF PS2DATASEND ; save data to send in register MOVLW 8 ; 8 bits in a byte MOVWF bitcounter ; set up counter for no of data bits to be sent BSF PS2DATA,0 ; prepare parity bit, set it because if we send out 0x00 with odd parity, ; then the only bit set in the transmission will be the parity bit, ; makeing the number of set bits 1, therefore odd. BSF STATUS,RP0 ; sel bank 1 BCF TRISA,0 ; set CLK ... BCF TRISA,1 ; ... and DATA pins as outputs BCF STATUS,RP0 ; sel bank 0 ; now we wiggle the data and clock lines a little in a magic manner to get the mouse's attention ; so it will listen to us BCF PORTA,0 ; pull CLK low CALL delay1ms BCF PORTA,1 ; pull DATA low (this actually leaves it low for the start bit as well!) CALL delay1ms BSF STATUS,RP0 ; sel bank 1 BSF TRISA,0 ; let CLK pin be a input again, thereby letting it be pulled up to high ; by the external pullup BCF STATUS,RP0 ; sel bank 0 ;; should we wait here for CLK to go high ??? it should automatically after setting PORTA,0 as input, ;; thereby letting the pullup care for the high state... ;CALL wait_clk_hi bit_send: CALL wait_clk_lo ; this could be cause a hang... it'll never return if CLK never goes low! RRF PS2DATASEND,F ; shift bit to send out into carry BTFSC STATUS,C ; carry clear ? GOTO bit_send_hi ; no: send a '1' GOTO bit_send_lo ; yes: send a '0' bit_send_lo: BCF PORTA,1 ; send out a '0' on the data line GOTO bit_send_end bit_send_hi: CALL parity_upd ; update parity if bit is set BSF PORTA,1 ; send out a '1' on the data line GOTO bit_send_end bit_send_end: CALL wait_clk_hi ; this could be cause a hang... it'll never return if CLK never goes hi! ; now the mouse will be able to latch the bit on the wire and later lets CLK go low when it is done DECFSZ bitcounter,F GOTO bit_send send_parity: CALL wait_clk_lo BTFSC PS2DATA,0 GOTO send_parity_hi GOTO send_parity_lo send_parity_lo: BCF PORTA,1 ; send out '0' parity bit GOTO send_parity_end send_parity_hi: BSF PORTA,1 ; send out '1' parity bit GOTO send_parity_end send_parity_end: CALL wait_clk_hi send_stopbit: CALL wait_clk_lo BSF PORTA,1 ; stopbit is always high CALL wait_clk_hi receive_ack: ; ACK bit is sent by the MOUSE _TO_ the HOST BSF STATUS,RP0 ; sel bank 1 BSF TRISA,1 ; restore DATA pin as input, needed to wait for ACK bit from mouse! BCF STATUS,RP0 ; sel bank 0 CALL wait_clk_lo BTFSC PORTA,1 ; look for the ACK bit (low) GOTO no_ack ; if data line still high, no ACK... set some flags or something RETURN ; else: everything received by mouse, all is OK no_ack: ; set some flags for failed transmission RETURN ;;------------------------------- ;; RS-232 receive routine ;;------------------------------- ;; received char is left in W ;; loops until a char is actually received ! ;;------------------------------- rs232_recv: BTFSS PIR1,RCIF ; poll for new received data GOTO rs232_recv MOVF RCREG,W ; serially received byte -> W BSF RCSTA,CREN ; make sure receive errors don't block, (re-)enable continuous receive RETURN ;;------------------------------- ;; RS-232 send routine ;;------------------------------- ;; send char in W via RS-232 ;; waits until sending is done ! ;;------------------------------- rs232_send: MOVWF TXREG ; send out data in W via serial port BSF STATUS,RP0 ; sel bank 1 rs232_send_wait: BTFSS TXSTA,TRMT GOTO rs232_send_wait ; wait till byte sent BCF STATUS,RP0 ; sel bank 0 RETURN ;;------------------------------- ;; send a "alive" message to PC ;;------------------------------- ;; sends string "MCP744REPORTING" ;;------------------------------- send_alive: MOVLW 'M' CALL rs232_send MOVLW 'C' CALL rs232_send MOVLW 'P' CALL rs232_send MOVLW '7' CALL rs232_send MOVLW '4' CALL rs232_send MOVLW '4' CALL rs232_send MOVLW 'R' CALL rs232_send MOVLW 'E' CALL rs232_send MOVLW 'P' CALL rs232_send MOVLW 'O' CALL rs232_send MOVLW 'R' CALL rs232_send MOVLW 'T' CALL rs232_send MOVLW 'I' CALL rs232_send MOVLW 'N' CALL rs232_send MOVLW 'G' CALL rs232_send ; send line break CRLF MOVLW 0x0D ; CR CALL rs232_send MOVLW 0x0A ; LF CALL rs232_send RETURN send_test1: MOVLW '1' CALL rs232_send MOVLW 0x0D ; CR CALL rs232_send MOVLW 0x0A ; LF CALL rs232_send RETURN send_test2: MOVLW '2' CALL rs232_send MOVLW 0x0D ; CR CALL rs232_send MOVLW 0x0A ; LF CALL rs232_send RETURN send_test3: MOVLW '<' CALL rs232_send RETURN send_test4: MOVLW '>' CALL rs232_send MOVLW 0x0D ; CR CALL rs232_send MOVLW 0x0A ; LF CALL rs232_send RETURN send_test5: MOVLW '5' CALL rs232_send MOVLW 0x0D ; CR CALL rs232_send MOVLW 0x0A ; LF CALL rs232_send RETURN ;;------------------------------- ;; delay routines ;;------------------------------- delay1ms: MOVLW 1 MOVWF delaycounter_outer ; takes value in W for delay lenght in msec delay1_outer: MOVLW 200 ; 200*5usec = 1msec MOVWF delaycounter_inner delay1_inner: ; inner loop takes 5usec @4MHz NOP NOP DECFSZ delaycounter_inner,F GOTO delay1_inner DECFSZ delaycounter_outer,F GOTO delay1_outer RETURN ;;------------------------------- delay250ms: MOVLW 250 ; 250 * 1msec = 250msec MOVWF delaycounter_outer delay250_outer: MOVLW 200 ; 200*5usec = 1msec MOVWF delaycounter_inner delay250_inner: ; inner loop takes 5usec @4MHz NOP NOP DECFSZ delaycounter_inner,F GOTO delay250_inner DECFSZ delaycounter_outer,F GOTO delay250_outer RETURN ;;================================================= ;; End of program ;;================================================= END