;********************************************************************* ; Filename: master.asm ; Date: 12/04/2000 ; Revision: 0.50 ; ; Author: Matt Bennett, much taken from the Microchip App. note: ; "Implementing Master I2C with the MSSP module on a PICmicro", ; by Richard L. Fischer, of Microchip ; ;********************************************************************* ; There is a set of DIP switches connected to PORTD and a LED ; LED attached to RA4 (pulled up to VCC) The bigh nybble of PORTD ; is sent to the slave (whose address is read from PORTD as well), ; and then read back. ; ; When the high nybble of the I2C recieved byte is equal to the bigh ; nybble of PORTD, it lights the LED. ;********************************************************************* list p=16f877 ; list directive to define processor #include ; processor specific variable definitions __CONFIG (_CP_OFF&_WDT_ON&_BODEN_ON&_PWRTE_ON&_HS_OSC&_WRT_ENABLE_ON&_LVP_OFF&_CPD_OFF) errorlevel -302 #define ramstart_0 0x20 #define ramstart_1 0xA0 #define ramstart_2 0x110 #define ramstart_3 0x1A0 #define unbanked 0x71 ; plus one for ICD #define i2cSizeMask 0x0F #define FOSC D'10000000' ; define FOSC to PICmicro #define I2CClock D'400000' ; define I2C bite rate #define ClockValue (((FOSC/I2CClock)/4) -1) ; #define timeout_value D'5000' ;bits for variable comm_status #define TX_buffer_full 0 #define RX_buffer_full 1 #define timedout 2 #define TX_done 3 #define RX_done 4 #define ack_error 5 ; flag bit ;******* INTERRUPT CONTEXT SAVE/RESTORE VARIABLES cblock unbanked w_temp status_temp pclath_temp i2cState ; I2C state machine variable TX_buffer RX_buffer i2c_target_address comm_status timeout_high ; timeout_low ; endc ;******* GENERAL PURPOSE I2C VARIABLES cblock ramstart_0 temp endc ;---------------------------------------------------------------------- ; ********************* RESET VECTOR LOCATION ******************** ;---------------------------------------------------------------------- org 0x000 ; processor reset vector nop movlw high start ; load upper byte of 'start' label movwf PCLATH ; initialize PCLATH goto start ; go to beginning of program ;---------------------------------------------------------------------- ; ******************* INTERRUPT VECTOR LOCATION ******************* ;---------------------------------------------------------------------- org 0x004 ; interrupt vector location movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register clrf STATUS ; ensure file register bank set to 0 movwf status_temp ; save off contents of STATUS register movf PCLATH,w movwf pclath_temp ; save off current copy of PCLATH clrf PCLATH ; reset PCLATH to page 0 ; TEST FOR COMPLETION OF VALID I2C EVENT bsf STATUS,RP0 ; select SFR bank btfss PIE1,SSPIE ; test is interrupt is enabled goto test_buscoll ; no, so test for Bus Collision Int bcf STATUS,RP0 ; select SFR bank btfss PIR1,SSPIF ; test for SSP H/W flag goto test_buscoll ; no, so test for Bus Collision Int bcf PIR1,SSPIF ; clear SSP H/W flag call service_i2c ; service valid I2C event ; TEST FOR I2C BUS COLLISION EVENT test_buscoll banksel PIE2 ; select SFR bank btfss PIE2,BCLIE ; test if interrupt is enabled goto test_timer1 ; no, so test for Timer1 interrupt bcf STATUS,RP0 ; select SFR bank btfss PIR2,BCLIF ; test if Bus Collision occured goto test_timer1 ; no, so test for Timer1 interrupt bcf PIR2,BCLIF ; clear Bus Collision H/W flag call service_buscoll ; service bus collision error ; TEST FOR TIMER1 ROLLOVER EVENT test_timer1 banksel PIE1 ; select SFR bank btfss PIE1,TMR1IE ; test if interrupt is enabled goto exit_isr ; no, so exit ISR ; put timer1 stuff here banksel PIE1 ; select SFR bank bcf PIE1,TMR1IE ; disable Timer1 interrupt exit_isr clrf STATUS ; ensure file register bank set to 0 movf pclath_temp,w movwf PCLATH ; restore PCLATH movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f ; swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt ;---------------------------------------------------------------------- ; *********************** I2C Service ************************* ;---------------------------------------------------------------------- service_i2c movlw i2cSizeMask andwf i2cState,w ; retrieve current I2C state addlw low (I2CJump + 1) ; calc state machine jump addr into W I2CJump ; address were jump table branch occurs, this addr also used in fill movwf PCL ; index into state machine jump table ; jump to processing for each state = i2cState value for each state goto WrtStart ; write start sequence = 0 goto SendWrtAddr ; write address, R/W=1 = 1 goto WrtAckTest ; test ack,write data = 2 goto WrtStop ; do stop if done = 3 goto ReadStart ; write start sequence = 4 goto SendReadAddr ; write address, R/W=0 = 5 goto ReadAckTest ; test acknowledge after address = 6 goto ReadData ; read more data = 7 goto ReadStop ; generate stop sequence = 8 I2CJumpEnd Fill (return), (I2CJump-I2CJumpEnd) + i2cSizeMask ;---------------------------------------------------------------------- ; ********************* Write data to Slave ********************* ;---------------------------------------------------------------------- ; Generate I2C bus start condition [ I2C STATE -> 0 ] WrtStart incf i2cState,f ; update I2C state variable banksel SSPCON2 ; select SFR bank bsf SSPCON2,SEN ; initiate I2C bus start condition return ; ; Generate I2C address write (R/W=0) [ I2C STATE -> 1 ] SendWrtAddr bcf STATUS,C ; ensure carry bit is clear rlf i2c_target_address,w ; compose 7-bit address incf i2cState,f ; update I2C state variable banksel SSPBUF ; select SFR bank movwf SSPBUF ; initiate I2C bus write condition return ; ; Test acknowledge after address and data write [ I2C STATE -> 2 ] WrtAckTest banksel SSPCON2 ; select SFR bank btfss SSPCON2,ACKSTAT ; test for acknowledge from slave goto WrtData ; go to write data module bsf comm_status,ack_error ; set acknowledge error clrf i2cState ; reset I2C state variable banksel SSPCON2 ; select SFR bank bsf SSPCON2,PEN ; initiate I2C bus stop condition banksel PIE1 bcf PIE1,SSPIE ; disable SSP interrupt return ; ; Generate I2C write data condition WrtData movfw TX_buffer ; ; don't increment if there is more data to send incf i2cState,f ; update I2C state variable banksel SSPBUF ; select SFR bank movwf SSPBUF ; initiate I2C bus write condition return ; ; Generate I2C bus stop condition [ I2C STATE -> 3 ] WrtStop banksel SSPCON2 ; select SFR bank btfss SSPCON2,ACKSTAT ; test for acknowledge from slave goto no_error ; bypass setting error flag bsf comm_status,ack_error ; set acknowledge error clrf i2cState ; reset I2C state variable goto stop no_error incf i2cState,f ; update I2C state variable for read stop banksel SSPCON2 ; select SFR bank bsf SSPCON2,PEN ; initiate I2C bus stop condition bcf comm_status,TX_buffer_full bsf comm_status,TX_done banksel PIE1 bcf PIE1,SSPIE ; disable SSP interrupt return ; ;---------------------------------------------------------------------- ; ********************* Read data from Slave ********************* ;---------------------------------------------------------------------- ; Generate I2C start condition [ I2C STATE -> 4 ] ReadStart incf i2cState,f ; update I2C state variable banksel SSPCON2 ; select SFR bank bsf SSPCON2,SEN ; initiate I2C bus start condition return ; ; Generate I2C address write (R/W=1) [ I2C STATE -> 5 ] SendReadAddr bsf STATUS,C ; ensure cary bit is clear rlf i2c_target_address,w ; compose 7 bit address incf i2cState,f ; update I2C state variable banksel SSPBUF ; select SFR bank movwf SSPBUF ; initiate I2C bus write condition return ; ; Test acknowledge after address write [ I2C STATE -> 6 ] ReadAckTest banksel SSPCON2 ; select SFR bank btfss SSPCON2,ACKSTAT ; test for not acknowledge from slave goto StartReadData ; good ack, go issue bus read bsf comm_status,ack_error ; set ack error flag clrf i2cState ; reset I2C state variable banksel SSPCON2 ; select SFR bank bsf SSPCON2,PEN ; initiate I2C bus stop condition banksel PIE1 bcf PIE1,SSPIE ; disable SSP interrupt return StartReadData bsf SSPCON2,RCEN ; generate receive condition incf i2cState,f ; update I2C state variable return ; Read slave I2C [ I2C STATE -> 7 ] ReadData banksel SSPBUF ; select SFR bank movf SSPBUF,w ; save off byte into W ; Send Not Acknowledge SendReadNack movwf RX_buffer ; save off null character incf i2cState,f ; update I2C state variable banksel SSPCON2 ; select SFR bank bsf SSPCON2,ACKDT ; acknowledge bit state to send (not ack) bsf SSPCON2,ACKEN ; initiate acknowledge sequence return ; Send Acknowledge SendReadAck movwf RX_buffer ; no, save off byte banksel SSPCON2 ; select SFR bank bcf SSPCON2,ACKDT ; acknowledge bit state to send bsf SSPCON2,ACKEN ; initiate acknowledge sequence btfsc SSPCON2,ACKEN ; ack cycle complete? goto $-1 ; no, so loop again bsf SSPCON2,RCEN ; generate receive condition return ; ; Generate I2C stop condition [ I2C STATE -> 8 ] ReadStop banksel SSPCON2 ; select SFR bank bcf PIE1,SSPIE ; disable SSP interrupt bsf SSPCON2,PEN ; initiate I2C bus stop condition clrf i2cState ; reset I2C state variable bsf comm_status,RX_done; set read/write done flag banksel PIE1 bcf PIE1,SSPIE ; disable SSP interrupt return ;---------------------------------------------------------------------- ; ***** INITIALIZE VARIABLES USED IN SERVICE_I2C FUNCTION ****** ;---------------------------------------------------------------------- init_vars clrf i2cState clrf comm_status return ; ;---------------------------------------------------------------------- ; ******************* INITIALIZE MSSP MODULE ******************* ;---------------------------------------------------------------------- init_i2c banksel SSPADD ; select SFR bank movlw ClockValue ; read selected baud rate movwf SSPADD ; initialize I2C baud rate bcf SSPSTAT,6 ; select I2C input levels bcf SSPSTAT,7 ; enable slew rate movlw b'00011000' ; iorwf TRISC,f ; ensure SDA and SCL are inputs bcf STATUS,RP0 ; select SFR bank movlw b'00111000' ; movwf SSPCON ; Master mode, SSP enable return ; return from subroutine ;---------------------------------------------------------------------- ; ******************* INITIALIZE PORTS ************************* ;---------------------------------------------------------------------- init_ports banksel PORTA ; select SFR bank movlw b'00010000' ; initialize PORTA to have bit 4 on (which means ; the LED will be off) movwf PORTA ; initialize PORTS clrf PORTB ; clrf PORTC ; movlw 0xFF movwf PORTD ; make port D all inputs (to read dip switches) bsf STATUS,RP0 ; select SFR bank movlw b'00000110' ; movwf ADCON1 ; make PORTA digital clrf TRISB ; movlw b'000000' ; movwf TRISA ; movlw b'00011000' ; tristate SCL and SDA movwf TRISC ; return ;---------------------------------------------------------------------- ; ******************* MAIN CODE START LOCATION ****************** ;---------------------------------------------------------------------- start pagesel init_ports call init_ports ; initialize Ports call init_i2c ; initialize I2C module call init_vars ; initialize variables banksel PIE2 ; select SFR bank bsf PIE2,BCLIE ; enable interrupt bsf INTCON,PEIE ; enable peripheral interrupt bsf INTCON,GIE ; enable global interrupt goto main ;******************************************************************* ; MAIN LOOP BEGINS HERE ;******************************************************************* org 0x100 main banksel PORTD clrf comm_status movfw PORTD andlw 0x07 movwf i2c_target_address movfw PORTD andlw 0xF0 call TX_i2c call reset_timeout TX_timeout_loop call check_timeout btfsc comm_status,TX_buffer_full goto TX_timeout_loop btfsc comm_status,timedout goto main call prepare_RX_i2c call reset_timeout RX_timeout_loop call check_timeout btfss comm_status,RX_buffer_full goto RX_timeout_loop banksel PORTD movfw RX_buffer xorwf PORTD,w andlw 0xF0 bsf PORTA,0x04 skpnz bcf PORTA,0x04 btfsc comm_status,ack_error ; test for ack error event flag call service_ackerror ; service ack error goto main ; goto main loop ;******************************************************************* TX_i2c ;******************************************************************* movwf TX_buffer clrf i2cState bsf comm_status,TX_buffer_full pagesel service_i2c call service_i2c banksel PIE1 bsf PIE1,SSPIE return ;******************************************************************* reset_timeout ;******************************************************************* movlw HIGH timeout_value movwf timeout_high movlw LOW timeout_value movwf timeout_low bcf comm_status,timedout return ;******************************************************************* check_timeout ;******************************************************************* clrwdt movlw 0x01 subwf timeout_low,f skpnc ; skip if carry (subtract inverts) return subwf timeout_high,f skpnc ; skip if carry return bsf comm_status,RX_buffer_full bcf comm_status,TX_buffer_full bsf comm_status,timedout return ;******************************************************************* prepare_RX_i2c ;******************************************************************* movlw 0x04 movwf i2cState bcf comm_status,RX_buffer_full pagesel service_i2c call service_i2c banksel PIE1 bsf PIE1,SSPIE return ;---------------------------------------------------------------------- ; *************** Bus Collision Service Routine ****************** ;---------------------------------------------------------------------- service_buscoll pagesel init_vars call init_vars ; re-initialize variables return ; ;---------------------------------------------------------------------- ; ************* Acknowledge Error Service Routine *************** ;---------------------------------------------------------------------- service_ackerror banksel comm_status ; select SFR bank bcf comm_status,ack_error ; reset acknowledge error event flag pagesel init_vars call init_vars ; re-initialize variables return ; END ; That's all folks