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.

How to un-hang RTC PCF8583 that stuck in I2C routine?

Status
Not open for further replies.

apricot_star

New Member
How to un-hang RTC PCF8583 that stuck in I2C routine?

I am using pic18F26xx C language to prgramme the chip.

My SDA and SCK pin are connected to the normal i/o pin.So, i am trying to emulate the pin as I2C bus.

Anyone encounter this problem before?

Regards,
 
I have encountered this problem. It is caused by interrupting the processor during a I²C transfer. The way I get around it is by manually clocking the clock line until the data line becomes high. At power up read the data pin, if it's high then repeatedly clock the clock pin be setting to output, set it low, and then switch back to input.

Mike.
 
Inside the main before i calling time from I2c, i will do a INIT I2C
// Init software I2C bus
SDA_DIR = OUT;
SDA = 1;
SCL_DIR = OUT;
SCL = 1;

Here is I2C routine i call for my RTC:
//////////////////
// I2C ROUTINE //
//////////////////

void I2C_START(void) {
SDA_DIR = OUT; // Set SDA as o/p
SDA = 1;
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
SDA = 0;
Delay10TCYx(4); // 5 uSec delay
SCL = 0;
Delay10TCYx(4); // 5 uSec delay
}

void I2C_RESTART(void) {
SDA_DIR = OUT; //Set SDA as o/p
SDA = 1;
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
SDA = 0;
Delay10TCYx(4); // 5 uSec delay
SCL = 0;
Delay10TCYx(4); // 5 uSec delay
}

void I2C_STOP(void) {
SDA_DIR = OUT; // Set SDA as o/p
SDA= 0; // clear SDA
Delay10TCYx(4); // 5 uSec delay
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
SDA = 1;
Delay10TCYx(4); // 5 uSec delay
}

char I2C_WRITE(char value) {
char i, k, ack = 0;

SDA_DIR = OUT;
i = 0x80;
for (k=0; k<8; k++){
if (i & value)
SDA = 1;
else
SDA = 0;
Delay10TCYx(1); // 5 uSec delay
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
SCL = 0;
Delay10TCYx(4); // 5 uSec delay
i >>= 1;
}
Delay10TCYx(12); // 15 uSec delay
SDA_DIR = IN;
SDA = 1;
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
ack = SDA;
SCL = 0;
Delay10TCYx(4); // 5 uSec delay
return ack;
}

char I2C_READ(void) {
char i, k, val = 0;
SDA_DIR = IN;
SDA = 1;
i = 0x80;
for (k=0; k<8; k++){
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
if (SDA)
val = (val | i);
SCL = 0;
Delay10TCYx(4); // 5 uSec delay
i >>= 1;
}
return val;
}

void I2C_ACK(void) {
SDA_DIR = OUT;
SDA = 0; // Clear SDA
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
SCL = 0;
SDA = 1; // Set SDA
Delay10TCYx(4); // 5 uSec delay
}

void I2C_NACK(void) {
SDA_DIR = OUT;
SDA = 1; // Set SDA
SCL = 1;
Delay10TCYx(4); // 5 uSec delay
SCL = 0;
Delay10TCYx(4); // 5 uSec delay
}

I dun get what you are trying to say maybe you may explain clearer by looking at my I2c routine.
 
What I am suggesting is that in your init routine you do,
Code:
    SDA_DIR = IN;
    SCL_DIR = OUT;
    SCL = 1;
    while(SDA==0){
        SCL = 0;
        Delay10TCYx(4); // 5 uSec delay
        SCL = 1;
        Delay10TCYx(4); // 5 uSec delay
    }

However, I assumed you were using the hardware MSSP. I see you are bit banging it and notice you are not doing it the conventional way. The way I²C routines are normally implemented on a pic chip is by setting the pin to output and low to pull the (data or clock) bus low and then set it back to input and let the external resistors pull the lines high. It looks like you are driving the bus low and high which is not the normal way to do it. I don't have time to look at your code in detail as I'm just on my way out. Maybe someone else can look at them, if not I'll try and look tomorrow. One last thought, have you tried slowing your oscillator - I don't know the I²C chip you are using but it may be clock stretching and you are not testing for that.

Mike.
 
Last edited:
I'm using RTC PCF8583 from Philips. The max it can go is 100Khz.
I dun understand why the clock appear to be hang while the MCU is still running as per normal and the 1hz interrupt signal (pin 7 of RTC) can be seen on osciloscope when the display on lCD seems to be hang.


I saw the get time error quite frequent also before the clock appear to be hang.
 
If you are still having problems with this code then it is maybe because you are writting the SDA_DIR before SDA. When you set SDA_DIR to input, this is the same as setting SDA high if a previous write made it high. Try swapping the order of writes to SDA_DIR and SDA in any places where a zero is written.

I.E.
Code:
change
    SDA_DIR = OUT;
    SDA = 0; // Clear SDA
to
    SDA = 0; // Clear SDA
    SDA_DIR = OUT;

Mike.
 
i've change accordingly but it seems that error which lead to the hang of the clock is still there.
Inside my ,main i will do tis:

INTCONbits.GIE = 1; //Enable Global Interrupts
while(1) {
if (Flags.Bit.SecondTick){
Flags.Bit.SecondTick = 0;
INTCONbits.GIE = 0; // Disable Global Interrupts
if (GetTime_I2C()==0) // If no error
Display_Time(); // Display time

else {
I2C_STOP();
WrLCDControl4Bit(0x80);
Out_LCD(ROM_TYPE "GetTime Error");
}
INTCONbits.GIE = 1; // Enable Global Interrupts
}

Here is routine i call for my time:
////////////////////////////////////
// INITIALIZATION OF TIME & DATE //
////////////////////////////////////
char Init_Time(){
I2C_START();
if (I2C_WRITE(0b10100000))
return(-1);
if (I2C_WRITE(0b00000000))
return(-1);
if (I2C_WRITE(0b00000000))
return(-1);
if (I2C_WRITE(0b00000000))
return(-1);
if (I2C_WRITE(0b01010000))
return(-1);
if (I2C_WRITE(0b01011001))
return(-1);
if (I2C_WRITE(0b00100011))
return(-1);
if (I2C_WRITE(0b00110001))
return(-1);
if (I2C_WRITE(0b00010010))
return(-1);
I2C_STOP();
return(0);

}
////////////////////////////
// DISPLAY OF TIME & DATE //
////////////////////////////
void Display_Time() {
WrLCDControl4Bit(0x80);
date = year_date & 0b00111111;
Out_Hex_LCD (date);

month = weekday_month & 0b00011111;
if (month == 0b00000001)
Out_LCD(ROM_TYPE "Jan");
else if (month == 0b01000010)
Out_LCD(ROM_TYPE "Feb");
else if (month == 0b00000011)
Out_LCD(ROM_TYPE "Mar");
else if (month == 0b00000100)
Out_LCD(ROM_TYPE "Apr");
else if (month == 0b00000101)
Out_LCD(ROM_TYPE "May");
else if (month == 0b00000110)
Out_LCD(ROM_TYPE "Jun");
else if (month == 0b00000111)
Out_LCD(ROM_TYPE "Jul");
else if (month == 0b00001000)
Out_LCD(ROM_TYPE "Aug");
else if (month == 0b00001001)
Out_LCD(ROM_TYPE "Sep");
else if (month == 0b00010000)
Out_LCD(ROM_TYPE "Oct");
else if (month == 0b00010001)
Out_LCD(ROM_TYPE "Nov");
else if (month == 0b00010010)
Out_LCD(ROM_TYPE "Dec");
year = year_date & 0b11000000;
year =year>>6;
year +=8;
if (year<10){
Out_LCD(ROM_TYPE "0");
Out_Dec_LCD(year);
}
else
Out_Dec_LCD(year);
Out_LCD(ROM_TYPE " ");

Out_Hex_LCD(hour);
Out_LCD(ROM_TYPE ":");
Out_Hex_LCD(min);
Out_LCD(ROM_TYPE ":");
Out_Hex_LCD(sec);
}

/////////////////////
// I2C TIME & DATE //
/////////////////////
char GetTime_I2C(){
I2C_START();
if (I2C_WRITE(0b10100000))
return(-1);
if (I2C_WRITE(0b00000000))
return(-1);
I2C_RESTART();
if (I2C_WRITE(0b10100001))
return(-1);
control = I2C_READ();
I2C_ACK();
hsec = I2C_READ();
I2C_ACK();
sec = I2C_READ();
I2C_ACK();
min = I2C_READ();
I2C_ACK();
hour = I2C_READ();
I2C_ACK();
year_date = I2C_READ();
// if (!(year_date & 0x3F))
// return(-1);
I2C_ACK();
weekday_month = I2C_READ();
// if (!(weekday_month & 0x1F))
// return(-1);
I2C_NACK();
I2C_STOP(); // Stop the process
Delay_msec(2);
return(0);
}

When the LCD appear to be hang , the last display will always be 3f11 FF:FF:FF:12
or 0008 00:00:00:14
The last two digit is changing tentatively.

Here you can viewed the error signal when error msgshown on LCD:
https://photobucket.com/aprodite_star

Thx!
 
Your problem could be to do with your interrupt routine. Can you try doing this in your main, and leave interrupts disabled.

Code:
static char OldSeconds=0;
    while(1) { 
        if (GetTime_I2C()!=0){ 		//Get Time
            I2C_STOP();			//has errored
            WrLCDControl4Bit(0x80);
            Out_LCD(ROM_TYPE "GetTime Error");
	}else{
            if(sec!=OldSeconds){	//has time changed
                Display_Time(); 	//yes, Display time
                OldSeconds=sec;	
            }
        }
    }

Can you also confirm that this error just happens randomly and not after a compile and that most of the time the code works correctly?

Mike.
 
honestly, i need the interrupt to give me 1 sec tick, if i disable the interupt i can't display the msg i wan at 1 sec interval.
 
The code I posted above takes care of that problem by only updating the display if the seconds have changed. By trying the code above it will identify of the problem is in the I²C routines or an interrupt/main code interaction problem.

Mike.
 
The code i program in is something like this but the error is still popping up.

INTCONbits.GIE = 1; //Enable Global Interrupts
while(1) {
//if (Flags.Bit.SecondTick){
//Flags.Bit.SecondTick = 0;
//NTCONbits.GIE = 0; // Disable Global Interrupts
if (GetTime_I2C()!=0){ // If no error
I2C_STOP();
WrLCDControl4Bit(0x80);
Out_LCD(ROM_TYPE "GetTime Error");}
else {
if(sec!=OldSeconds){ //has time changed
Display_Time(); //yes, Display time
OldSeconds=sec;
}
}
//INTCONbits.GIE = 1; // Enable Global Interrupts

WrLCDControl4Bit(0xC0);
switch(display){
case 1: Out_LCD(ROM_TYPE "DISPLAY1= "); break;
case 2: Out_LCD(ROM_TYPE "DISPLAY2= "); break;
case 3: Out_LCD(ROM_TYPE "DISPLAY3= "); break;
case 4: Out_LCD(ROM_TYPE "DISPLAY4= "); break;
case 5: Out_LCD(ROM_TYPE "DISPLAY5= "); break;
case 6: Out_LCD(ROM_TYPE "DISPLAY6= "); break;
}
display++;
if (display > 6)
display = 1;

if (Flags.Bit.FiveSecondTick) {
Flags.Bit.FiveSecondTick = 0;
FUBC1();
FUNC2();
}//end of FiveSecondTick

if (Flags.Bit.TenSecondTick) {
Flags.Bit.TenSecondTick = 0;
FUNC3();
}// end of TenSecondTick


if (Flags.Bit.FifteenSecondTick) {
Flags.Bit.FifteenSecondTick = 0;
INTCONbits.GIE = 0; //Disable interrupts
FUNC4();
INTCONbits.GIE = 1; //Enable interrupts
}// end of FifteenSecondTick

else {
FUBC5();
FUNC6();
}
// }// end of SecondTick
} //end of while
} //end of main
 
Last edited:
Can you turn off the interrupts by setting GIE=0 and see if it still crashes.

Also, when you post code, if you type
Code:
 before it and
after it then it will keep its format.

Mike.
P.S. going out now, will look tomorrow.
 
Last edited:
The display went hay wire ( as in the timing of the display very fast and the error msg become very frequent) however the sec tick continue to tick. So, is it the int prob or the I2c interfacing part causes the error? Is is because of the interrupt causes interference in the I2c LINE?

below is the code that i juz prog in:
Code:
//INTCONbits.GIE = 1; //Enable Global Interrupts
while(1) { 
//if (Flags.Bit.SecondTick){
//Flags.Bit.SecondTick = 0;
//NTCONbits.GIE = 0; // Disable Global Interrupts
if (GetTime_I2C()!=0){ // If no error
I2C_STOP();
WrLCDControl4Bit(0x80);
Out_LCD(ROM_TYPE "GetTime Error");}
else {
if(sec!=OldSeconds){ //has time changed
Display_Time(); //yes, Display time
OldSeconds=sec; 
}
} 
//INTCONbits.GIE = 1; // Enable Global Interrupts

WrLCDControl4Bit(0xC0); 
switch(display){
case 1: Out_LCD(ROM_TYPE "DISPLAY1= "); break;
case 2: Out_LCD(ROM_TYPE "DISPLAY2= "); break;
case 3: Out_LCD(ROM_TYPE "DISPLAY3= "); break;
case 4: Out_LCD(ROM_TYPE "DISPLAY4= "); break;
case 5: Out_LCD(ROM_TYPE "DISPLAY5= "); break;
case 6: Out_LCD(ROM_TYPE "DISPLAY6= "); break;
}
display++;
if (display > 6)
display = 1;

if (Flags.Bit.FiveSecondTick) {
Flags.Bit.FiveSecondTick = 0;
FUBC1();
FUNC2();
}//end of FiveSecondTick

if (Flags.Bit.TenSecondTick) {
Flags.Bit.TenSecondTick = 0;
FUNC3(); 
}// end of TenSecondTick


if (Flags.Bit.FifteenSecondTick) {
Flags.Bit.FifteenSecondTick = 0;
//INTCONbits.GIE = 0; //Disable interrupts
FUNC4(); 
//INTCONbits.GIE = 1; //Enable interrupts 
}// end of FifteenSecondTick

else {
FUBC5();
FUNC6();
}
// }// end of SecondTick
} //end of while 
} //end of main
 
Okay now, after program in the above code which means no interrupt happen ,error msg still thr, the time stop updating after 9 mins, but display msg still shown on LCD.
 
Status
Not open for further replies.

Latest threads

Back
Top