PIC18F4520 with I2C Mode (PICDEM2 Plus Board)

Thread Starter

symphony888

Joined May 5, 2014
13
I'm using PICDEM2 Plus Board as the microcontroller board with the P18F4520. There is also a 24LC256 EEPROM attached to it.

The set up of it is 1 Master and 1 Slave both using the same board.
The main question is, when I'm transmitting something from Master to Slave, does both the Master and Slave SSPBUF, shows as Write or Master as Write and Slave as Read?
 

ErnieM

Joined Apr 24, 2011
8,415
Hi, welcome to the forums.

SSPBUF is called the "Serial Receive/Transmit Buffer Register" wehich implies yoes you use it for both transmission and reception of data.

In the config you are using the PIC is always the master. The slave bus address, slave memory address, and any data to be written go into SSPBUF. When reading back a byte you find it also in SSPBUF.

Study the 24LC256 transmission carefully. When writing data the procedure is clear: Write slave addr, write data starting addr, then however many bytes you wish to send. However, wher reading data you send the first two elements (slave addr, data starting addr) to set the internal ROM data pointer, then restart to read by sending slave addr, then reading each byte of data.

You ACK each byte you want until the last byte, which you NAK.
 

Thread Starter

symphony888

Joined May 5, 2014
13
Hi, welcome to the forums.

SSPBUF is called the "Serial Receive/Transmit Buffer Register" wehich implies yoes you use it for both transmission and reception of data.

In the config you are using the PIC is always the master. The slave bus address, slave memory address, and any data to be written go into SSPBUF. When reading back a byte you find it also in SSPBUF.

Study the 24LC256 transmission carefully. When writing data the procedure is clear: Write slave addr, write data starting addr, then however many bytes you wish to send. However, wher reading data you send the first two elements (slave addr, data starting addr) to set the internal ROM data pointer, then restart to read by sending slave addr, then reading each byte of data.

You ACK each byte you want until the last byte, which you NAK.
Do I really need to use the 24LC256 EEPROM for the sending/receiving for the I2C Mode? Or is it possible to just wire the RC3 and RC4 which is know as CLK and Data I assume for I2C. And also the address of the EEPROM, do I need to set it manually or it has a pre-set address?
 

ErnieM

Joined Apr 24, 2011
8,415
Do I really need to use the 24LC256 EEPROM for the sending/receiving for the I2C Mode? Or is it possible to just wire the RC3 and RC4 which is know as CLK and Data I assume for I2C. And also the address of the EEPROM, do I need to set it manually or it has a pre-set address?
Now I am confused as to your intent. When you state you have a PIC18F4520 and a 24LC256 EEPROM it is natual to assume you want to use the PIC18F4520 to talk to the 24LC256 EEPROM.

If you are asking can you do I2C without using the hardware module the answer is yes, it is very do-able to write code for the master (PIC controller) side. There are lots of examples on the web, but you may have to mentally translate from some other language to the one you are using.

The 24LC256 does indeed have it's own range of preset slave addresses, well defined in the data sheet. Most of the address is preset, some has to be hard wired to the pins.
 

Thread Starter

symphony888

Joined May 5, 2014
13
Now I am confused as to your intent. When you state you have a PIC18F4520 and a 24LC256 EEPROM it is natual to assume you want to use the PIC18F4520 to talk to the 24LC256 EEPROM.

If you are asking can you do I2C without using the hardware module the answer is yes, it is very do-able to write code for the master (PIC controller) side. There are lots of examples on the web, but you may have to mentally translate from some other language to the one you are using.

The 24LC256 does indeed have it's own range of preset slave addresses, well defined in the data sheet. Most of the address is preset, some has to be hard wired to the pins.
Right now, I'm also finding out if I should do it so that it communicates with the EEPROM. Is there any example of it? I'm not sure where to do on the EEPROM side.

I've done some coding and used an Oscilloscope to see if I can get the DATA/ADDRESS bit, START/STOP bits working. But sometimes it works, sometimes it don't. In my code, I have nothing related to the EEPROM as I'm not sure how to start at all, been trying to find an example but can't seem to find one. I'm using MPLAB IDE with C18 Compiler.
 

ErnieM

Joined Apr 24, 2011
8,415
Figure 6.1 and 8.2 of the 24LC256 data sheet shows the bit by bit transaction sequence.

If sometimes it works and sometimes it doesn't make sure every START is followed by a STOP.

If all you are sending is the address then "works" is defined by seeing the ACK bit (a zero) in slot 9.
 

Thread Starter

symphony888

Joined May 5, 2014
13
I want to ask this question though. I still can't find an answer to it.

My SSPBUF on my Master Device shows as 0xA0(R/W bit is '0') while on my Slave Device it shows as 0xA1(R/W bit is '1'). Is that suppose to be it or both needs to be '0'?
 

Thread Starter

symphony888

Joined May 5, 2014
13
Here is my Master Code:
Rich (BB code):
#include <p18f4520.h>
#include <delays.h>
#include <i2c.h>
unsigned char i2cPutByteAndWait(unsigned char);

char clearData = 0;

void main(void)
{	
	char address_w = 0xA8, result = 0, temp = 0;
	SSPSTAT = 0x80;
	SSPCON1 = 0x28;
	SSPCON2 = 0x00;
	SSPCON1bits.SSPEN = 1;
	
	TRISC = 0b00000000;
	SSPBUF=temp;
	CloseI2C();
	OpenI2C(MASTER, SLEW_OFF);
	SSPADD = 0x09;
	IdleI2C();
	StartI2C();
	while(SSPCON2bits.SEN);
	Delay10TCYx(5);
	
	WriteI2C((0xA8) & (0xFE));
	while(SSPCON2bits.ACKSTAT);
	
/*do
{
	result = WriteI2C(address_w & 0xFE);
	if(-1 == result)
	{
		temp = SSPBUF;
		SSPCON1bits.WCOL = 0;
	}
}while(0 != result);	


/*do
{
  result=WriteI2C (0x02);//sets the register on the RTC
  if (-1 == result) //write collision handler
  {
    temp=SSPBUF;       //Clearing the junk in buffer
    SSPCON1bits.WCOL=0;//Clearing the write collision flag
  }
 }*/
 //while (0!=result);//we're repeating transmission until ACK is received

IdleI2C();
	StopI2C();
	
while(SSPCON2bits.PEN);
	Delay10TCYx(5);
	CloseI2C();

}
/*unsigned char i2cPutByteAndWait(unsigned char data_out) {
    SSPBUF = data_out;          // write single byte to SSPBUF
    if ( SSPCON1bits.WCOL ) {   // test if write collision occurred
        SSPCON1bits.WCOL = 0;           // if WCOL bit is set return negative #
    }
	while(PIR1bits.SSPIF == 0);
	PIR1bits.SSPIF = 0;
    while( SSPSTATbits.BF );    // wait until write cycle is complete

   IdleI2C();           // wait until bus goes idle

   
}
And here is my Slave Code:
Rich (BB code):
#include <p18f4520.h>
#include <delays.h>
#include <stdlib.h>
#include <i2c.h>


// Function prototypes for the high priority ISRs
void ISR(void);

// Function prototype for the SSP ISR
void SSPISR(void);

// This is the code for at the high priority vector
#pragma code H_vector=0x08
void H_vector(void) { _asm goto ISR _endasm }
#pragma code

// The actual high priority ISR
#pragma interrupt ISR
void ISR(void) 
{
    if (PIR1bits.SSPIF) // Check for SSP interrupt
	{ 
		SSPISR();
        PIR1bits.SSPIF = 0;  // Clear the interrupt flag
    }
    
}

// This is the actual SSP ISR
void SSPISR(void) 
{
    PORTBbits.RB1=1;
	
}

void main(void)
{
	
	unsigned char sync_mode = SLAVE_7, slew = SLEW_OFF,slave_address = 0x08, clearBUF = 0;
	TRISA = 0b11111111;
	TRISB = 0b11000001;
	TRISC = 0b00011110;			// configure SCL SDA as input
	INTCONbits.GIEH = 0;
	RCONbits.IPEN = 1;          // Enable interrupt priorities
	IPR1bits.SSPIP = 1;         // Set SSP interrupt priority to high
	PIR1bits.SSPIF = 0;  // Clear the interrupt flag
	PIE1bits.SSPIE = 1;         // Enable SSP interrupt
	INTCONbits.GIEH = 1;

	PORTBbits.RB1=1;
	Delay10KTCYx(200);
	PORTBbits.RB1=0;
	
	SSPADD = 0xA8;
	SSPCON1 = 0b00100110;
	SSPCON2 = 0b00000001;
	SSPBUF = clearBUF;

	IdleI2C();
	OpenI2C(SLAVE_7 , SLEW_OFF);
	IdleI2C();
	SSPCON1bits.CKP = 1;
	while(1);
	
	
}
Now my SSPBUF keeps showing 0x00 and I got no idea why its like that. I'm very sure I'm overlooking something and I can't find out what is it about.
 

ErnieM

Joined Apr 24, 2011
8,415
My SSPBUF on my Master Device shows as 0xA0(R/W bit is '0') while on my Slave Device it shows as 0xA1(R/W bit is '1'). Is that suppose to be it or both needs to be '0'?
What condition is this? When do you see this mismatch?


Slave code? Why in the world do you need slave code? What device is the master when the PIC is the slave?

If you are attempting to make one PIC talk to another stop, you need to learn one side at a time.

(I will check the master side code later.)
 

Thread Starter

symphony888

Joined May 5, 2014
13
What condition is this? When do you see this mismatch?


Slave code? Why in the world do you need slave code? What device is the master when the PIC is the slave?

If you are attempting to make one PIC talk to another stop, you need to learn one side at a time.

(I will check the master side code later.)
I'm using a PICDEM2 Plus Board with the same chip which is the P18F4520. One set as Master and another as a Slave. With the RC3 and RC4 physically wired up to each other. SDA and SCL.
 

ErnieM

Joined Apr 24, 2011
8,415
I think I now get what you are doing: you have TWO PIC18F4520 chips, and you wish to communicate from one to the other using I2C. The EEPROM is not part of the set-up.


Is this correct?
 

Thread Starter

symphony888

Joined May 5, 2014
13
I think I now get what you are doing: you have TWO PIC18F4520 chips, and you wish to communicate from one to the other using I2C. The EEPROM is not part of the set-up.


Is this correct?
Yes sir. That is the current set up. As I have no idea about the EEPROM(even after reading the Datasheet, I'm still clueless about initializing it). So I'm just trying to do it via another way which is this.

The EEPROM though, is attached to the board but I'm not using it at all. I'm scared that, the EEPROM might interfere with the communication of the 2 chips.
 

ErnieM

Joined Apr 24, 2011
8,415
Unless you give the slave the same address as the EEPROM then it will sit there mute and ignore what you are doing. (So don't use 0xA0 thru 0xAF.)

I have yet to check your code, but suggest you start by sending the very simple sequence START-ADDR-STOP, and use that to see if your slave code can ACK reliably. Once you have that working (and it works for either read or write) then you can start doing some real reading and writing.

You have a scope, use it well. If you have a PICkit it works as an in circuit code debugger, meaning you can see what your code is really doing inside the hardware.
 

Thread Starter

symphony888

Joined May 5, 2014
13
Unless you give the slave the same address as the EEPROM then it will sit there mute and ignore what you are doing. (So don't use 0xA0 thru 0xAF.)

I have yet to check your code, but suggest you start by sending the very simple sequence START-ADDR-STOP, and use that to see if your slave code can ACK reliably. Once you have that working (and it works for either read or write) then you can start doing some real reading and writing.

You have a scope, use it well. If you have a PICkit it works as an in circuit code debugger, meaning you can see what your code is really doing inside the hardware.
I have PICKIT 3 that I'm using currently. Oh, the slave address initially I use was 0xA8 so I think maybe that might be one of the cause.

So basically:
IdleI2C();
OpenI2C(MASTER, SLEW_OFF);
StartI2C();
IdleI2C();
WriteI2C(Slave_address & 0XFE);
IdleI2C();
WriteI2C(data_out);
IdleI2C();
StopI2C();
IdleI2C();
CloseI2C();

Is that what you're talking about?

Also for Master Device, does the SSPBUF clears itself?

edit1: I've just tried changing the address and nothing came up on the scope. No waveform or anything and also the Master Device bits.BF isn't Full and also there was no ACK from Slave. I must be overlooking something which I can't find out.
edit2: Now the Interrupt Flag is set but the return of SSPBUF on Slave Device is = 0x99 and the SSPBUF on Master Device is 0x98. Is the R/W bit suppose to be like that or? It should be the same as the master.
edit3: Interrupt Flag isn't always set. It is random. Sometimes it the IF will set, sometimes it will not. This happens even when I did not change anything.
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,415
symphony888: sorry I owe you a reply. Could you post you code as you have it now, and with any luck I can peek at it tonight.

(Hopefully you already have it all working and onto the next big thing.)
 

Thread Starter

symphony888

Joined May 5, 2014
13
symphony888: sorry I owe you a reply. Could you post you code as you have it now, and with any luck I can peek at it tonight.

(Hopefully you already have it all working and onto the next big thing.)
Slave.c

Rich (BB code):
#include <p18f4520.h>
#include <delays.h>
#include <stdlib.h>
#include <i2c.h>

#pragma config OSC = INTIO67
// Function prototypes for the high priority ISRs
void ISR(void);

// Function prototype for the SSP ISR
void SSPISR(void);

// This is the code for at the high priority vector
#pragma code H_vector=0x08
void H_vector(void) { _asm goto ISR _endasm }
#pragma code

// The actual high priority ISR
#pragma interrupt ISR
void ISR(void) 
{
    if (PIR1bits.SSPIF) // Check for SSP interrupt
{ 
SSPISR();
        PIR1bits.SSPIF = 0;  // Clear the interrupt flag
    }
    
}

// This is the actual SSP ISR
void SSPISR(void) 
{
    PORTBbits.RB1=1;

}

void main(void)
{

unsigned char sync_mode = SLAVE_7, slew = SLEW_OFF,slave_address = 0x08, clearBUF = 0;
TRISA = 0b11111111;
TRISB = 0b11000001;
TRISC = 0b00011110;// configure SCL SDA as input
PORTBbits.RB1 = 0;
INTCONbits.GIEH = 0;
RCONbits.IPEN = 1;          // Enable interrupt priorities
IPR1bits.SSPIP = 1;         // Set SSP interrupt priority to high
PIR1bits.SSPIF = 0;  // Clear the interrupt flag
PIE1bits.SSPIE = 1;         // Enable SSP interrupt
INTCONbits.GIEH = 1;
SSPADD = 0x98;
SSPCON1 = 0b00100110;
SSPCON2 = 0b00000000;
SSPBUF = clearBUF ;


OpenI2C(SLAVE_7 , SLEW_OFF);

SSPCON1bits.CKP = 1;
while(1);
}
Master.c
Rich (BB code):
#include <p18f4520.h>
#include <delays.h>
#include <i2c.h>
unsigned char i2cPutByteAndWait(unsigned char);

char clearData = 0;

void main(void)
{
char address_w = 0x98, result = 0, temp = 0;
SSPSTAT = 0xC0;
SSPCON1 = 0x28;
SSPCON2 = 0x00;
SSPADD = 0x09;
TRISC = 0b00000000;
OpenI2C(MASTER, SLEW_OFF);
StartI2C();
IdleI2C();
WriteI2C(address_w & 0xFE);
IdleI2C();
WriteI2C(0x01);
IdleI2C();

SSPBUF = address_w;
while(SSPSTATbits.BF);
while((SSPCON2 & 0x1F) || (SSPSTAT & 0x04));

SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN == 1);
}
 

ErnieM

Joined Apr 24, 2011
8,415
Besides a general comment not to put function calls inside an ISR nothing jumped out as wrong to me.

Exactly what are you seeing, what is working, what is not working?
 

Thread Starter

symphony888

Joined May 5, 2014
13
Besides a general comment not to put function calls inside an ISR nothing jumped out as wrong to me.

Exactly what are you seeing, what is working, what is not working?
I repatched the circuit. Only a PIC18F4520 and the needed stuffs for the I2C. No EEPROM and such, and still it didn't work.

No start bit/stop bit is being produced at all.
 

ErnieM

Joined Apr 24, 2011
8,415
OK, so the problem right now is in the master.

Simplest thing first: do you have the I2C pull up resistors? 5K is typical. An unprogrammed (just bulk erase it) PIC should show these 2 pins as a high.

Next, your code master does one write to the slave then stops. It's much easier to see a periodic repeating signal. I would make my test master as such:

Rich (BB code):
#include <p18f4520.h>
#include <delays.h>
#include <i2c.h>
unsigned char i2cPutByteAndWait(unsigned char);
char clearData = 0;
void main(void)
{
    char address_w = 0x98, result = 0, temp = 0;
    SSPSTAT = 0xC0;
    SSPCON1 = 0x28;
    SSPCON2 = 0x00;
    SSPADD = 0x09;
    TRISC = 0b00000000;     // afaik the ports should be set as inputs
                            // but OpenI2C does this for you.
    while(1)    
    {
        OpenI2C(MASTER, SLEW_OFF);
        StartI2C();
        IdleI2C();
        WriteI2C(address_w & 0xFE);
        IdleI2C();
        WriteI2C(0x01);
        IdleI2C();
 
        SSPBUF = address_w;
        while(SSPSTATbits.BF);
        while((SSPCON2 & 0x1F) || (SSPSTAT & 0x04));
 
        SSPCON2bits.PEN = 1;        // StopI2C
        // add a delay here to break up transmissions on your scope
    }    
//    while(SSPCON2bits.PEN == 1);
}
Toggling an unused pin while transmitting gives your scope a synch to use. It's also a nice sanity test.

(BTW, leaving the EEPROM would give you a 2nd slave to test the master's START/ADDR/STOP against. So it may have helped to have left it in.)
 
Top