it seems that still a lot of people have problems with I2C slave mode because mP does not support it. Below please find the routines I wrote for an 18F452. With slight mods in the configuration they work on an 16F876/7 too. They are pretty straight forward, but work well for my purpose.
Code: Select all
//Port configuration:
TRISC := %xxx11xxx;
// x RC0
// x RC1
// x RC2
// 1 RC3, SCL
// 1 RC4, SDA
// x RC5
// x RC6, TX
// x RC7, RX
//I2C-Slave-Mode settings:
SSPCON1 := %00010110; //Slave Mode
SETBIT(SSPCON2, SEN); //Automatic clock stretching 0=OFF, 1=ON
CLEARBIT(SSPSTAT, CKE); //Disable SMBus specific inputs
//Set I2C-Slave Address
SSPADD := %11111000;
//Enable MSSP-Modul
SETBIT(SSPCON1, SSPEN); //MSSP-Modul enable
This is basically the main program:
Code: Select all
//******************************************************************************
WHILE (TRUE) DO
BEGIN
GetCommand; //Gets CommandByte via I2C from the Master
CASE CommandByte OF
0: SendStatus;
134: ReadAD;
135: SendAD;
136: RC5set;
137: RC5clear;
END; //CASE
END; //WHILE
//******************************************************************************
Code: Select all
//******************************************************************************
PROCEDURE GetCommand;
//Receives the CommandByte via I2C
BEGIN
//Wait for address match
WaitForPIR1SSPIFset; //Wait for address match
dummy := SSPBUF; //Get rid of Address
CLEARBIT(PIR1,SSPIF);
CommandByte := ReceiveByteI2C;
END; //GetCommand
//******************************************************************************
Code: Select all
//******************************************************************************
PROCEDURE WaitForPIR1SSPIFset;
//Waits till interrup flag PIR1,SSPIF is set
BEGIN
REPEAT
UNTIL TESTBIT(PIR1,SSPIF) = 1;
END; //WaitforPIR1SSPIFset
//******************************************************************************
PROCEDURE SendByteI2C(TByte:BYTE);
//Sends the byte in TByte via I2C
BEGIN
REPEAT
UNTIL TESTBIT(SSPSTAT,BF) = 0; //Wait for empty buffer
NOP;
NOP;
NOP;
// NOP; //Use 6 NOP for 40MHz, 3 for 20MHz
// NOP; //Don’t ask me why, but it does not
// NOP; //work without the NOPs.
SSPBUF := TByte; //Put TByte in buffer
NOP;
NOP;
NOP;
// NOP; //Use 6 NOP for 40MHz, 3 for 20MHz
// NOP;
// NOP;
SETBIT(SSPCON1,CKP); //Release the clock. This actually starts the transmission.
WaitForPIR1SSPIFset;
CLEARBIT(PIR1,SSPIF); //Clear interrupt flag
END; //SendByteI2C
//******************************************************************************
FUNCTION ReceiveByteI2C:BYTE;
//Receives one byte via I2C
//Clock is held LOW after receive (must be manually released by other code)
BEGIN
SETBIT(SSPCON1,CKP); //Release clock
WaitForPIR1SSPIFset;
RESULT := SSPBUF;
CLEARBIT(PIR1,SSPIF); //Clear interrupt flag
END; //ReceiveByteI2C
//******************************************************************************
Code: Select all
//******************************************************************************
PROCEDURE RC5set;
//Turn RC5 on
BEGIN
SETBIT(SSPCON1,CKP); //Release clock
SETBIT(PORTC, 5);
END; //ReadAD
//******************************************************************************
//******************************************************************************
PROCEDURE SendAD;
//Sends the values of the A/D channels read by ReadAD via I2C
BEGIN
SETBIT(SSPCON1,CKP); //Release clock
WaitForPIR1SSPIFset; //Wait for RepeatStart
dummy := SSPBUF; //Get rid of Address
CLEARBIT(PIR1,SSPIF);
ADCheckSum := 0;
//Send data
FOR i:=0 TO 7 DO
BEGIN
TmpWord := ADValue[i];
dummy := Hi(TmpWord);
ADCheckSum := ADCheckSum + dummy;
SendByteI2C(dummy);
TmpWord := ADValue[i];
dummy := Lo(TmpWord);
ADCheckSum := ADCheckSum + dummy;
SendByteI2C(dummy);
END; //FOR
SendByteI2C(ADCheckSum);
END; //SendAD
//******************************************************************************
SETBIT(SSPCON1,CKP); //Release clock
Otherwise the bus will be blocked.
Restrictions:
1. The procedures do not check what read/write information is in the address. I simply don’t need it because the protocol defines it already. But it should be simple to extract it.
2. The program spends the most time waiting for a command. If you need it, it shouldn't be too difficult to use interrupts.
3. There is no error handling.
It would be nice if you could give some feedback in this forum about your experiences with the routines.
Have fun
Andreas