/* ****************************************************************************
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