Here are my I2C slave routines

General discussion on mikroPascal.
Post Reply
Author
Message
Andreas
Posts: 139
Joined: 05 Feb 2005 12:43
Location: Germany

Here are my I2C slave routines

#1 Post by Andreas » 30 Nov 2005 14:38

Hi,
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
In my application I always send a command byte to the slave first, this byte determines what the slave should do.

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
//******************************************************************************
This procedure receives the command byte:

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
//******************************************************************************
This 3 procedures do the I2C communication:

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
//******************************************************************************
Here are 2 example procedures that are called depending on the command byte

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
//******************************************************************************
IMPORTANT: When in receive mode, do not forget to release the clock after the last byte by adding
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

DomDom
Posts: 4
Joined: 07 Jul 2006 15:40

#2 Post by DomDom » 06 Sep 2006 17:59

excuse me...i am newbie in I2C...but i am looking for the implementation I2C for PIC slave
Can you upload the whole code?
i am trying to communicate between number of pic (16f877a)
i got no idea how i should programme it
At the beginning, i tried 16f877a I2C with eeprom. it works fine...
Thanks

Andreas
Posts: 139
Joined: 05 Feb 2005 12:43
Location: Germany

Here are my I2C slave routines

#3 Post by Andreas » 06 Sep 2006 21:27

Hi DomDom,
I2C is a little tricky. Especially the slave mode, as the PICs have a hardware flaw and sometimes (under certain conditions) a bit can get lost which causes the bus to stall. It took me (and others) weeks to get the first code working.

There is not much more to post regarding the above procedures. It is all in the comments. The only difference between the 18F452 and the 16F876 is here the automatic clock stretching. Do you have a specific question?

DomDom
Posts: 4
Joined: 07 Jul 2006 15:40

Re: Here are my I2C slave routines

#4 Post by DomDom » 07 Sep 2006 01:56

Andreas wrote:Hi DomDom,
I2C is a little tricky. Especially the slave mode, as the PICs have a hardware flaw and sometimes (under certain conditions) a bit can get lost which causes the bus to stall. It took me (and others) weeks to get the first code working.

There is not much more to post regarding the above procedures. It is all in the comments. The only difference between the 18F452 and the 16F876 is here the automatic clock stretching. Do you have a specific question?

Hm.I am studying how PIC and PIC communication with I2C.
But then, i got no idea how to write in slave mode.
If your code is working , can you send me and i have a try?
Slave and master mode as well

Below is my master mode PIC with eeprom

Code: Select all

#define LC01CTRLIN 	H'A0'	; 
#define LC01CTRLOUT 	H'A1' 	; 
#define LC01ADDR 	H'12' 	; 
#define LC01DATA 	H'34' 	; 
#define BAUD 		D'100' 	; 
#define FOSC 		D'4000' ; 

#include	<p16f877A.inc>	; processor specific variable definitions
	
	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _XT_OSC & _WRT_OFF & _LVP_OFF & _CPD_OFF

	ORG 0
; *** Setup I/O ***
	clrf 		PORTB ; 
	BANKSEL 	TRISC ; 
	movlw 		B'00011000' ; 
	movwf 		TRISC ; 
	clrf 		TRISB ; 
; *** Setup Registers for I2C ***

	BANKSEL 	SSPCON
	movlw 		B'00101000'; 
	movwf 		SSPCON ; 
	BANKSEL 	SSPSTAT
	movlw 		0x80 ; 
	movwf 		SSPSTAT ; 

	BANKSEL 	SSPADD
	movlw 		b'10000000' ; 
	movwf 		SSPADD ; 
; *** Begin I2C Data Transfer  ***
I2CWrite

	BANKSEL 	SSPCON2 ; 
	bsf 		SSPCON2,SEN ; 
	call 		WaitMSSP ; 
	movlw 		LC01CTRLIN ; 
	call 		Send_I2C_Byte ; 
	call 		WaitMSSP ; 
	BANKSEL 	SSPCON2;
	btfsc 		SSPCON2,ACKSTAT ;
	goto 		I2CFail ; 

	movlw 		LC01ADDR ; 
	call 		Send_I2C_Byte ; 
	call 		WaitMSSP ;
	BANKSEL 	SSPCON2;
	btfsc 		SSPCON2,ACKSTAT ; 
	goto 		I2CFail ; 

	movlw 		LC01DATA ; 
	call 		Send_I2C_Byte ;
	call 		WaitMSSP ; 
	BANKSEL 	SSPCON2;
	btfsc 		SSPCON2,ACKSTAT ; 
	goto 		I2CFail ; 

	BANKSEL 	SSPCON2;
	bsf 		SSPCON2,PEN ;
	call 		WaitMSSP ; 

I2CRead 
	BANKSEL 	SSPCON2;
	bsf 		SSPCON2,RSEN ; 
	call 		WaitMSSP ; 
	movlw 		LC01CTRLIN ; 
	call 		Send_I2C_Byte ; 
	call 		WaitMSSP ; 
	BANKSEL 	SSPCON2
	btfsc 		SSPCON2,ACKSTAT ; 
	goto 		I2CRead ; 
	movlw 		LC01ADDR ;
	call 		Send_I2C_Byte ; 
	call 		WaitMSSP ;
	BANKSEL 	SSPCON2
	btfsc 		SSPCON2,ACKSTAT ; 
	goto 		I2CFail ;

	bsf 		SSPCON2,RSEN ; 
	call 		WaitMSSP ; 

	movlw 		LC01CTRLOUT ; 
	call 		Send_I2C_Byte ; 
	call 		WaitMSSP ; 
	BANKSEL 	SSPCON2
	btfsc 		SSPCON2,ACKSTAT ; 
	goto 		I2CFail ; 

	bsf 		SSPCON2,RCEN ;

	call 		WaitMSSP ; 
	BANKSEL 	SSPCON2
	bsf 		SSPCON2,ACKDT ;
	bsf 		SSPCON2,ACKEN ; 

	bsf 		SSPCON2,PEN ; 
	call 		WaitMSSP ; 

	BANKSEL 	SSPBUF ; 
	movf 		SSPBUF,W ; 
	movwf 		PORTB ; 

	goto 		$ ; 
I2CFail
	BANKSEL 	SSPCON2
	bsf 		SSPCON2,PEN ; 
	call 		WaitMSSP ; 
	BANKSEL 	PORTB ;
	movlw 		0xFF ; 
	movwf 		PORTB ; 
	goto 		$ ; 

Send_I2C_Byte
	BANKSEL 	SSPBUF ; 
	movwf 		SSPBUF ; 
	retlw 		0 ; 

WaitMSSP
	BANKSEL 	PIR1 ; 
	btfss 		PIR1,SSPIF ; 
	goto 		$-1 ; 
	bcf 		PIR1,SSPIF ; 
	Retlw 		0 ; 


	END       


Andreas
Posts: 139
Joined: 05 Feb 2005 12:43
Location: Germany

Here are my I2C slave routines

#5 Post by Andreas » 07 Sep 2006 15:45

Are you talking about mikroPascal or Assembler? This is a mikroPascal board but your codes is in ASM. If you need ASM, you could simply port the above to ASM.
Unfortunately I'm on vacation right now and don't have any sources with me.

DomDom
Posts: 4
Joined: 07 Jul 2006 15:40

Re: Here are my I2C slave routines

#6 Post by DomDom » 08 Sep 2006 12:15

Andreas wrote:Are you talking about mikroPascal or Assembler? This is a mikroPascal board but your codes is in ASM. If you need ASM, you could simply port the above to ASM.
Unfortunately I'm on vacation right now and don't have any sources with me.
if your sources is in mikorepascal, it's fine for me...i can try to understand it
i just want to make my I2C communication work between 4 to 5 MCU.
I am actually doing robotic project...and i stuck in here...
hope you can help me to solve this soon..
Perhaps you can send your source code to my mail box
gaston_keatmy@yahoo.com
Thanks!

Kalain
Posts: 1093
Joined: 11 Mar 2005 18:26
Location: Aubenas, France

Re: Here are my I2C slave routines

#7 Post by Kalain » 17 Sep 2009 21:43

DomDom wrote:i just want to make my I2C communication work between 4 to 5 MCU.
I am actually doing robotic project...and i stuck in here...
Hi,

Did you manage to find a solution in order to communicate between your 4 to 5 MCU.
Alain

Alexandre47
Posts: 1
Joined: 13 Jan 2014 18:45

Re: Here are my I2C slave routines

#8 Post by Alexandre47 » 13 Jan 2014 18:51

And what about the variables? for example in the PROCEDURE SendAD the vars are not declared, are they all global variables? and what types are they? and how can i read the A/D channels? i mean wich variable should i put the A/D value to send it via I2C? thank you.

Andreas
Posts: 139
Joined: 05 Feb 2005 12:43
Location: Germany

Re: Here are my I2C slave routines

#9 Post by Andreas » 14 Jan 2014 18:54

Hi,
it is really not that difficult. Just read the PIC's spec and the help in the Compiler. I hope this helps:

Code: Select all

VAR
TmpWord : WORD;
ADValue : ARRAY[8] OF WORD;
CommandByte, dummy, ADCheckSum : BYTE;
//******************************************************************************
PROCEDURE ReadAD; //Tested
//Reads all 8 A/D channels. Takes 0.8ms for all.
BEGIN
  SSPCON1.CKP := 1;                      //Release clock
  FOR i:=0 TO 7 DO
  BEGIN
    TmpWord := ADC_Read(i);      //This could possibly be one operation, the older compilers sometimes had problems.
    ADValue[i] := TmpWord;
  END; //FOR
END; //PROCEDURE ReadAD;
//******************************************************************************
After this you should give the PIC some time for the A/D sampling before requesting the results.

BTW: Having a PIC I2C slave as the first PIC project might not be the best idea. Start with something simple.

Post Reply

Return to “mikroPascal General”