cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 I2C Issue?

markgilson9
Associate II
Posted on May 23, 2012 at 18:47

At a high level, I'm able to get I2C on the STM32F4-Discovery board working with our part (MAX5418), but there's one problem I'm having, and that is that I have to write commands twice in order for them to be received.  Here's my code:

****INITIALIZATION CODE****

GPIO_InitTypeDef GPIO_InitStructure;

  I2C_InitTypeDef  I2C_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    

  /* GPIOB Periph clock enable */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

  /* Configure I2C1 pins: SCL and SDA ----------------------------------------*/

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_9;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;

  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource6,GPIO_AF_I2C1);

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9,GPIO_AF_I2C1);   

  /* I2C1 Periph clock enable */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  

  I2C_DeInit(I2C1);

  /* I2C1 configuration ------------------------------------------------------*/

  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;

  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;

  I2C_InitStructure.I2C_OwnAddress1 = 0x00;

  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;

  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

  I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;

  /* Enable the I2C peripheral */

  I2C_Cmd(I2C1, ENABLE);

  I2C_Init(I2C1, &I2C_InitStructure);

****FUNCTION USED TO WRITE DATA TO CHIP****

void I2C_WriteBuffer(uint8_t device_addr, uint8_t* pbuffer, uint8_t length)

{

    /* Send START condition */

    I2C_GenerateSTART(I2C1, ENABLE);

    /* Test on EV5 and clear it */

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));  

    /* Send EEPROM address for write */

    I2C_Send7bitAddress(I2C1, device_addr, I2C_Direction_Transmitter);

    /* Test on EV6 and clear it */

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    while(length--)

    {

        /* Send the byte to be written */

        I2C_SendData(I2C1, *(pbuffer++)); 

        /* Test on EV8 and clear it */

        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    }

  

    /* Send STOP condition */

    I2C_GenerateSTOP(I2C1, ENABLE);

}

If  I call the I2C_WriteBuffer() function twice the chip accepts the data, but if only once, nothing happens.  This could also be an issue with the chip as well, but wanted to remove the possibility of it being code.  Could it be something wrong with the way I leave the I2C bus (stop bit wrong, I2C disabling, etc..)?
7 REPLIES 7
carl2399
Associate II
Posted on May 24, 2012 at 11:16

The STM32F series of chips seem to have troublesome I2C operation since the days of the STM32F1xx family. If you do a google search you can find a number of hits, and all the posts read similar to yours.

I also had issues - the interface would just stop, and as far as I can tell it was getting into a state that supposedly wasn't possible (according to the data sheet). There is an STM32 technical bulletin, but it just seems to result in even more bloated and confusing code.

All I wanted was I2C master - my device never needs to be a slave device. So, I threw out the whole lot of I2C library code and wrote a bit bash I2C driver. It clocks at some random frequency, but the beauty of I2C is that most I2C peripherals are state machine driven so the clock rate doesn't matter (it's even okay for it to vary by huge amounts from one bit to the next).

I wrote my version of driver to stall the application while it sends out a byte (wiggling SCL and SDA as it goes), but you could probably get better performance and a more uniform clock rate if you use a timer interrupt.

Basic instructions:

Set SCL and SDA pins as open driver outputs.

I use the following defines to set and read the I2C port.

// SCL on PB10

#define SCL_LOW GPIOB->BSRRH = 0x0400

#define SCL_HIGH GPIOB->BSRRL = 0x0400

// SDA on PB11

#define SDA_LOW GPIOB->BSRRH = 0x0800

#define SDA_HIGH GPIOB->BSRRL = 0x0800

#define SDA_STATE ((GPIOB->IDR & 0x0800) != 0)

You don't need to change the direction of the pins while you go.

I2C is the easiest of all the protocols to bit bash - so if you've never tried it before then I'd encourage you to give it a go.

Best of luck.

frankmeyer9
Associate II
Posted on May 24, 2012 at 15:22

I wrote my version of driver to stall the application while it sends out a byte...

That's correct. You waste a lot of cycles to get down to the i2c speed. The advantage is, you can single-step through the i2c states and check the levels statically with a voltmeter.

... but you could probably get better performance and a more uniform clock rate if you use a timer interrupt.

 

But this makes it rather complex, much more error prone and difficult to debug and port.

Do you have a bit-banging solution that supports bit stretching ?

markgilson9
Associate II
Posted on May 24, 2012 at 16:28

Are you implying that the provided firmware is faulty in some way?  I'm just looking to get this working with the current drivers.

carl2399
Associate II
Posted on May 25, 2012 at 09:15

I could publish what I've got, but it's a bit rough and ready. The clock rate varies between about 50kHz and 300kHz on a bit to bit basis. It would probably need to be checked on an oscilloscope against the compiler and optimisation level selected to make sure you stay below the maximum clock frequency of the attached device (400kHz in my case). What I've done works for me, as I only have to transfer 10 byes every 75Hz. So, not much data, but on a device that could be switched on for an extensive period of time.

markgilson9
Associate II
Posted on May 29, 2012 at 13:24

Bit banging with your own driver seems unwise seeing as there is firmware available to control the I2C unless, of course, the firmware drivers are faulty, which I don't think this is the case.  Has anyone else had success with I2C, if so, can you take a look at the code attached?  Thanks.

markgilson9
Associate II
Posted on August 08, 2012 at 13:34

I still believe I am having issues with the I2C, specifically with the start and stop sequences.  Below is a screenshot of what I'm seeing.  The I2C address and data are definitely being acked, but Even though I have:

I2C_GenerateSTART(I2C, ENABLE);

....

I2C_GenerateSTOP(I2C, ENABLE);

It doesn't seem like I'm seeing the start or stop.  As you can see there is a restart event between my messages, implying no stop.

0690X00000602lSQAQ.jpg

Any suggestions???

markgilson9
Associate II
Posted on August 08, 2012 at 17:51

Actually, it appears the start is working, just not the stop.  Here's what I'm doing for my stop:

I2C_GenerateSTOP(I2C, DISABLE);

I2C_ClearFlag(I2C, I2C_FLAG_STOPF);

/* Send STOP condition */

I2C_GenerateSTOP(I2C, ENABLE);

while(I2C_GetFlagStatus(I2C, I2C_FLAG_STOPF)); // stop bit flag