We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi, I am trying to use the I2C bus with a 89LV55 MCU. I have 2 devices : a 24LC16B serial eeprom and a digital pot DS1844. The MCU has no built-in facilities for the I2C bus so I created the low level routines for the communications. I believed, from the I2C bus specifications, that these functions should be adequate, but not the way I use them to access the devices. A first question would be how do you put them together ? or how to construct a program... I know I am in trouble ! I have two files : I2C.c with the I2C functions and a definition file I2C.h. The I2C functions are : void I2C_Start(void) // start I2C communication void I2C_Write(unsigned char data_out) // write a byte unsigned char I2C_Read(void) // read a byte bit I2C_Ack (void) // read acknowledge void I2C_NoAck (void) // no acknowledge void I2C_Delay(void) // 5 us delay for low speed (100kHz) void I2C_Stop(void) // stop I2C communication
----------------------------------------------------------------------------------------------------------------------- void I2C_Start(void) { SDA = HIGH; /* Bus idle when SDA and SCK at level HIGH */ SCK = HIGH; I2C_Delay(); SDA = LOW; /* Start condition */ I2C_Delay(); //SCK = LOW; /* I2C_READ and I2C_WRITE */ } ----------------------------------------------------------------------------------------------------------------------- void I2C_Write(unsigned char data_out) { unsigned char position; for (position = 0; position < 8; position++) { SCK = LOW; SDA = ((data_out & 0x80) ? 1 : 0); /* Mask to check the MSB and send 0 or 1 */ SCK = HIGH; I2C_Delay(); data_out <<= 1; /* Shift data */ } } ----------------------------------------------------------------------------------------------------------------------- unsigned char I2C_Read(void) { unsigned char data_in, position; SCK = LOW; SDA = HIGH; /* Set data pin in read mode */ data_in = 0x00; for (position = 0; position < 8; position++) { I2C_Delay(); data_in <<=1; SCK = HIGH; I2C_Delay(); data_in |= SDA; /* Read pin state */ SCK = LOW; } return (data_in); } ----------------------------------------------------------------------------------------------------------------------- bit I2C_Ack (void) { bit ack_bit; SCK = LOW; /* Wait for high state of SCK for the 9th clock cycle */ I2C_Delay(); SCK = HIGH; ack_bit = SDA; /* Read the acknowledge bit */ I2C_Delay(); return (ack_bit); } ----------------------------------------------------------------------------------------------------------------------- void I2C_NoAck (void) { SCK = LOW; /* Wait for high state of SCK for the 9th clock cycle */ SDA = HIGH; /* Set the acknowledge bit */ I2C_Delay(); SCK = HIGH; I2C_Delay(); } ----------------------------------------------------------------------------------------------------------------------- void I2C_Delay(void) // 5 us delay { _nop_(); } ----------------------------------------------------------------------------------------------------------------------- void I2C_Stop(void) { SCK = LOW; SDA = LOW; SCK = HIGH; /* Generate stop condition */ I2C_Delay(); SDA = HIGH; /* Generate stop condition */ I2C_Delay(); } -----------------------------------------------------------------------------------------------------------------------
Sorry, add to split my message into 2 parts Basically, I only use the 24LC16B to test my functions. The 24LC16B is a 16K serial eeprom, consisting of 8 blocks of 256 bytes (16 pages, each page consists of 16 bytes). In main.c, I want to fill the 16K, so I use a loop. ... // initialisation LCD, variables :command,address, value[]={0x12,0xDA,0xE4,…}... error = ERR_ACK_NULL; // error variable to determine if error occurred, and where (display on LCD)
for (block=0; block<=7; block++) { temp = (block<<1) | (CMDWR & MASK_BLOCK); // select block (0 to 7) for (page=0; (page<=0xF0) && (!error); page += 0x10) // select page (0 to 15) { I2C_Start(); I2C_Write(temp); if (I2C_Ack()) { error = ERR_ACK_CONTROL; break; } I2C_Write(page); if (I2C_Ack()) { error = ERR_ACK_ADDRESS; break; } for (i=0; i< sizeof(value) && (i<=0x0F) && (!error); i++) // { I2C_Write(value[i]); if (I2C_Ack()) { error = ERR_ACK_DATA; break; } } I2C_Stop(); Delay(400); // delay to avoid polling end of internal write for seeprom } }
bit I2C_Ack (void) { bit ack_bit; SDA=HIGH; // set pin in read mode SCK = LOW; /* Wait for high state of SCK for the 9th clock cycle */ I2C_Delay(); SCK = HIGH; ack_bit = SDA; /* Read the acknowledge bit */ I2C_Delay(); return (ack_bit); }
the philips (owner of IIC) website has oodles and bunches of bit bang routines. Go there and use that as a base Erik One note: forget about making delay loops in C, they must be in assembler.
Hi Erik, what difference does it make in my case to have a C function or a asm function to waste 5us (@12Mhz). Since I know it takes 2 mc for a call, 1 nop and 2 mc to go back to caller, it is a known delay... I have seem others programs about bit bang routines, but they are normally for low level routines (start, stop condition byte read and write) but not really how to handle a device (and if things go wrong...) but I guess it is a more complex discussion..
"Since I know it takes 2 mc for a call, 1 nop and 2 mc to go back to caller," So you think you know, do you... But what memory model are you using? What Optimisation setting? Even, what compiler version?! All of these do affect the generated code! With a high-level language like 'C' you have absolutely no guarantee whatsoever that any particular source construct will generate any specific sequence of machine instructions!
"With a high-level language like 'C' you have absolutely no guarantee whatsoever that any particular source construct will generate any specific sequence of machine instructions!" Unless you use Keil's language extension _nop_(), which is guaranteed to translate to a single nop. For the delay required in this case this would seem ideal, but do inline them (possibly by encapsulating them in a macro) rather than using a function. Stefan