Blame | Last modification | View Log | RSS feed
;---------------------------------------------------------------------; File: an734_PIC16.asm;; Written By: Stephen Bowling, Microchip Technology;; Version: 1.00;; Assembled using Microchip Assembler;; Functionality:;; This code implements the basic functions for an I2C slave device; using the SSP module. All I2C functions are handled in an ISR.; Bytes written to the slave are stored in a buffer. After a number; of bytes have been written, the master device can then read the; bytes back from the buffer.;; Variables and Constants used in the program:;; The start address for the receive buffer is stored in the variable; 'RXBuffer'. The length of the buffer is denoted by the constant; value 'RX_BUF_LEN'. The current buffer index is stored in the; variable 'Index'.;;--------------------------------------------------------------------;; The following files should be included in the MPLAB project:;; an734_PIC16.asm-- Main source code file;; 16f877a.lkr-- Linker script file; (change this file for the device you are using);;---------------------------------------------------------------------;---------------------------------------------------------------------; Include Files;---------------------------------------------------------------------list p=16F88#include <p16f88.inc> ; Change to device that you are using.__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _CCPMX_RB3 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_ON & _HS_OSCERRORLEVEL -302;---------------------------------------------------------------------;Constant Definitions;---------------------------------------------------------------------#define NODE_ADDR 0x4e ; I2C address of slave 2#define LED0 PORTA,0#define LED1 PORTA,1#define LED2 PORTA,2#define LED3 PORTA,3#define LED4 PORTA,4#define LED5 PORTB,2#define LED6 PORTB,0#define LED7 PORTB,5; Change this value to address that; you wish to use.;---------------------------------------------------------------------; Buffer Length Definition;---------------------------------------------------------------------#define RX_BUF_LEN 8 ; Length of receive buffer;---------------------------------------------------------------------; Variable declarations;---------------------------------------------------------------------udata_shrWREGsave res 1RXBuffer res RX_BUF_LENudataSTATUSsave res 1FSRsave res 1PCLATHsave res 1Index res 1 ; Index to receive bufferTemp res 1 ;; device.;---------------------------------------------------------------------; Vectors;---------------------------------------------------------------------STARTUP code 0x00nopgoto Startup ;nop ; 0x0002nop ; 0x0003goto ISR ; 0x0004PROG code;---------------------------------------------------------------------; Macros;---------------------------------------------------------------------memset macro Buf_addr,Value,Lengthmovlw Length ; This macro loads a range of data memorymovwf Temp ; with a specified value. The startingmovlw Buf_addr ; address and number of bytes are alsomovwf FSR ; specified.SetNext movlw Valuemovwf INDFincf FSR,Fdecfsz Temp,Fgoto SetNextendmLFSR macro Address,Offset ; This macro loads the correct valuemovlw Address ; into the FSR given an initial datamovwf FSR ; memory address and offset value.movf Offset,Waddwf FSR,Fendm;---------------------------------------------------------------------; Main Code;---------------------------------------------------------------------Startupbcf STATUS,RP1bsf STATUS,RP0call Setupbanksel WREGsavebanksel RXBufferclrf RXBufferbcf LED0bcf LED1bcf LED2bcf LED3bcf LED4bcf LED5bcf LED6bcf LED7Main clrwdt ; Clear the watchdog timer.banksel PORTBbtfsc RXBuffer,0bsf LED0btfss RXBuffer,0bcf LED0btfsc RXBuffer,1bsf LED1btfss RXBuffer,1bcf LED1btfsc RXBuffer,2bsf LED2btfss RXBuffer,2bcf LED2btfsc RXBuffer,3bsf LED3btfss RXBuffer,3bcf LED3btfsc RXBuffer,4bsf LED4btfss RXBuffer,4bcf LED4btfsc RXBuffer,5bsf LED5btfss RXBuffer,5bcf LED5btfsc RXBuffer,6bsf LED6btfss RXBuffer,6bcf LED6btfsc RXBuffer,7bsf LED7btfss RXBuffer,7bcf LED7goto Main ; Loop forever.;---------------------------------------------------------------------; Interrupt Code;---------------------------------------------------------------------ISRmovwf WREGsave ; Save WREGmovf STATUS,W ; Get STATUS registerbanksel STATUSsave ; Switch banks, if needed.movwf STATUSsave ; Save the STATUS registermovf PCLATH,W ;movwf PCLATHsave ; Save PCLATHmovf FSR,W ;movwf FSRsave ; Save FSRbanksel PIR1btfss PIR1,SSPIF ; Is this a SSP interrupt?goto $ ; No, just trap here.bcf PIR1,SSPIFcall SSP_Handler ; Yes, service SSP interrupt.banksel FSRsavemovf FSRsave,W ;movwf FSR ; Restore FSRmovf PCLATHsave,W;movwf PCLATH ; Restore PCLATHmovf STATUSsave,W;movwf STATUS ; Restore STATUSswapf WREGsave,F ;swapf WREGsave,W ; Restore WREGretfie ; Return from interrupt.;---------------------------------------------------------------------Setup;; Initializes program variables and peripheral registers.;---------------------------------------------------------------------banksel PCONbsf PCON,NOT_PORbsf PCON,NOT_BORbanksel ANSELmovlw 0x00movwf ANSELbanksel Index ; Clear various program variablesclrf Indexclrf PORTBclrf PIR1banksel TRISBclrf TRISBbsf TRISB,4bsf TRISB,1banksel TRISAclrf TRISAmovlw 0x36 ; Setup SSP module for 7-bitbanksel SSPCONmovwf SSPCON ; address, slave modemovlw NODE_ADDRbanksel SSPADDmovwf SSPADDclrf SSPSTATbanksel PIE1 ; Enable interruptsbsf PIE1,SSPIEbsf INTCON,PEIE ; Enable all peripheral interruptsbsf INTCON,GIE ; Enable global interruptsbcf STATUS,RP0return;---------------------------------------------------------------------SSP_Handler;---------------------------------------------------------------------; The I2C code below checks for 5 states:;---------------------------------------------------------------------; State 1: I2C write operation, last byte was an address byte.; SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1;; State 2: I2C write operation, last byte was a data byte.; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1;; State 3: I2C read operation, last byte was an address byte.; SSPSTAT bits: S = 1, D_A = 0, R_W = 1 (see Appendix C for more information);; State 4: I2C read operation, last byte was a data byte.; SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0;; State 5: Slave I2C logic reset by NACK from master.; SSPSTAT bits: S = 1, D_A = 1, BF = 0 (see Appendix C for more information);; For convenience, WriteI2C and ReadI2C functions have been used.;----------------------------------------------------------------------banksel SSPSTATmovf SSPSTAT,W ; Get the value of SSPSTATandlw b'00101101' ; Mask out unimportant bits in SSPSTAT.banksel Temp ; Put masked value in Tempmovwf Temp ; for comparision checking.State1: ; Write operation, last byte was anmovlw b'00001001' ; address, buffer is full.xorwf Temp,W ;btfss STATUS,Z ; Are we in State1?goto State2 ; No, check for next state.....memset RXBuffer,0,RX_BUF_LEN ; Clear the receive buffer.clrf Index ; Clear the buffer index.banksel SSPBUF ; Do a dummy read of the SSPBUF.movf SSPBUF,WreturnState2: ; Write operation, last byte was data,movlw b'00101001' ; buffer is full.xorwf Temp,Wbtfss STATUS,Z ; Are we in State2?goto State3 ; No, check for next state.....LFSR RXBuffer,Index ; Point to the buffer.banksel SSPBUF ; Get the byte from the SSP.movf SSPBUF,Wmovwf INDF ; Put it in the buffer.incf Index,F ; Increment the buffer pointer.movf Index,W ; Get the current buffer index.sublw RX_BUF_LEN ; Subtract the buffer length.btfsc STATUS,Z ; Has the index exceeded the buffer length?clrf Index ; Yes, clear the buffer index.returnState3: ; Read operation, last byte was an address,movf Temp,W ;andlw b'00101100' ; Mask BF bit in SSPSTATxorlw b'00001100'btfss STATUS,Z ; Are we in State3?goto State4 ; No, check for next state.....clrf Index ; Clear the buffer index.LFSR RXBuffer,Index ; Point to the buffermovf INDF,W ; Get the byte from buffer.call WriteI2C ; Write the byte to SSPBUFincf Index,F ; Increment the buffer index.returnState4: ; Read operation, last byte was data,banksel SSPCON ; buffer is empty.btfsc SSPCON, CKPgoto State5movlw b'00101100'xorwf Temp,Wbtfss STATUS,Z ; Are we in State4?goto State5 ; No, check for next state....movf Index,W ; Get the current buffer index.sublw RX_BUF_LEN ; Subtract the buffer length.btfsc STATUS,Z ; Has the index exceeded the buffer length?clrf Index ; Yes, clear the buffer index.LFSR RXBuffer,Index ; Point to the buffermovf INDF,W ; Get the bytecall WriteI2C ; Write to SSPBUFincf Index,F ; Increment the buffer index.returnState5:movf Temp,W ; NACK received when sending data to the masterandlw b'00101000' ; Mask RW bit in SSPSTATxorlw b'00101000' ;btfss STATUS,Z ;goto I2CErr ;return ; If we aren?t in State5, then something is; wrong.I2CErr nopbanksel PORTB ; Something went wrong! Set LEDbsf PORTB,2 ; and loop forever. WDT will resetgoto $ ; device, if enabled.return;---------------------------------------------------------------------; WriteI2C;---------------------------------------------------------------------WriteI2Cbanksel SSPSTATbtfsc SSPSTAT,BF ; Is the buffer full?goto WriteI2C ; Yes, keep waiting.banksel SSPCON ; No, continue.DoI2CWritebcf SSPCON,WCOL ; Clear the WCOL flag.movwf SSPBUF ; Write the byte in WREGbtfsc SSPCON,WCOL ; Was there a write collision?goto DoI2CWritebsf SSPCON,CKP ; Release the clock.returnend