Hello! I have write a program to write and read to/from AT24c04 serial ROM (89C52 interface with AT24C04). I am using Keil C to program. But i can read any data which i have write to AT24c04. I don't know exacterly why. My purpose is very simple. I listent from USART and get data from PC key board. If the character which i get from pc is 'w' or 'W' then i do get data from PC and write to AT24c04 until i get the 0x0D. Now if i get the 'r' or 'R' character from PC i read data which i have just write to AT24c04 and send back to PC. But i can't read any. Here is my code #include <REG52.h> unsigned char data DEVICEADD; sbit SDA = P1^0; sbit SCL = P1^1; int data c; char data wrom; char data rrom; void outchar(char data chr) { while(!TI) { } TI = 0; SBUF = chr; } void outstr(char* str) { while(*str != 0) { outchar(*str++); } } char i2c_start(void) { /* if(!SDA || !SCL) return 0; // i2c_start error: bus not free */ // Both bus lines are high, SDA = 0; // so set data line low to generate a start condition. SCL = 0; /* SDA = 1; SCL = 1; SDA = 0; SCL = 0; */ return 1; } void i2c_stop(void) { /* SDA = 0; SCL = 1; SDA = 1; */ unsigned char input_var; SCL = 1; SDA = 0; SCL = 1; SDA = 1; input_var = SDA; } // return ack value: 0 for fale; 1 for success char i2c_write(char data byte) { int data i; for(i = 0; i < 8 ; i++) { SDA = ((byte & 0x80) ? 1 : 0); SCL = 1; // Ensure that the data is stable during the high period. byte <<= 1; SCL = 0; } // Get ack SDA = 1; SCL = 1; i = SDA; //i = 500; SCL = 0; /* while(i-- && SDA) { } */ return !i; // Get data at end of cycle. } char i2c_writebyte(int data address, char data byte) { if(!i2c_start()) { outstr("i2c_start error: bus not free.\n"); return 0; } if(!i2c_write(DEVICEADD)) { i2c_stop(); i2c_start(); if(!i2c_write(DEVICEADD)) // Interface failed to acknowledge device address { i2c_stop(); outstr("Interface failed to acknowledge device address.\n"); return 0; } } if(!i2c_write(address >>8)) // Interface failed to acknowledge byteH address { i2c_stop(); outstr("Interface failed to acknowledge byteH address.\n"); return 0; } if(!i2c_write(address)) // Interface failed to acknowledge byteL address { i2c_stop(); outstr("Interface failed to acknowledge byteL address.\n"); return 0; } if(!i2c_write(byte)) // Interface failed to acknowledge data { i2c_stop(); outstr("Interface failed to acknowledge data.\n"); return 0; } i2c_stop(); return 1; // success } char i2c_read(char data ACK) { char index, byte; index = SDA; // Put data pin into read mode byte = 0x00; for(index = 0; index < 8; index++) // Send 8 bits to the I2C Bus { byte <<= 1; SCL = 1; // Clock the data into the I2C Bus byte |= SDA; // Input the data from the I2C Bus SCL = 0; } // Write ack bit SDA = ACK; SCL = 1; // Ensure that the data is stable during the high period. SCL = 0; return byte; } char i2c_readbyte(int data address) { char data byte; if(!i2c_start()) { outstr("i2c_start error: bus not free.\n"); return 0; } if(!i2c_write(DEVICEADD)) { i2c_stop(); i2c_start(); if(!i2c_write(DEVICEADD)) // Interface failed to acknowledge device address { i2c_stop(); outstr("Interface failed to acknowledge device address.\n"); return 0; } } // Transmit address to read from. if(!i2c_write(address >> 8)) // Interface failed to acknowledge byteH address { i2c_stop(); outstr("Interface failed to acknowledge byteH address. \n"); return 0; } if(!i2c_write(address)) // Interface failed to acknowledge byteL address { i2c_stop(); outstr("Interface failed to acknowledge byteL address. \n"); return 0; } // Now terminate write operation and do a read. if(!i2c_start()) { outstr("i2c_start error: bus not free.\n"); return 0; } // Transmit address to read from. if(!i2c_write(DEVICEADD | 1)) // Slave failed to ack read access { i2c_stop(); outstr("Slave failed to ack read access.\n"); return 0; } // Read from address and ack = 1. byte = i2c_read(1); i2c_stop(); return byte; } char get_char() { while(!RI) { } RI = 0; return SBUF; } void WriteData() { char data ch; ch = get_char(); while(ch != 0x0D) { i2c_writebyte(c++,ch); outchar(ch); ch = get_char(); } wrom = 0; ES = 1; } void ReadData() { char data index; char data input; for(index = 0; index < c; index++) { input = i2c_readbyte(index); if(input != 0) outchar(input); } c = 0; rrom = 0; ES = 1; } void main(void) { // Init serial SCON = 0x52; // Set timer1 mode and reload value TMOD &= ~0xF0; // clear timer 1 mode bits TMOD |= 0x20; // put timer 1 into MODE 2 8 bit auto reload TH1 = 0xFD; TR1 = 1; // start timer 1 TI = 0; ES = 1; // Enable Serial interrupt EA = 1; // Enable all interrupts c = 0; rrom = 0; wrom = 0; DEVICEADD = 0xA0; // 10100000B while(1) { if(wrom == 1) { outchar('W'); WriteData(); } if(rrom == 1) { outchar('R'); ReadData(); } /* count = get_char(); outchar(count); */ } } void serial (void) interrupt 4 using 3 { char data inp; if(RI) // Received data interrupt { RI = 0; inp = (char)SBUF; if((inp == 'w') || (inp == 'W')) { ES = 0; rrom = 0; c = 0; TI = 1; wrom = 1; } else if((inp == 'r') || (inp == 'R')) { ES = 0; wrom = 0; TI = 1; rrom = 1; } } else if(TI) // Transmitted data interrupt { TI = 0; } } Could you help me ?
I suspect you are seeing the AT24C02 "stretch" the I2C clock when it stores the data in the shift register into the memory. The clock on the I2C bus is "negotiated". Any time you write 0 to the SCL you are pulling down the clock line; any time you write 1 to SCL you are releasing your pull down expecting the clock to float high. However, when a slave sees the clock transition low, it latches the clock line as well in conjunction with the master. Even though the master floats the clock high, it can still be held low by the slave util it is ready for the next clock. This way the clock actually "slows" down to the slowest device on the I2C bus, basically stretching things out a little. Basically: -------------------------------------- To send the clock low: Replace SCL = 0 with // If the clock is still held low, back off // a little while(!SCL) i2c_nop(); // -- Some arbitrary delay (typically 600nS) to keep from violating timing specs // Now drive the clock low SCL = 0; -------------------------------------- To send the clock high: Replace SCL = 1 with // Release the pull-down SCL = 1; // Wait for the slave to release its // latch as well while(!SCL) i2c_nop(); In either case the i2c_nop() function should provide some form of reset for the watchdog timer and whatever other time critical activity needs to be done in the interrim.
Hello. I had change some base function such as i2c_start,i2c_stop, i2c_write and i2c_read And send the clock low was Replace SCL = 0 with the RELEASE_SCL_LOW function and send the clock hight was Replace SCL = 1 with the RELEASE_SCL_HIGHfunction. But when i write a byte to AT24C04 then i can't write device address. I mean in my program it's generate an error such as: Wrire: Interface failed to acknowledge device address Here is my change.
void RELEASE_SCL_HIGH() { SCL = 1; while(!SCL) { } }
void RELEASE_SCL_LOW() { SCL = 0; while(SCL) { } }
char i2c_start(void) { SDA = 1; //SCL = 1; RELEASE_SCL_HIGH(); _nop_(); SDA = 0; SCL = 0; RELEASE_SCL_LOW(); _nop_(); return 1; }
void i2c_stop(void) { unsigned char data input_var; SCL = 1; RELEASE_SCL_HIGH(); _nop_(); SDA = 0; SCL = 1; RELEASE_SCL_LOW(); _nop_(); SDA = 1; //SCL = 0; input_var = SDA; }
char i2c_write(char data dta) { for(index = 0; index < 8 ; index++) { //SCL = 1; // Ensure that the data is stable during the high period. SDA = ((dta && 0x80) ? 1 : 0); //SCL = 1; // Ensure that the data is stable during the high period. RELEASE_SCL_HIGH(); _nop_(); dta <<= 1; //SCL = 0; RELEASE_SCL_LOW(); _nop_(); } // Get ack SDA = 1; //SCL = 1; RELEASE_SCL_HIGH(); _nop_(); index = SDA; //SCL = 0; RELEASE_SCL_LOW(); _nop_(); return !index; // Get data at end of cycle. }
char i2c_read(char data ACK) { char data result; index = SDA; // Put data pin into read mode result = 0; for(index = 0; index < 8; index++) // Send 8 bits to the I2C Bus { //result <<= 1; //SCL = 1; // Clock the data into the I2C Bus RELEASE_SCL_HIGH(); _nop_(); _nop_(); result |= SDA; // Input the data from the I2C Bus result <<= 1; //SCL = 0; RELEASE_SCL_LOW(); _nop_(); } // Write ack bit SDA = ACK; //SCL = 1; // Ensure that the data is stable during the high period. RELEASE_SCL_HIGH(); _nop_(); //SCL = 0; RELEASE_SCL_LOW(); _nop_(); return result; }
without "digging into" your code a couple of things I do not recall any IIC bitvangers without delay, I see none in the above with the exception of a _nop_ here and there. The IIC bus is limited to (depending on the device) 100 or 400kHz and you definitely seem to clock faster. also I suggest you have a look at The best (and most) IIC appnotes: http://www.semiconductors.philips.com/buses/i2c/support/index.html and, if you or anyone else are interested in really understanding IIC, may I suggest the IIC "class" http://www.semiconductors.philips.com/acrobat_download/various/design_con_2003_tecforum_i2c_bus_overview.pdf
I can't do any things. Write/read... Poor me. Have any one from you do this ?
Have any one from you do this ? I have bit-banged IIC many times, always using the code in one of the Philips appnotes (long ago, do not ask which) as a base. Ther is a DEAD EASY way out: Today I would not even dream of bit-banging IIC. There are chips galore with hardware IIC such as Philips LPC or P89C66x or even SILabs chip (stay away from the f3xx crap) which all have hardware IIC> Then use CodeArchitect (free from http://www.esacademy.com )to generate the code. For the LPC it works out of the box, for the P89C66x it takes 10 minutes to modify, for the SILabs about an hour. Erik