View previous topic :: View next topic |
Author |
Message |
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
I2C Problems with SCL SDA lines |
Posted: Fri Aug 11, 2006 6:55 am |
|
|
I have written a simple program to verify the storage and retrieval of data from a PIC16C57 to a 24LC16B EEProm. The code will loop through and store a value in the EEprom that is equal to the loop counter “i” times 10 plus the address. It will store the values in successive locations in the EEProm starting with address 1. After it stores the information it will then read a value from the EEprom that is at a location as selected by the variable “loc”.
If it runs properly after its 3 loops I should have the following:
data1 = 12
data2 = 22
data3 = 33
When I run the program and use my picmaster ICE to watch the values in the data registers I get different numbers almost every time. After I run the program sometimes data1 = data2 = data3 = 12. Occasionally data1 =12, data2 = 255, and data3 = 255
When I watch the SDA and SCL lines with my digital o-scope I see an occasional error when it loops back around and sends the new I2C_START(); command. I have noticed that the SDA line is low and when I come into this command and the SCL line goes from high to low the SCL line is already low so it does not give the correct Start sequence to the EEPROM. It does the same thing occasionally on the I2C_STOP(); command. The SCL will go from low to high but the SDA will stay low.
Is there a command I am missing that lets the SDA line return to a state it needs to be in prior to sending a Start of Stop command?
I have 10K pullups on the the SDA and SCL pins.
The delay_cycles(1); commands are used as NOPs so I can put a breakpoint in my code to step through the program.
Code: |
#include <16C57.h>
#device *=8,
#fuses HS,NOWDT,NOPROTECT
#use delay(clock=4000000)
#use I2C(master, SDA=PIN_B7, SCL=PIN_B6)
#define EEPROM_SDA PIN_B7
#define EEPROM_SCL PIN_B6
// #include <2416.c>
int data, i, data1, data2, data3, data4, data5, loc;
void main()
{
loc = 2;
while (1==1)
{
output_float(EEPROM_SCL);
output_float(EEPROM_SDA);
i=0;
data1=0;
data2=0;
data3=0;
data4=0;
data5=0;
while (i<3)
{
i++;
output_float(EEPROM_SCL);
output_float(EEPROM_SDA);
I2C_START();
delay_cycles(1);
I2C_WRITE(160);
delay_cycles(1);
I2C_WRITE(1);
delay_cycles(1);
I2C_WRITE(1+i*10);
delay_cycles(1);
I2C_WRITE(2+i*10);
delay_cycles(1);
I2C_WRITE(3+i*10);
delay_cycles(1);
I2C_STOP();
delay_cycles(1);
switch(i)
{
case 1:
output_float(EEPROM_SCL);
output_float(EEPROM_SDA);
I2C_START();
delay_cycles(1);
I2C_WRITE(160);
delay_cycles(1);
I2C_WRITE(LOC);
delay_cycles(1);
I2C_START();
delay_cycles(1);
I2C_WRITE(161);
delay_cycles(1);
data1 = I2C_READ();
break;
case 2:
output_float(EEPROM_SCL);
output_float(EEPROM_SDA);
I2C_START();
delay_cycles(1);
I2C_WRITE(160);
delay_cycles(1);
I2C_WRITE(LOC);
delay_cycles(1);
I2C_START();
delay_cycles(1);
I2C_WRITE(161);
delay_cycles(1);
data2 = I2C_READ();
break;
case 3:
output_float(EEPROM_SCL);
output_float(EEPROM_SDA);
I2C_START();
delay_cycles(1);
I2C_WRITE(160);
delay_cycles(1);
I2C_WRITE(LOC);
delay_cycles(1);
I2C_START();
delay_cycles(1);
I2C_WRITE(161);
delay_cycles(1);
data3 = I2C_READ();
}
delay_cycles(1);
I2C_STOP();
}
delay_cycles(1);
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Fri Aug 11, 2006 8:12 am |
|
|
The obvious answer is that the write has not finished. You need to either pause for 5mSec after writing the data, or use acknowledge polling, to find out when the chip has actually finished. Till the write has finished, it won't ACK, and further commands won't work.
Best Wishes |
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Fri Aug 11, 2006 9:05 am |
|
|
Thanks for the input,
I changed all of the delay_cycles(1) to delay_ms(10) and re-ran the code. I figured adding the 10ms delay would be a quick way to see if you cornered the gremlin. Unfortunatley I get the same problems. I will incorporate acknowledge polling but I think the 10ms delay should have cleared up the problem?
Thanks,
Ed |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Fri Aug 11, 2006 9:09 am |
|
|
The output_float() command only needs to be done once in the main() body before your main while() loop.
Your program is going to be writing to the eeprom as fast as it can and you will probably wear it out fairly quickly. Eeproms have a maximum number of times that it can be written to and then it dies. Your program has no control on how often it writes data.
I made a test program and I got the results of:
11
21
31
as the data that would be written.
Try something around 4K for your pull-ups.
You are writing three numbers into your eeprom but only allowing one to be read from the eeprom, in your switch(). This should not keep it from working though. It's just that you write all three numbers for each variable read back.
Ttelmah is correct, you need to poll the eeprom to see if it's finished the writing cycle. Look at the spec. sheet and it will tell you what to do. You could put a small delay in but the best way is to see if the eeprom will ACK back before you try to read the data back.
Ronald
__________________________
You know you're a parent when you realize you're brushing your teeth with your kid's acne [spam]. |
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Fri Aug 11, 2006 9:27 am |
|
|
Hi Ronald,
The program is something quick to familiarize myself with the I2C EEprom. (I put a breakpoint in my ICE after I complete the 3 write loops to prevent it from writing indefinitely). In the actual application I am working on the program will probably only write to the Eeprom once or twice a week. I changed the pullups to 4.7K. Still have the same problems.
Thanks to everyone for helping me get up to speed on this.
Ed |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Aug 11, 2006 2:53 pm |
|
|
You're also leaving out the NAK at the end of the last i2c_read().
This is in the data sheet for the 24LC16B, and it's in the sample
CCS drivers. Since you have three separate read operations,
and one i2c_read() is done in each one, then you need to do a
NAK in each of them. This is done by adding a 0 parameter to
the statement. Example:
Quote: |
data1 = I2C_READ(0);
break; |
|
|
|
edbfmi1
Joined: 18 Jul 2006 Posts: 103
|
|
Posted: Mon Aug 21, 2006 9:30 am |
|
|
Thanks PCM programmer!!!
That is what I was needing. |
|
|
|