;******************************************************************* ;dbt_kypd_echo_slave for Derbot Hand Controller ;Reads keypad value when pressed and sends interrupt to main AGV. ;Transmits keypad character on I2C (as Slave) when asked (by Master). ;Displays any character sent on I2C from Master. ;TJW 13.7.05 tested & working 21.7.05 ;Initialisation revised July 06 ;******************************************************************* ;Clock is 1MHz approx ; ; Port A Port B Port C ; ------ ------ ------ ;0 lcd bus 2 not used Display RS ;1 lcd bus 3 keypad col 3 Display E ;2 lcd bus 4 keypad col 2 Interrupt op ;3 lcd bus 5 keypad col 1 SCL ;4 lcd bus 6 keypad row 4 SDA ;5 lcd bus 7 keypad row 3 Display R/W ;6 - keypad row 2 lcd bus 0 ;7 - keypad row 1 lcd bus 1 ;Set Configuration Word: crystal oscillator RC, WDT off, ; power-up timer on, code protect off, LV Program off. __CONFIG _RC_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF list p=16F873A #include p16f873A.inc ;Specify RAM delcntr1 equ 20 ;used in delays delcntr2 equ 21 temp equ 22 ;a temp location, to be used only in consecutive instructions lcd_op equ 23 ;holds word to be sent to display pointer equ 24 ;points to Character string Tables kpad_pat equ 25 ;holds pattern of keypad kpad_add equ 26 ;holds kpad address to lu table kpad_char equ 27 ;holds most recent character from keypad I2C_RX_word equ 28 ;holds most recent I2C word recd I2C_TX_word equ 2a ;holds word to be transmitted on I2C display_pntr equ 2b ;holds pointer to next display digit, as this prog controls digit position ;bit 4 indicates line flags equ 2c ;bit 0 holds RS state during busy_check SR ;Specify some port bits ;Port C lcd_RS equ 0 ;0 for instruction, 1 for character lcd_E equ 1 lcd_RW equ 5 ;Port A lcd_busy equ 5 ; org 00 goto main org 04 ;interrupt vector goto Interrupt_SR ; ;Initialise SFRs in Bank 1 main bcf status,rp1 bsf status,rp0 ;select memory bank 1 movlw B'00000110' ;All Port A bits digital movwf adcon1 movlw B'00000000' ;Port A initially all op movwf trisa movlw B'00011000' ;I2C bits of Port C to ip movwf trisc movlw B'11110000' ;Port B initially Row bits ip, column op movwf trisb movlw B'10100100' movwf sspadd ;our address to be 52H (it's shifted by one in sspadd) bsf pie1,sspie ;enable I2C interrupt ;Initialise SFRs in Bank 0 bcf status,rp0 ;select bank 0 movlw B'00110110' ;SSPCON1:MSSP on, I2C Slave, 7 bit address, interrupts off, movwf sspcon ;no clock stretch ; ;initialise display call delay200 ;wait for power supply to settle bcf portc,lcd_RS ;set control lines bcf portc,lcd_RW ;Relies first on automatic initialisation, as described in data sheet ;now send four command words. Busy Flag not used. movlw B'00001111' ;first command word, Function Set, rotated by 2 bits movwf porta bsf portc,lcd_E bcf portc,lcd_E call delay1 ;wait at least 39us ; movlw B'00000011' ;next command, Display ON/OFF movwf porta bsf portc,6 bsf portc,7 bsf portc,lcd_E bcf portc,lcd_E call delay1 ;wait at least 39us ; movlw B'00000000' ;next command, Display Clear movwf porta bcf portc,7 bsf portc,6 bsf portc,lcd_E bcf portc,lcd_E call delay1 call delay1 ;wait at least 1.53ms ; movlw B'00000001' ;next command, Entry Mode Set movwf porta bsf portc,7 bcf portc,6 bsf portc,lcd_E bcf portc,lcd_E call delay1 ;further initialisation, and interrupt enabling clrf portb ;initialise keypad value movlw 0ff ;preset display pointer, so it increments to 0 movwf display_pntr bcf intcon,rbif ;clear pending interrupts bcf pir1,sspif bsf intcon,rbie bsf intcon,peie bsf intcon,gie loop goto loop ;await keypad and I2C interrupts ;*************************************************************** ;This is ISR, caused by keypad or I2C address match. ;Does not context save, as all action is in ISRs. ;*************************************************************** Interrupt_SR btfsc intcon, rbif ;is it keypad interrupt? goto kpad_ISR ;Here if interrupt is I2C, either address match (Ack sent automatically) ;OR further received byte has been detected. ;check whether this byte was address or data bsf status,rp0 btfsc sspstat,d_a goto ISR1 ;go if word was data bcf status,rp0 movf sspbuf,0 ;dummy read of the address byte, to clear flag ;check if read, if so load and send byte bsf status,rp0 btfsc sspstat,r_w goto Send_I2C bcf status,rp0 ;otherwise exit ISR, to await incoming data byte bcf pir1,sspif ;clear interrupt bit, and end ISR retfie ;Here if data byte has been detected, word is already in buffer. ISR1 call dig_pntr_set ;sort display pointer bcf status,rp0 ;read word movf sspbuf,0 movwf I2C_RX_word ;save word movwf lcd_op ;prepare to send word to display bsf portc,lcd_rs call lcd_write ;transfer to lcd is done, end ISR. bcf pir1,sspif ;clear interrupt bit retfie ;here if sending I2C word. Send byte held in kpad_char Send_I2C bcf status,rp0 bcf pir1,sspif movf kpad_char,0 ;move character to sspbuf movwf sspbuf bsf sspcon,ckp ;release clock btfss pir1,sspif ;wait for completion of transfer goto $-1 ;transfer is complete, end ISR. bcf pir1,sspif ;clear interrupt bit retfie ; ;Keypad press has been detected through Port B Interrupt on Change. ;Gets value, converts to character, stores in kpad_char, awaits key release, ;and sends interrupt to AGV kpad_ISR call kpad_rd call kp_code_conv rel_test call kpad_rd ;test now for keypad release movf kpad_pat,0 andlw 0fe ;suppress lsb, which is not used sublw 0fe ;test if inactive btfss status,z goto rel_test bsf portc,2 ;send interrupt to AGV nop bcf portc,2 bcf intcon,rbif ;clear interrupt bit retfie ;********************************************** ;SUBROUTINES ;********************************************** ;Reads keypad, places raw row/column pattern into kpad_pat, and ;resets keypad interface kpad_rd movf portb,w ;read portb value, this will be row pattern andlw B'11110000' ;ensure unwanted bits are suppressed movwf kpad_pat bsf status,rp0 ;set row to op, column to ip movlw B'00001110' movwf trisb bcf status,rp0 movlw 00 movwf portb ;ensure output values still zero movf portb,w ;read portb value, this will be column pattern andlw B'00001110' ;ensure unwanted bits are suppressed iorwf kpad_pat,1 ;OR those results into the pattern ;reset keypad interface bsf status,rp0 ;set row to ip, column to op movlw B'11110000' movwf trisb bcf status,rp0 clrf portb ;ensure output values still zero return ; ;Converts keypad pattern held in kpad_pat to ASCII character, first forming ;address that is used in lu table. Returns with character held in kpad_char kp_code_conv bcf status,c rrf kpad_pat,1 ;discard bit 0 which is not used clrf kpad_add ;deduce row btfsc kpad_pat,6 goto kp1 goto col_find ;here if row 1, kpad_add stays as is kp1 btfsc kpad_pat,5 goto kp2 movlw B'00000100' ;here if row 2 iorwf kpad_add,1 ;form table address goto col_find kp2 btfsc kpad_pat,4 goto kp3 movlw B'00001000' ;here if row 3 iorwf kpad_add,1 ;form table address goto col_find kp3 btfsc kpad_pat,3 goto kp4 movlw B'00001100' ;here if row 3 iorwf kpad_add,1 ;form table address goto col_find kp4 movlw D'16' ;no row detected, return "E" via Table goto keypad_op ;now deduce column col_find btfsc kpad_pat,2 goto cf1 goto keypad_op ;here if column 1, kpad_add stays as is cf1 btfsc kpad_pat,1 goto cf2 movlw B'00000001' ;here if column 2 iorwf kpad_add,1 ;form table address goto keypad_op ;assume now column 3 cf2 movlw B'00000010' iorwf kpad_add,1 ;form table address keypad_op movf kpad_add,0 call kp_table movwf kpad_char ;save the character return ;Table called to convert pattern recd from keypad to actual character. Note that ;ASCII codes will be returned, as each digit is in format 'D'. kp_table addwf pcl,1 retlw '1' retlw '2' retlw '3' retlw 'A' ;Error code retlw '4' retlw '5' retlw '6' retlw 'B' ;Error code retlw '7' retlw '8' retlw '9' retlw 'C' ;Error code retlw '*' retlw '0' retlw '#' retlw 'D' ;Error code retlw 'E' ;Error code ; ;adjusts the digit pointer on the display, so that sent digits don't fall off! dig_pntr_set bcf status,rp0 incf display_pntr,1 ;increment and test pointer movf display_pntr,0 andlw 0f ;test lower 4 bits only sublw 08 btfss status,z goto pntr_on ;no adjustment needed movf display_pntr,0 xorlw 10 ;toggle line bit andlw 10 ;return all other bits to zero movwf display_pntr movlw B'10000000' ;set up new value for digit address command btfsc display_pntr,4 movlw B'10101000' ;second row address command movwf lcd_op bcf portc,lcd_rs call lcd_write pntr_on bcf status,rp0 return ; ;Waits until busy clear, and writes word held in lcd_op to display. ;RS must be preset to required value, this status is preserved. lcd_write call busy_check bcf portc,lcd_rw bcf status,c rrf lcd_op,1 ;form output bits, op word sits across ports a & c bcf portc,6 ;set value of bit 0 of bus btfsc status,c bsf portc,6 bcf status,c rrf lcd_op,1 bcf portc,7 ;set value of bit 1 of bus btfsc status,c bsf portc,7 movf lcd_op,0 movwf porta bsf portc,lcd_E bcf portc,lcd_E return ;Test Busy Flag, and wait till cleared busy_check bsf status,rp0 ;select memory bank 1 movlw B'00111111' ;set port A all ip movwf trisa bcf status,rp0 bcf flags,0 btfsc portc,lcd_RS ;save RS bit in flags, 0 bsf flags,0 bcf portc,lcd_RS ;access instruction register bsf portc,lcd_RW ;set to read busy_loop bcf portc,lcd_E bsf portc,lcd_E btfsc porta,lcd_busy ;test the busy flag, loop if still busy goto busy_loop bcf portc,lcd_E bsf status,rp0 ;select memory bank 1 movlw B'00000000' ;set port A all op, movwf trisa bcf status,rp0 bcf portc,lcd_RS btfsc flags,0 ;reinstate RS bit bsf portc,lcd_RS return ; ;introduces delay of 100us approx delay100u movlw D'6' ;6 cycles called, ; each taking 16us movwf delcntr1 del1001 nop ;4 inst cycles in this loop, ie 16us decfsz delcntr1,1 goto del1001 return delay1 movlw D'62' ;62 cycles called, ; each taking 16us movwf delcntr1 del1 nop ;4 inst cycles in this loop, ie 16us decfsz delcntr1,1 goto del1 return ; ;introduces delay of 5ms approx delay5 movlw D'250' ;250 cycles called movwf delcntr1 del51 nop ;5 inst cycles in this loop, ie 20us nop decfsz delcntr1,1 goto del51 return ; ;introduces delay of 200ms approx, by 200 calls to delay1 delay200 movlw D'200' movwf delcntr2 del2 call delay1 decfsz delcntr2,1 goto del2 return ; end