Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Problem with MSSP as I2C master

Status
Not open for further replies.

bleux

New Member
Hello everybody.
I'm trying to operate a 24LC256 EEPROM with a PICDEM 2 board. Since I'm new on PIC and I2C bus, with the program below, I have some problems:

1. The data I got was always constant (0x01) whatever the memory address was.

2. The ACKEN bit was never cleared by the hardware for NACK sending sequences. So the program could never reach the i2c_stop() function of a read operation.

I compared my program with those I found on internet, there were no significant differences. :confused: Moreover, I had exactly the same problems when I operated a TC74A5 temperature sensor except that the constant value was different.

Could anybody be kind enough to figure out the errors for me? Thank you very much in advance!!!;)

Hardware: Microchip PICDEM 2 plus (24LC256 and TC74 on board) + PIC18F4520
Software: MPLAB IDE 8.3 + HI-TECH PICC18 compiler

Program:

Code:
[COLOR="Navy"]#include <pic18.h>

__CONFIG(1,IESODIS & FCMDIS & XT);
/* Clock switchover disabled
   Fail-Safe Mode disabled
   External Crystal oscillator */

__CONFIG (2, BORDIS & PWRTEN & WDTDIS);
/* Brown-out Reset disabled
   Power-up Timer disabled (for ICD2 debugger) 
   Watch Dog disabled */

__CONFIG (3, MCLREN & LPT1DIS & PBDIGITAL);
/* Master Clear enabled
   Timer1 Low Power Mode disabled
   PortB 0:4 configured as digital */

__CONFIG (4, DEBUGEN & LVPDIS);
/* ICD enabled
   LV ICSP disabled
   Stack Full/Overflow Reset enabled (default)
   Extended Instruction disabled (default) */

/* Code/Write/Read Protection bits
__CONFIG (5, );
__CONFIG (6, );
__CONFIG (7, );
Default Values : No protections */

#define WADD 0b10100000			//1001101+0 write
#define RADD 0b10100001			//1001101+1 read

void init()
{
[INDENT]    GIE =0;			//Disable all interrupts

    //**********************************************
    TRISC3 =1;		//RC3 as INPUT (SCL)
    TRISC4 =1;		//RC4 as INPUT (SDA)	

    //**********************************************
    //MSSP Configuration
    SSPCON1 =0x28;	//Enable MSSP in I2C Master mode
    SSPCON2 =0;
    SMP =1;		//Disable slew rate control
    CKE =0;		//I2C compatible
    SSPADD =0x09;	//Fscl = 100kHz pour Fosc=4MHz
                         //Fscl = Fosc/(4*(SSPADD+1))[/INDENT]
}

void i2c_idle()		//Wait until the bus is idle
{
 [INDENT]   while ((SSPCON2 & 0x1F) | RW);[/INDENT]
}

void i2c_start()
{
 [INDENT]   SEN =1;		//Start Sequence[/INDENT]	
}

void i2c_stop()
{
[INDENT]    PEN =1;		//Stop Sequence[/INDENT]	
}

void i2c_restart()
{
 [INDENT]   RSEN =1;		//Restart Sequence[/INDENT]
}

void i2c_nack()
{
[INDENT]    ACKDT =1;			
    ACKEN =1;	//Start not acknowledge sequence[/INDENT]
}

void i2c_ack()
{
[INDENT]    ACKDT =0;			
    ACKEN =1;	//Start acknowledge sequence[/INDENT]
}

void i2c_write(unsigned char data)
{
[INDENT]    SSPBUF =data;	//Write data or command or address + R/W bit
    i2c_idle();	//Wait for the end of write (RW cleared)
    while (ACKSTAT);	//Wait for ACK from slave[/INDENT]
}

unsigned char i2c_read ()
{
[INDENT]    RCEN =1;		//Receive data
    i2c_idle();	//Waiting for the end of read (RCEN cleared)
    return (SSPBUF);[/INDENT]
}

void main ()
{
[INDENT]    unsigned char temp=0,i;
    init();
    while (1)
    {
[INDENT]        //write (page write)
        i2c_start();
        i2c_idle();
        i2c_write(WADD);	//EEPROM selected in write mode
        i2c_write(0x00);	//Address HIGH
        i2c_write(0x00);	//Address LOW

        for (i=1;i<5;i++)
 [INDENT]           i2c_write(i+1);	//Page write (4 bytes)[/INDENT]
        i2c_idle();
        i2c_stop();
        i2c_idle();

        delay_ms(50);  //EEPROM executing internal writing cycles

        //read (sequential read)
        i2c_start();
        i2c_idle();
        i2c_write(WADD);	//EEPROM selected in write mode
        i2c_write(0x00);	//Address HIGH
        i2c_write(0x00);	//Address LOW
        i2c_restart();
        i2c_idle();
        i2c_write(RADD);	//EEPROM selected in read mode
	
        for (i=1;i<5;i++)
        {
[INDENT]            temp=i2c_read();
            if (i==4)
                i2c_nack();   //NACK before STOP
            else
                i2c_ack();    //ACK for next read
            i2c_idle();[/INDENT]        
        }
        i2c_stop();
        i2c_idle();[/INDENT]    
    }[/INDENT]
}[/COLOR]
 
Last edited:
Hello, Gayan.

Yes, on the development board, there are pull up resistances (4.7k I think) for SCL and SDA lines.
 
When you set any bit (SEN, PEN etc) you have to wait for the module to become idle by waiting for SSPIF to be set. Once set you must clear it before continuing. I am not at home at the moment but if you're still having problems later I can post complete routines.

Mike.
 
Hello, Mike, thanks for your response.

I know that polling SSPIF is a I2C bus management method. But on page 171 of the 18F4520 datasheet, I found "ORing the bit R/W (bit 2 of SSPSTAT) with SEN, RSEN, PEN, RCEN or ACKEN will indicate if the MSSP is in Active mode". So I think my function i2c_idle() should work, and I really found many programs using the same wait-for-idle function. However, I'll try the SSPIF, and I'll tell you the result later, thanks.
 
Last edited:
Hello, Mike.

I tried polling SSPIF:

void i2c_idle()
{
while (!SSPIF)
SSPIF=0;​
}

but surprisingly, the program was blocked just after the start sequence, the SSPIF was never set by the hardware...

i2c_start();
i2c_idle(); ---------------------->dead loop here :(
i2c_write(WADD);
......
 
Hi bleux,

Here is my I2C code in BoostC. The read write are for a 1Meg EEPROM and so not applicable to your app. However, you should be able to convert this to get something up and running.

Code:
#define I2cClock        portc.3
#define I2cData         portc.4
#define I2cClockTris    trisc.3
#define I2cDataTris     trisc.4


void WaitSSP(){
    while(pir1.SSPIF==0);
    pir1.SSPIF=0;
}

void I2cStart(){
    sspcon2.SEN=1;
    WaitSSP();
}

void I2cStop(){
    sspcon2.PEN=1;
    WaitSSP();
}

void I2cReStart(){
    sspcon2.RSEN=1;
    WaitSSP();
}

char I2cSendByte(unsigned char data){
    sspbuf=data;
    WaitSSP();
    return(!sspcon2.ACKSTAT);
}

char I2cReceiveByte(){
    sspcon2.RCEN=1;
    WaitSSP();
    return(sspbuf);
}


void I2cInit(){
    I2cDataTris=1;              //Make data input
    I2cClockTris=1;             //and clock
    while(!I2cData){            //if something holding line low
        I2cClock=0;             //then pulse clock line
        I2cClockTris=0;
        asm{
            goto End            //short delay
            End:
        }
        I2cClockTris=1;
    }
    sspadd=8000/4/100;
    sspstat=1<<SMP;
    sspcon=0x28;
    pir1.SSPIF=0;
    pir2.BCLIF=0;
}

void I2cNack(){
    sspcon2.ACKDT=1;
    sspcon2.ACKEN=1;
    WaitSSP();
}

void I2cAck(){
    sspcon2.ACKDT=0;
    sspcon2.ACKEN=1;
    WaitSSP();
}

unsigned char I2cSendId(unsigned char ID){
char count;
    I2cStart();
    if(I2cSendByte(ID))
        return(1);
    count=0;
    do{
        I2cReStart();
        if(I2cSendByte(ID))
            return(1);
    }while(--count!=0);
    return(0);
}

char I2cWriteByte(long Address,char Dat){
char block;
    Address&=0x1ffff;
    block=(Address>0xffff)?0:8;
    if(!I2cSendId(0xa0+block))            //EEPROM ID
        return(0);
    I2cSendByte(Address>>8);
    I2cSendByte(Address&255);
    I2cSendByte(Dat);
    I2cStop();
    return(1);
}

char I2cReadByte(long Address){
char temp,block;
    Address&=0x1ffff;
    block=(Address>0xffff)?0:8;
    if(!I2cSendId(0xa0+block))            //EEPROM ID
        return(0);
    I2cSendByte(Address>>8);
    I2cSendByte(Address&255);
    I2cReStart();
    I2cSendByte(0xa1+block);
    temp=I2cReceiveByte();
    I2cNack();
    I2cStop();
    return(temp);
}

Let us know if it works and what you had to change to get it working with your compiler.

Mike.
 
Thanks a lot for your help, Mike.

Before seeing your code, I tried change my pic to see if it was a hardware problem. Since I hadn't a new 18F4520 at hand, I used a new 18F452, and by accident I replaced my delay function delay_ms() with the one offered by the compiler __delay_ms(). And thus the program worked correctely! I reinstalled the 18F4520 and keep the __delay_ms() functions, it also worked well.

In conclusion, I made a stupid mistake on my delay function. I thought delay_ms(1) would bring a delay of 1ms, but in fact it brought only a delay in us perhaps. And in the page write mode, the EEPROM writes all data received into memory only when the master stops the transmit. That is to say, the more bytes to write in a page write, the more delay is needed by the EEPROM after the master's stop action. Since I didn't leave enough time to EEPROM for its internal writing cycles, the data read are certainly wrong.

By the way, I still don't understand why the start sequence was stuck when I used the SSPIF polling method... (I'll look at this if I have the time.)

Anyway, I'm happy that the code works despite the stupid mistake. Thank your for your generous help, Mike. Thank you too Gayan.
 
When using the MSSP module you shouldn't need any delay and so I'm confused at your reply. Anyway, you got your code working which is the important bit.

Mike.
 
Hi, Mike, yes you are right. But in fact, the delays in my code are just after page write operations. The data sheet of the EEPROM indicate that the user should leave some time to make the EEPROM write all the data in internal buffer into the "real" memory cells and during this internal writing time, all external commands are ignored.
 
Same problem here.

Hi Everyone,

Well I am using CCS compiler and PIC simulator IDE for interfacing PIC-EEPROM 24C256. I have done that using built in functions but when I am compiling my own written functions, the program gets stuck in the start condition. SSPIF never sets high. This is because scl and sda are never changing their states (always active high).. Code is very simple but I am failed fixing the problem. Can anybody indicate the problem. Many Thanks.

CODE:

#define sda portc4
#define scl portc3

void main(void)
{

trisc3=1;
trisc4=1;
SSPCON2=0X00;
SSPCON=0X38;
SSPADD=0x09;
SSPSTAT=0X80;
sspif=0;
bclif=0;
sen=1;
while(sspif==0); --PROGRAM GETS STUCK HERE.
sspif=0;

}

Note that I am performing simulations so there are no hardware considerations. Also I have simulated the same module using built in function of CCS compiler successfully.
 
Last edited:
I don't think you can simulate I²C without setting up a very complex simulation file. Build the hardware and see if it works.

Mike.
 
Hi pommie,

Thanks for responding. Well I am using PIC simulator IDE. It involves its own EEPROM module. I have noted the scl and sda pins being configured by the simulator itself. It means that it is working as well as the same code written with built in functions runs successfully with desired results. May be the built in function in CCS internally involves something that sets up the I2C module in simualtion.. what do you say?

Regards,
Mork.
 
Problems with I2C in Picdem 2 Board (18f4520, 24C04, LM75), Hi-Tec-Compiler

Hi bleux,

I also have severe Problems with the i2c-Protokoll. I tried your code with modified Delay-function, but it doesnt work. Could you probably send me your code. That would be great.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top