cancel
Showing results for 
Search instead for 
Did you mean: 

Need I2C EEPROM example code for STM32F407

nickcupery
Associate II
Posted on February 01, 2016 at 15:28

I have a project running nicely on a STM32F407 (same chip as on the Discovery 4 board).  It's fairly simple, using only a couple of UARTs and some GPIOs, and it

uses ABSOLUTELY NO STM CODE (including HAL).  I now need to get it to

write/read arbitrary individual bytes of a 2K-bit (byte-wide) EEPROM that is

connected to the chip's I2C1 interface.  I have no experience with I2C, and I find

all STM code and documentation to be incomprehensible.  Thus, I'm hoping someone

would be willing to provide this poor boy with a working example of using the I2C

interface to exchange data with an EEPROM.  I'd be grateful!

#stm32-i2c-eeprom
9 REPLIES 9
Radosław
Senior
Posted on February 01, 2016 at 15:37

search AN2824,  is good start to create good code.

nickcupery
Associate II
Posted on February 02, 2016 at 14:08

Thank you very much for telling me about AN2824.  I had looked for App Notes before bothering y'all, of course, but I found nothing about I2C.  Apparently, my search had been limited by the fact that I was in the STM32F4 section, and AN2824 pertained to the STM32F10xx.

AN2824 is a lot better than nothing, but it is very confusing!  It talks about sending ''the address byte'' when, in fact, there are TWO DIFFERENT address bytes for the modern-day EEPROM chips I'm looking at -- the chip address (which must be sent first), and the databyte address (which must be sent immediately next).  So, every discussion, and every flowchart, in AN2824 is non-applicable.  (I've selected the Atmel AT24C02D

EEPROM, but I will probably switch to the Microchip 24AA0248 to get the MAC address.)

Anyway, I thank you again for pointing me to AN2824 even

though I find it incomprehensible (like everything STM, including this forum).  However, I'm still hoping someone will simply offer a working EXAMPLE of code to write/read an arbitrary EPROM byte.

Radosław
Senior
Posted on February 02, 2016 at 14:26

EEPROM cell addres or register in I2C protocol is concerned as data.

1 data byte or 2 (is some cases are interpreted as reg (cell) address in IC) but this is IC dependent not protocol dependent.

stm322399
Senior
Posted on February 02, 2016 at 14:41

Modern day EEPROM are not that different from old one, except having an extra byte for memory addressing (to address more than 256 EEPROM bytes).

Most EEPROM requires ''the chip address'' to target the EEPROM device of the I2C bus (yes it's a bus), also requires an address inside the EEPROM matrix. Beware, it is likely that you need a word (two bytes) to address a single EEPROM byte.

The I2C controller is not known to be software friendly, but it does its job perfectly when properly driven. You are lucky, I already shared a simple read procedure, here is also the write counterpart.

Of course, this must be adapted to 16bit or 24bit addressing. Adaptation is also required when you need to read/write more than one byte at once.

Write_a_single_8bit_addressed_byte

{

// Prepare device access

I2C_GenerateSTART(I2C1, ENABLE);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_SB) == RESET );

I2C_Send7bitAddress(I2C1, device_address*2, I2C_Direction_Transmitter);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == RESET );

I2C_GetFlagStatus(I2C1, I2C_FLAG_MSL);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET );

// Send an 8bit byte address

I2C_SendData(I2C1, regnum);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET );

// Write one byte

I2C_SendData(I2C1, value);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET );

// Close transfer

I2C_GenerateSTOP(I2C1, ENABLE);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET );

}

Read_a_single_8bit_addressed_byte

{

// Prepare device access

I2C_GenerateSTART(I2C1, ENABLE);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_SB) == RESET );

I2C_Send7bitAddress(I2C1,device_address*2, I2C_Direction_Transmitter);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == RESET );

I2C_GetFlagStatus(I2C1, I2C_FLAG_MSL);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET );

// Send an 8bit byte address

I2C_SendData(I2C1, regnum);

// Prepare to receive data

I2C_AcknowledgeConfig(I2C1, DISABLE);

I2C_GenerateSTART(I2C1, ENABLE);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_SB) == RESET );

I2C_Send7bitAddress(I2C1, address*2, I2C_Direction_Receiver);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == RESET );

I2C_GetFlagStatus(I2C1, I2C_FLAG_MSL);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET );

// Read one byte

value = I2C_ReceiveData(I2C1);

// Close transfer

I2C_GenerateSTOP(I2C1, ENABLE);

while ( I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET );

return value;

}

nickcupery
Associate II
Posted on February 03, 2016 at 12:54

I think I finally get it. 🙂  From the point of view of the EEPROM chip, receiving a byte of data involves TWO addresses (chip address and databyte address) followed by the data byte, but from the point of view of the STM32F4 sending the byte, only ONE address and two data bytes are involved.  Thank you for pointing that out.

nickcupery
Associate II
Posted on February 03, 2016 at 13:08

I think the I2C controller is downright software antagonistic. 🙂  But I think I understand the control sequences involved now, and

I thank you for sharing that code.  Of course, every one of those ''while'' loops will have to go, and be replaced with interrupt handlers, given that my project has no time to poll ANYTHING.

I'm hoping I don't hafta go to DMA for this EEPROM stuff, but I'm not yet convinced that I can guarantee fast enough inter-byte interrupt response without DMA.  This whole thing makes me want to hug my UARTs. 🙂  Thank you very much for your help.

John F.
Senior
Posted on February 03, 2016 at 16:35

You could optionally write a ''bit-banged'' I2C routine (just using GPIO pins) where each bit transition is handled in a timer interrupt service routine. The I2C protocol is not very sophisticated and a simple state machine would suffice (where the current state is ''static'' inside the ISR).

I2C is completely unconcerned about bit rate and usually works down to DC so it wouldn't matter what clock rate you achieve (< 100kHz usually safest) and the I2C bus transitions don't need a 50/50 clock.

Note that I2C bus needs open drain configuration and pull up resistors if you do use GPIO.

nickcupery
Associate II
Posted on February 04, 2016 at 14:28

Well John, bit-banging was the first thing that came to my mind when I read the data sheets for a couple of standard EEPROM chips.  Then after reading the ''official'' (I believe) I2C spec from NXP, I was thoroughly convinced that the I2C protocol is very simple and could be very easily bit-banged.  Trouble is, I cannot spare the TIME for bit-banging, given that conversations with the EEPROM must take place at runtime in a very busy project.  All conversations with the EEPROM memory MUST be interruptable

(and interrupt-driven).

The NXP I2C protocol spec is crystal clear with one little exception (it does not mention the correct response to a Nack at the end of the device-address byte), so I have no worries about I2C.  My problem (so far) is with the STM chip's I2C hardware

interface and the STM documentation.  Both the STM data sheet/reference manuals and the STM I2C App Note are VERY

poorly written, leaving the reader to GUESS at a multitude of issues.  The are many sentences in those documents that could

be read in about three different ways.  I was appalled, quite frankly, and I have begun perusing some of the Atmel MCU documentation.

Anyway, my most pressing issue with the STM support of I2C is that I cannot convince myself that there is any way to GUARANTEE the complete transmission or reception of a byte

to/from a slave device TOTALLY UNDER HARDWARE CONTROL.  So far, I haven't found a way to force the STM hardware to tack on the STOP bit without assistance from the software (which, in the worst case, it ain't gonna get).  I need

a solution that allows, say, two hundred microseconds of interrupt

latency.  Yes, that would be one BUSY system, but its a perfectly

reasonable design goal.

My next step is to get out my trusty oscilloscope and run a few experiments that I really had hoped to not need to spend time on.

Meanwhile, it would make me feel good to hear one or more of

you guys tell me that what I need is INDEED POSSIBLE with

the STM32F407 I2C hardware.

In any case, thanks to everyone who has been offering advice on

this issue.

 - eNick

nickcupery
Associate II
Posted on February 04, 2016 at 16:59

Okay, I'm still having trouble and, without an actual example to look at, I'd like to ask for some more help.  With the STM32F407 I2C clock.  When John said ''I2C is completely unconcerned about bit rate and usually works down to DC'' I found that comforting.  However, it seems like I2C is a lot easier to make happy than the STM I2C interface itself.

I am completely mystified by STM documentation of the I2C clock settings, and I'm hoping someone will help me out if I explain my setup.

I have an STM32F407 running at the full 168 MHz, with the APB1 bus (to which the I2C is connected) running with a divisor of 4, meaning that I am runnung the APB at 42 Mhz.  So, it seems clear to me that 42 is what I should put into bits 5:0 of the I2C_CR2 register.

However, I am at a total loss to figure out what I should put into bits 11:0 of the I2C_CCR register.  I'm leaving bit 15 of IC2_CCR at its default (reset) value of 0, to select Sm mode (100 KHz).

So, could someone please tell me what value to use for IC2_CCR 11:0 (and hopefully explain how that value was derived)?

Consider the following direct quote from the Reference Manual

(RM0090):

 

If FREQR = 08, TPCLK1 = 125 ns so CCR must be programmed with 0x28

(0x28 <=> 40d x 125 ns = 5000 ns.)

I did the above quote with cut/paste, AND proofread it, and that is what it says.  Does that make any sense to anyone?  What is ''FREQR'' anyway?  That term is not found anywhere else when searching the whole document!  And what does 5000 ns have to do with anything anyway?  Maybe I'm just dumber than I thought.  In any case, I'd sure appreciate an example.

 - eNick