/* **************************************************************************** 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()
Copyright © 2006 SourceBoost Technologies