2018-12-27 05:08 AM
Hi all,
using the STEVAL-PCC009V2 demo board I've connected an external EEPROM 24LC02 to this board by means of the I2C1 peripheral through the lines PB6 (SCL) and PB7 (SDA), these lines are available through the connector J2 (30 pin connectors on board).
I'm based on some code found over the net and adapted it to my microcontroller (STM32F103RBT6) and peripheral (I2C1).
For your reference here below the code that I've used to perform the peripheral init, byte read, byte write and multiple byte write task.
The following defeine is used for timout checking purpose:
#define I2C_TIMEOUT_MAX 100000
Here below the peripheral initialization procedure, to be called before doing any operation through the I2C port.
void I2C_Memory_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// Enable APB1 clock and peripheral
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
// SCL and SDA pins config
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_PinRemapConfig(GPIO_Remap_I2C1,DISABLE);
I2C_DeInit(I2C1);
// I2C1 config
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = 0x00;
I2C_InitStruct.I2C_Ack = I2C_Ack_Disable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = 100000; // Hz
I2C_Init(I2C1, &I2C_InitStruct);
I2C_Cmd(I2C1, ENABLE);
}
then for reading a byte:
int I2C_Memory_Read(I2C_TypeDef* I2Cx, uint8_t address)
{
uint32_t timeout = I2C_TIMEOUT_MAX;
uint8_t Data = 0;
I2C_GenerateSTART(I2Cx, ENABLE);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if ((timeout--) == 0) return -1;
}
I2C_Send7bitAddress(I2Cx, 0xA0, I2C_Direction_Transmitter);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if ((timeout--) == 0) return -1;
}
I2C_SendData(I2Cx, address);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if ((timeout--) == 0) return -1;
}
I2C_GenerateSTART(I2Cx, ENABLE);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if ((timeout--) == 0) return -1;
}
I2C_Send7bitAddress(I2Cx, 0xA0, I2C_Direction_Receiver);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if ((timeout--) == 0) return -1;
}
I2C_AcknowledgeConfig(I2Cx, DISABLE);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED))
{
if ((timeout--) == 0) return -1;
}
I2C_GenerateSTOP(I2Cx, ENABLE);
Data = I2C_ReceiveData(I2Cx);
return Data;
}
For writing a single byte:
int I2C_Memory_Write(I2C_TypeDef* I2Cx, uint8_t address, uint8_t data)
{
uint32_t timeout = I2C_TIMEOUT_MAX;
I2C_GenerateSTART(I2Cx, ENABLE);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if ((timeout--) == 0) return -1;
}
I2C_Send7bitAddress(I2Cx, 0xA0, I2C_Direction_Transmitter); // Control Byte 0xA0
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if ((timeout--) == 0) return -1;
}
I2C_SendData(I2Cx, address);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if ((timeout--) == 0) return -1;
}
I2C_SendData(I2Cx, data);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if ((timeout--) == 0) return -1;
}
I2C_GenerateSTOP(I2Cx, ENABLE);
return 0;
}
To perform a multiple byte writing sequence you have to write the control byte, the address and then the bytes in sequence, after the last one byte you have to generate the STOP bit to instruct the EEPROM to start the writing task, so with a simple modification of the basic write function by means of an array and a counter of the bytes to transmit also this task is easy to do.
int I2C_Memory_Write_Bytes(I2C_TypeDef* I2Cx, uint8_t address, uint8_t data[], uint8_t ndata)
{
uint8_t index = 0;
uint32_t timeout = I2C_TIMEOUT_MAX;
I2C_GenerateSTART(I2Cx, ENABLE);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if ((timeout--) == 0) return -1;
}
I2C_Send7bitAddress(I2Cx, 0xA0, I2C_Direction_Transmitter);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if ((timeout--) == 0) return -1;
}
I2C_SendData(I2Cx, address);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if ((timeout--) == 0) return -1;
}
// Send ndata bytes stored inside the data[] array
for (index = 0; index<ndata; index++)
{
I2C_SendData(I2Cx, data[index]);
timeout = I2C_TIMEOUT_MAX;
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if ((timeout--) == 0) return -1;
}
}
// STOP bit generation to end the transmission and start the writing task inside the EEPROM
I2C_GenerateSTOP(I2Cx, ENABLE);
return 0;
}
Hope it can help.
Best regards
Fabio
2018-12-27 08:22 AM
Beware multiple byte write is not going to work well. EEPROM has pages and multiple byte write within the same page only will work. If you try to write at address 0xff the 2 bytes value 0x11,0x12, the second one won t be written where you expect. A bit more work is needed to get rugged. Personally, at 400khz, i2c single master can be generated by gpio + sw, on any pin.
2018-12-28 02:39 AM
Hi @S.Ma thank for your answer.
Yes, the multiple byte write is intended just to perform a Page Write operation or write multiple byte inside the same page.
Considering the 24AA02/24LC02B EEPROM a page write is made of 8 bytes (because the internal buffer is 8 bytes length) so you can load the byte array up to 8 byte and set the starting address e.g. to 0x00, this will perform the whole page write for the first 8 bytes, of course is possible to use other address but care be done to respect the physical page boundaries that are integer multiplies of the page buffer size.
For sake of clearity I report below the Note taken directly from the EEPROM datasheet that explain better this concept:
Page write operations are limited to writing bytes within a single physical page regardless of the number of bytes actually being written.
Physical page boundaries start at addresses that are integer multiples of the page buffer size (or ‘page size’) and end at addresses that are
integer multiples of [page size - 1]. If a Page Write command attempts to write across a physical page boundary, the result is that the data wraps around to the beginning of the current page (overwriting data previously stored there), instead of being written to the next page, as might be expected. It is therefore necessary for the application software to prevent page write operations that would attempt to cross a page boundary.
Thank to have pointed this aspect.
Best regards
Fabio
2018-12-28 08:00 AM
To be more complete I've another point to put in evidence.
If you perform a Write operation (no matter if single or multiple) you have to wait the writing time required to the EEPROM to perform the write operation, typically for the model used in this example is about 5 ms max - Parameter Write cycle time (byte or page).
A very simple approach is to put a delay greater than this time, a better approach is check when the writing task is finished, this can be done following the note on the EEPROM datasheet (section 5 ACKNOWLEDGE POLLING).
Best regards
Fabio