Subversion Repositories group.electronics

Rev

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
;---------------------------------------------------------------------
;Constant Definitions
;---------------------------------------------------------------------
#define NODE_ADDR 0x4d ; I2C address of slave 2

    #define SR_DATA    PORTA,0
    #define SR_CLOCK   PORTA,1
    #define SR_LOAD    PORTA,2

    #define LED      PORTA,4
; 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           ;
    in_loop    res 1           ; Loop for the shift registe
    in_byte    res 1
    i2c_recv    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
    bsf     SR_LOAD
    bcf     SR_CLOCK

Main clrwdt ; Clear the watchdog timer.
    banksel PORTA
    call    shift_in
    movfw   in_byte
    movwf   RXBuffer

    goto    Main ; Loop forever.

shift_in
    clrf    in_byte

    movlw   .8          ; Do this 8 times (Once per bit)
    movwf   in_loop
    
    bcf     SR_LOAD         ; Strobe to have SR load values
    nop
    bsf     SR_LOAD

bit_in
                            ; Values are oppsite due to PULL-UP resistors
    btfss   SR_DATA         ; If DATA is low
    bsf     STATUS,C        ;   -> Set high
    btfsc   SR_DATA
    bcf     STATUS,C
    rlf     in_byte,f       ; Right rotate (we recieve lowest bit first)

    bsf     SR_CLOCK    ; Strobe a clock to move to next bit
    nop
    bcf     SR_CLOCK

    decfsz  in_loop,f
    goto    bit_in

    return

;---------------------------------------------------------------------
; 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
    bsf     TRISA,0     ; PortA,2: SR_Load is an input
    bcf     LED

    banksel i2c_recv
    clrf    i2c_recv

    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.
    bsf     i2c_recv,0  ; Data received on i2c
    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