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_OSC
ERRORLEVEL -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_shr
WREGsave res 1
RXBuffer res RX_BUF_LEN
udata
STATUSsave res 1
FSRsave res 1
PCLATHsave res 1
Index res 1 ; Index to receive buffer
Temp res 1 ;
; device.
;---------------------------------------------------------------------
; Vectors
;---------------------------------------------------------------------
STARTUP code 0x00
nop
goto Startup ;
nop ; 0x0002
nop ; 0x0003
goto ISR ; 0x0004
PROG code
;---------------------------------------------------------------------
; Macros
;---------------------------------------------------------------------
memset macro Buf_addr,Value,Length
movlw Length ; This macro loads a range of data memory
movwf Temp ; with a specified value. The starting
movlw Buf_addr ; address and number of bytes are also
movwf FSR ; specified.
SetNext movlw Value
movwf INDF
incf FSR,F
decfsz Temp,F
goto SetNext
endm
LFSR macro Address,Offset ; This macro loads the correct value
movlw Address ; into the FSR given an initial data
movwf FSR ; memory address and offset value.
movf Offset,W
addwf FSR,F
endm
;---------------------------------------------------------------------
; Main Code
;---------------------------------------------------------------------
Startup
bcf STATUS,RP1
bsf STATUS,RP0
call Setup
banksel WREGsave
banksel RXBuffer
clrf RXBuffer
bcf LED0
bcf LED1
bcf LED2
bcf LED3
bcf LED4
bcf LED5
bcf LED6
bcf LED7
Main clrwdt ; Clear the watchdog timer.
banksel PORTB
btfsc RXBuffer,0
bsf LED0
btfss RXBuffer,0
bcf LED0
btfsc RXBuffer,1
bsf LED1
btfss RXBuffer,1
bcf LED1
btfsc RXBuffer,2
bsf LED2
btfss RXBuffer,2
bcf LED2
btfsc RXBuffer,3
bsf LED3
btfss RXBuffer,3
bcf LED3
btfsc RXBuffer,4
bsf LED4
btfss RXBuffer,4
bcf LED4
btfsc RXBuffer,5
bsf LED5
btfss RXBuffer,5
bcf LED5
btfsc RXBuffer,6
bsf LED6
btfss RXBuffer,6
bcf LED6
btfsc RXBuffer,7
bsf LED7
btfss RXBuffer,7
bcf LED7
goto Main ; Loop forever.
;---------------------------------------------------------------------
; Interrupt Code
;---------------------------------------------------------------------
ISR
movwf WREGsave ; Save WREG
movf STATUS,W ; Get STATUS register
banksel STATUSsave ; Switch banks, if needed.
movwf STATUSsave ; Save the STATUS register
movf PCLATH,W ;
movwf PCLATHsave ; Save PCLATH
movf FSR,W ;
movwf FSRsave ; Save FSR
banksel PIR1
btfss PIR1,SSPIF ; Is this a SSP interrupt?
goto $ ; No, just trap here.
bcf PIR1,SSPIF
call SSP_Handler ; Yes, service SSP interrupt.
banksel FSRsave
movf FSRsave,W ;
movwf FSR ; Restore FSR
movf PCLATHsave,W;
movwf PCLATH ; Restore PCLATH
movf STATUSsave,W;
movwf STATUS ; Restore STATUS
swapf WREGsave,F ;
swapf WREGsave,W ; Restore WREG
retfie ; Return from interrupt.
;---------------------------------------------------------------------
Setup
;
; Initializes program variables and peripheral registers.
;---------------------------------------------------------------------
banksel PCON
bsf PCON,NOT_POR
bsf PCON,NOT_BOR
banksel ANSEL
movlw 0x00
movwf ANSEL
banksel Index ; Clear various program variables
clrf Index
clrf PORTB
clrf PIR1
banksel TRISB
clrf TRISB
bsf TRISB,4
bsf TRISB,1
banksel TRISA
clrf TRISA
movlw 0x36 ; Setup SSP module for 7-bit
banksel SSPCON
movwf SSPCON ; address, slave mode
movlw NODE_ADDR
banksel SSPADD
movwf SSPADD
clrf SSPSTAT
banksel PIE1 ; Enable interrupts
bsf PIE1,SSPIE
bsf INTCON,PEIE ; Enable all peripheral interrupts
bsf INTCON,GIE ; Enable global interrupts
bcf STATUS,RP0
return
;---------------------------------------------------------------------
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 SSPSTAT
movf SSPSTAT,W ; Get the value of SSPSTAT
andlw b'00101101' ; Mask out unimportant bits in SSPSTAT.
banksel Temp ; Put masked value in Temp
movwf Temp ; for comparision checking.
State1: ; Write operation, last byte was an
movlw 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,W
return
State2: ; Write operation, last byte was data,
movlw b'00101001' ; buffer is full.
xorwf Temp,W
btfss 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,W
movwf 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.
return
State3: ; Read operation, last byte was an address,
movf Temp,W ;
andlw b'00101100' ; Mask BF bit in SSPSTAT
xorlw 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 buffer
movf INDF,W ; Get the byte from buffer.
call WriteI2C ; Write the byte to SSPBUF
incf Index,F ; Increment the buffer index.
return
State4: ; Read operation, last byte was data,
banksel SSPCON ; buffer is empty.
btfsc SSPCON, CKP
goto State5
movlw b'00101100'
xorwf Temp,W
btfss 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 buffer
movf INDF,W ; Get the byte
call WriteI2C ; Write to SSPBUF
incf Index,F ; Increment the buffer index.
return
State5:
movf Temp,W ; NACK received when sending data to the master
andlw b'00101000' ; Mask RW bit in SSPSTAT
xorlw b'00101000' ;
btfss STATUS,Z ;
goto I2CErr ;
return ; If we aren?t in State5, then something is
; wrong.
I2CErr nop
banksel PORTB ; Something went wrong! Set LED
bsf PORTB,2 ; and loop forever. WDT will reset
goto $ ; device, if enabled.
return
;---------------------------------------------------------------------
; WriteI2C
;---------------------------------------------------------------------
WriteI2C
banksel SSPSTAT
btfsc SSPSTAT,BF ; Is the buffer full?
goto WriteI2C ; Yes, keep waiting.
banksel SSPCON ; No, continue.
DoI2CWrite
bcf SSPCON,WCOL ; Clear the WCOL flag.
movwf SSPBUF ; Write the byte in WREG
btfsc SSPCON,WCOL ; Was there a write collision?
goto DoI2CWrite
bsf SSPCON,CKP ; Release the clock.
return
end