Source Code File:"I2C_Slave_1.c"


/* ****************************************************************************
 SourceBoost C Code

 Processor:     PIC16F877A

 Filename:      I2C_Slave_1.c
 Author:        RSABear
 Date:          8 January 2008
 Version:       1.00
 Description:
    
            Uses Hardware UART - RS232
            Uses Hardware MSSP - I2C

            BoostC implementation of AN734A (DS00734A) I2C Slave
            
            Code written in Poll Mode and can be converted to ISR 

******************************************************************************
Hardware:

    LED on RA2 - PIN 2
    4.0MHz Crystal on PINs 13 & 14, 22pF Caps to Vss
    100K Pull-up Resistor from PIN1 to Vdd

    Maxim DS275 Chip
    RX-Out - PIN 26
    TX-In  - PIN 25

    16F887A

    PIN 18 - SCK
    PIN 23 -    SDA

Revision History:

    I2C Slave code
    Supports the XEE_Read & XEE_Write functions in the i2c_test.c & I2C_Driver.h sample files
    Slave Hardware address set to 0x06 (iff_slave)

Modified:

Date:    


****************************************************************************
*/

#include <system.h>
#include <stdlib.h>
#include <string.h>
#include "i2c_driver.h"
#include <icd2.h>

// Set the 16F877A device configuration bits - Page 
// External 4.0Mhz Crystal Oscillator
#pragma DATA _CONFIG, _CPD_OFF & _DEBUG_ON & _PWRTE_ON & _LVP_OFF & _BODEN_ON & _PWRTE_ON & _WDT_OFF & _XT_OSC

// 4.0 Mhz
#pragma CLOCK_FREQ 4000000

#define FOSC 4000000
#define spBAUD 9600
#define fSPBCLK (FOSC)  // UART Baud rate generator clock (high speed)
// #define fSPBCLK (FOSC / 4)   // UART Baud rate generator clock (low speed)
#define SPBRG_VAL ((fSPBCLK / (spBAUD * 16L)) - 1L)

// bit_time FOSC / 4 / BAUDRATE 
#define bit_time 104        // 9600 baud at 4MHz

// Hardware dependant defines RC6 & 7
#define RX_PIN      7
#define TX_PIN      6

#define RC3     3   // SCL
#define RC4     4   // SDA

// **** START OF DEFAULTS USED FOR HARDWARE USART ****
////////////////////////////////////////////////////////////////////////////
// USART hardware implementation template argument values
////////////////////////////////////////////////////////////////////////////
// variables cannot be passed as template arguments. The following constants map to
// the PIC registers and PIC's USART register locations. These constants are
// then used by the templated functions. When moving between PIC families the
// register mapping must be changed to map to the corresponding physical register
//
// PIC16F877A defaults for hardware USART support
// DS30292C-page 95 (TX) & DS30292C-page 104 (RX)

#define TX_PORT     PORTC
#define TX_TRIS     TRISC
#define TX_BIT          TX_PIN
#define RX_PORT     PORTC
#define RX_TRIS     TRISC
#define RX_BIT          RX_PIN
#define e_SPBRG     SPBRG
#define e_RCREG     RCREG
#define e_TXREG     TXREG
#define e_TXSTA     TXSTA
#define e_RCSTA     RCSTA
#define e_TXIF_PIR  PIR1
#define e_RCIF_PIR  PIR1
#define e_TXIF_BIT  TXIF
#define e_RCIF_BIT  RCIF
#define MODE        (USART_reset_wdt | USART_HW)

// **** END OF DEFAULTS USED FOR HARDWARE USART ****

#include "rs232_driver.h"

////////////////////////////////////////////////////////////////////////////
// i2c hardware implementation template arguments
////////////////////////////////////////////////////////////////////////////
#define i2c_ARGS    3, e_MSSP_PORT, e_MSSP_TRIS, 4, e_MSSP_PORT, e_MSSP_TRIS, e_SSPCON1, e_SSPCON2, \
                    e_SSPSTAT, e_SSPBUF, e_SSPIF_BIT, e_SSPIF_PIR,          \
                    e_BCLIF_BIT, e_BCLIF_PIR, 7, e_SSPADD, (i2c_reset_wdt | i2c_SMP |i2c_HW)

// variables cannot be passed as template arguments. The following constants map to
// the PIC registers and PIC's i2c register locations. These constants are
// then used by the templated functions. 
#define e_MSSP_PORT PORTC
#define e_MSSP_TRIS TRISC
#define e_SSPCON1       SSPCON
#define e_SSPCON2       SSPCON2
#define e_SSPSTAT       SSPSTAT
#define e_SSPADD        SSPADD
#define e_SSPBUF        SSPBUF
#define e_SSPIF_PIR PIR1
#define e_BCLIF_PIR PIR2
#define e_SSPIF_BIT SSPIF
#define e_BCLIF_BIT BCLIF

////////////////////////////////////////////////////////////////////////////
// I2C Device constants
////////////////////////////////////////////////////////////////////////////
    
// define Internal I2C slave (Hardware) addresses
#define iff_slave   0x06    // Node address of this 16F887A device

#define ADDR_BUF_LEN 0x05   // Length of the address receive buffer
                                    // 0x0000 = Slave Address
                                    // 0x0001 = Internal HIGH address
                                    // 0x0002 = Internal LOW address
                                    // 0x0003 = rfu
                                    // 0x0004 = rfu
#define ADDR_SLAVE  0x00
#define ADDR_HIGH       0x01
#define ADDR_LOW        0x02

#define RX_BUF_LEN 32       // Length of receive buffer

#define SSPSTAT_BIT_MASK  0b00101101            // Mask for I2C status bits
// bit 0 BF: Buffer Full Status bit
// bit 2 R/W: Read/Write bit Information (I2C mode only)
// bit 3 S: START bit
// bit 5 D/A: Data/Address bit (I2C mode only)

// State 1 - SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1
#define state_1 0b00001001
// State 2 - SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1
#define state_2 0b00101001
// State 3 - SSPSTAT bits: S = 1, D_A = 0, R_W = 1, BF = 0
#define state_3 0b00001100
// State 4 - SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0
#define state_4 0b00101100
// State 5 - SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 0
#define state_5 0b00101000

#define State_1 0x01
#define State_2 0x02
#define State_3 0x03
#define State_4 0x04
#define State_5 0x05

// Declare some logic definitions for later use
#define SET             1
#define CLEAR           0
#define RESET           0
#define TRUE            1
#define FALSE           0
#define ZERO            0
#define ON              1
#define OFF             0

// Create a pointer to reference the ASCII values by
char ascii_buffer[6];
unsigned char *ascii_code;
volatile char i2c_state, i2c_status, rd_Index, wrt_AIndex, wrt_DIndex, i2c_data, rd_Offset, wrt_Offset, i_counter;
volatile unsigned char DATA_BUFFER[RX_BUF_LEN];
volatile unsigned char ADDR_BUFFER[ADDR_BUF_LEN];

// Function to write data to the terminal
void puts_debug(char *source)
{

    while (*source != 0) // wait until tx register is empty 
            putc(*source++);
            putc(0x20);
}

void main()
{

    ascii_code = &ascii_buffer[0];         // Get the address of the string buffer into the pointer

    i_counter = CLEAR;                          // Clear the general purpose counter register
    wrt_DIndex = 0x00;                          // Initialise the Index for data writing
    wrt_AIndex = 0x00;                          // Initialise the Index for address writing
    rd_Offset = 0x00;                               // Initialise the Offset for data reading
    wrt_Offset = 0x00;                          // Initialise the Offset for data writing
    rd_Index = 0x00;                                // Initialise the Address Buffer Index for data reading

    // Configure the PIC16F877 for I2C/RS232 and I/O use
    adcon0 = 0x00;      // Switch the ADC Unit Off
    ccp1con = 0x00; // Switch the comparator unit off
    trisa = 0x00;
    porta = 0x00;

    // User entertainment - show the RESET/REBOOT
    for ( i_counter = 1; i_counter < 20; i_counter++ )  {
        porta.0 = OFF;
        delay_ms(50);
        porta.0 = ON;
        delay_ms(50);
    }
    clear_bit(porta , 0);

    // RS232 Communications
    uart_init(1,SPBRG_VAL);         // set high speed divisor mode and divisor value - 9600
    puts("PIC16F877A I2C Slave");

    // I2C Communications - setup the PIC in I2C Slave mode
    //SCL and SDA as inputs
    set_bit( trisc, RC3 );
    set_bit( trisc, RC4 );

    // Set the Slave address - DS30292C-page 74
    sspadd = (iff_slave << 0x01);   // 0000 110x -> 7 bits address = Bits 7654 321 <7:1>
    sspcon = 0x36;                      // Initialise the I2C control register
    sspcon2 = 0x00;                         // Initialise the I2C control register

    // Bit 7 - Standard speed mode
    sspstat = 0x80;         // 1 = Slew rate control disabled for standard speed mode (100 kHz and 1 MHz)
    //sspstat = 0x00;   // 0 = Slew rate control enabled for high speed mode (400 kHz)

    set_bit( pie1, SSPIE);
    set_bit( intcon, PEIE);
    // Debugging - disable the Global interrupt
    //  set_bit( intcon, GIE);
    clear_bit( intcon, GIE);        // Don't interrupt the processor
    clear_bit( pir1, SSPIF);

    // Clear the data buffer
    for ( i_counter = 0x00; i_counter < RX_BUF_LEN; i_counter++ )   {   // Clear the data buffer
        DATA_BUFFER[i_counter] = CLEAR;
        }
    // Clear the address buffer
    for ( i_counter = 0x00; i_counter < ADDR_BUF_LEN; i_counter++ ) {   // Clear the address buffer
        ADDR_BUFFER[i_counter] = CLEAR;
        }

while(TRUE)
    {

    // Debug testing in Poll Mode
    if ( pir1.SSPIF == TRUE ) {         // Is this a SSP interrupt?
        
        if ( test_bit( sspcon, SSPOV ) == SET ) {   // Test if we have an overflow condition and clear it
            i2c_data = sspbuf;                              // Do a dummy read of the SSPBUF
            clear_bit( sspcon, SSPOV );                 // Clear the overflow flag
            }

        else {

            porta.0 = 1;                                        // Debug Code - Provide indication that data was received

            i2c_status = 0x00;                              // Clear the variable used to determine the I2C status
            
            // Mask the status bits out from the other unimportant register bits
            i2c_status = ( sspstat & SSPSTAT_BIT_MASK );
            
            if ( (i2c_status ^ state_1 ) == 0 )
                i2c_state = 0x01;           // State 1: Master Write, Last Byte was an Address
            else if ( (i2c_status ^ state_2 ) == 0 )
                i2c_state = 0x02;           // State 2: Master Write, Last Byte was Data
            else if ( (i2c_status ^ state_3 ) == 0 )
                i2c_state = 0x03;           // State 3: Master Read, Last Byte was an Address
            else if ( (i2c_status ^ state_4 ) == 0 )
                i2c_state = 0x04;           // State 4: Master Read, Last Byte was Data
            else if ( (i2c_status ^ state_5 ) == 0 )
                i2c_state = 0x05;           // State 5: Master NACK

            switch ( i2c_state ) {
                case State_1:           // State 1: Write operation, last byte was an address, buffer is full
                    wrt_AIndex = CLEAR;                         // Clear the ADDR write index
                    wrt_DIndex = CLEAR;                         // Clear the DATA write index
                    i2c_data = sspbuf;                          // Do a dummy read of the SSPBUF
                    i2c_data >>= 0x01;                              // Right Shift the address by 1
                    ADDR_BUFFER[wrt_AIndex] = i2c_data;     // Write the Addrress into the ADDR Buffer (00 - General Call)                  
                    wrt_AIndex++;                                   // Increment the address index
                    break;
                case State_2:           // State 2: Write operation, last byte was data, buffer is full
                        wrt_Offset = ADDR_BUFFER[ADDR_LOW];         // Get the Write Offset Value
                        if ( wrt_AIndex < (ADDR_BUF_LEN - 2) )  // Edit - When increasing the ADDR Buffer
                        {
                            ADDR_BUFFER[wrt_AIndex] = sspbuf;       // Write the Slave Address and Memory Offset to the ADDR buffer
                            wrt_AIndex = ( (wrt_AIndex < (RX_BUF_LEN - 1)) ? ++wrt_AIndex : CLEAR ); // Increment the buffer pointer
                        }
                    else    
                        {
                            DATA_BUFFER[(wrt_DIndex + wrt_Offset)] = sspbuf;        // Write the data from the Master to the DATA buffer
                            wrt_DIndex = ( (wrt_DIndex < (RX_BUF_LEN - 1)) ? ++wrt_DIndex : CLEAR ); // Increment the buffer pointer
                        }
                    break;
                case State_3:           //  State 3: Read operation, last byte was an address, buffer is empty
                    rd_Index = CLEAR;                                           // Clear the buffer index (rd_Index)
                    rd_Offset = ADDR_BUFFER[ADDR_LOW];                      // Get the Read Offset Value
                    i2c_data = DATA_BUFFER[rd_Index + rd_Offset];       // Get the byte from the data buffer
                    rd_Index = ( (rd_Index < (RX_BUF_LEN - 1)) ? ++rd_Index : CLEAR ); // Increment the rd_Index and wrap around if required

                    do {                                                // Yes, keep waiting
                            i_counter++;                                // Do something and loop again
                            }
                    while ( test_bit(sspstat, BF) == 1 );   // Is the buffer full?

                    // I2C_Write the data start (do not write the following into a function when using it in an ISR())
                    sspbuf = i2c_data;                          // Write the data to the SSP Buffer register

                    do {                                                    // Yes - Write data again
                        clear_bit( sspcon, WCOL);                   // Done waiting, clear the WCOL flag
                        sspbuf = i2c_data;                          // Write the data to the SSP Buffer register
                            }                   
                    while   ( test_bit(sspcon, WCOL) == 1);     // Was there a collision?

                    set_bit( sspcon, CKP);                          // Release the clock line - 16i2csi.asm
                    // I2C_Write the data complete
                    break;

                case State_4:           //  State 4: Read operation, last byte was data, buffer is empty
                    rd_Offset = ADDR_BUFFER[ADDR_LOW];                      // Get the Read Offset Value
                    i2c_data = DATA_BUFFER[rd_Index + rd_Offset];       // Get the byte from the data buffer
                    rd_Index = ( (rd_Index < (RX_BUF_LEN - 1)) ? ++rd_Index : CLEAR );  // Increment the rd_Index and wrap around if required

                    // I2C_Write the data start (do not write the following into a function when using it in an ISR())
                    do {                                                // Yes, keep waiting
                            i_counter++;                                // Do something and loop again
                            }
                    while ( test_bit(sspstat, BF) == 1);    // Is the buffer full?

                    sspbuf = i2c_data;                          // Write the data to the SSP Buffer register

                    do {                                                // Yes - Write data again
                        clear_bit( sspcon, WCOL);               // Done waiting, clear the WCOL flag
                        sspbuf = i2c_data;                      // Write the data to the SSP Buffer register
                            }                   
                    while   ( test_bit(sspcon, WCOL) ==1 ); // Was there a collision?

                    set_bit( sspcon, CKP);                      // Release the clock line - 16i2csi.asm

                    // I2C_Write the data complete
                    break;

                case State_5:

                    // Reset the SSP Unit
                    sspcon = 0x00;              // initialise the I2C control register
                    sspcon2 = 0x00;                 // initialise the I2C control register
                    sspcon = 0x36;              // initialise the I2C control register
                                                        // Bit 7 - Standard speed mode
                    sspstat = 0x80;                 // initialise the I2C status register (mirrors HW SSPSTAT)
                    set_bit( sspcon, CKP);      // Set the clock line - bug as per http://www.astrosurf.com/soubie/pic_as_an_i2c_slave.htm
                    break;

                default:
                    i2c_state = 0x00;

                } // End Switch

            } // We did not have an overflow condition to clear

            clear_bit( pir1, SSPIF );   // Reset the SSPIF interrupt flag

    }   // End pir1.SSPIF interrupt


        if (kbhit())        // When the user hits any key - write the debug information to the terminal
            {
                putc(getc());                                       // Get the input character from the UART
                
                    puts("Data Buffer");                            // Show the data received from the Master Device
                    for ( i_counter = 0; i_counter < RX_BUF_LEN; i_counter++ ) {
                        i2c_data = DATA_BUFFER[i_counter];
                        uitoa_hex( ascii_code, i2c_data, 2 );
                        puts_debug(ascii_code);
                        }

                    putc(0x0d);
                    putc(0x0a);

                    puts("Address Buffer");                         // Show the address received from the Master Device
                    for ( i_counter = 0; i_counter < ADDR_BUF_LEN; i_counter++ ) {
                        i2c_data = ADDR_BUFFER[i_counter];
                        uitoa_hex( ascii_code, i2c_data, 2 );
                        puts_debug(ascii_code);
                        }

                    putc(0x0d);
                    putc(0x0a);
        

                porta.0 = 0;                                        // Debug Code - Clear indication that data was received
        
            } // End Keyboard Hit

    } // End While Loop
}   // End main()



http://www.sourceboost.com

Copyright © 2006 SourceBoost Technologies