cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407VG ( F4 discovery board) I2C issue

Bogdan
Senior
Posted on May 12, 2014 at 00:20

Hi guys, i have a problem regarding comunicating with a sensor via i2c.

This is my first atempt to use i2c comunication on a ARM piece My code for the sake of time saving is this one bellow( original from https://github.com/Torrentula/STM32F4-examples/blob/master/I2C%20Master/main.c) Beside this code, i tryied other libraries with the same effect. My sensor is hooked on PB8 for SCL, and PB9 for SDA. I connected a logic analyzer to see whats going on, and the lines are silent... Tryied to do some improvisations, i for example i did a loop of only these two: while(1) {

I2Cx->CR1 |= I2C_CR1_START;

I2Cx->CR1 |= I2C_CR1_STOP; } And from above i can see on the i2c lines that i have a start and a stop signal, wich it indicates for me that the GPIO is configure right. Using this code bellow, and like i said before tryied other i2c libs, the result is i cant send any data, for the moment i tryied only to see that the sending data is ok, but its not... there is nothing happeling on the bus lines. As IDE i use IAR ewarm 6. Any ideas whats wrong? I got no errors on the compilator, or debugger

#include <
stm32f4xx.h
>
#include <
stm32f4xx_i2c.h
>
#include <
stm32f4xx_i2c.c
>
#include ''stm32f4xx_gpio.c''
#include ''stm32f4xx_rcc.c''
#define SLAVE_ADDRESS 0x3D // the slave address (example)
void init_I2C1(void){
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// enable APB1 peripheral clock for I2C1
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// enable clock for SCL and SDA pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* setup SCL and SDA pins
* You can connect I2C1 to two different
* pairs of pins:
* 1. SCL on PB6 and SDA on PB7
* 2. SCL on PB8 and SDA on PB9
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; // we are going to use PB6 and PB7
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // set pins to alternate function
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // set GPIO speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // set output to open drain --> the line has to be only pulled low, not driven high
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // enable pull up resistors
GPIO_Init(GPIOB, &GPIO_InitStruct); // init GPIOB
// Connect I2C1 pins to AF
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); // SCL
GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1); // SDA
// configure I2C1
I2C_InitStruct.I2C_ClockSpeed = 100000; // 100kHz
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; // I2C mode
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 50% duty cycle --> standard
I2C_InitStruct.I2C_OwnAddress1 = 0x00; // own address, not relevant in master mode
I2C_InitStruct.I2C_Ack = I2C_Ack_Disable; // disable acknowledge when reading (can be changed later on)
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // set address length to 7 bit addresses
I2C_Init(I2C1, &I2C_InitStruct); // init I2C1
// enable I2C1
I2C_Cmd(I2C1, ENABLE);
}
/* This function issues a start condition and
* transmits the slave address + R/W bit
*
* Parameters:
* I2Cx --> the I2C peripheral e.g. I2C1
* address --> the 7 bit slave address
* direction --> the tranmission direction can be:
* I2C_Direction_Tranmitter for Master transmitter mode
* I2C_Direction_Receiver for Master receiver
*/
void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){
// wait until I2C1 is not busy anymore
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
// Send I2C1 START condition
I2C_GenerateSTART(I2Cx, ENABLE);
// wait for I2C1 EV5 --> Slave has acknowledged start condition
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
// Send slave Address for write
I2C_Send7bitAddress(I2Cx, address, direction);
/* wait for I2C1 EV6, check if
* either Slave has acknowledged Master transmitter or
* Master receiver mode, depending on the transmission
* direction
*/
if(direction == I2C_Direction_Transmitter){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
}
else if(direction == I2C_Direction_Receiver){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
}
}
/* This function transmits one byte to the slave device
* Parameters:
* I2Cx --> the I2C peripheral e.g. I2C1
* data --> the data byte to be transmitted
*/
void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{
I2C_SendData(I2Cx, data);
// wait for I2C1 EV8_2 --> byte has been transmitted
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
/* This function reads one byte from the slave device
* and acknowledges the byte (requests another byte)
*/
uint8_t I2C_read_ack(I2C_TypeDef* I2Cx){
// enable acknowledge of recieved data
I2C_AcknowledgeConfig(I2Cx, ENABLE);
// wait until one byte has been received
while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
// read data from I2C data register and return data byte
uint8_t data = I2C_ReceiveData(I2Cx);
return data;
}
/* This function reads one byte from the slave device
* and doesn't acknowledge the recieved data
*/
uint8_t I2C_read_nack(I2C_TypeDef* I2Cx){
// disabe acknowledge of received data
// nack also generates stop condition after last byte received
// see reference manual for more info
I2C_AcknowledgeConfig(I2Cx, DISABLE);
I2C_GenerateSTOP(I2Cx, ENABLE);
// wait until one byte has been received
while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
// read data from I2C data register and return data byte
uint8_t data = I2C_ReceiveData(I2Cx);
return data;
}
/* This funtion issues a stop condition and therefore
* releases the bus
*/
void I2C_stop(I2C_TypeDef* I2Cx){
// Send I2C1 STOP Condition
I2C_GenerateSTOP(I2Cx, ENABLE);
}
int main(void){
init_I2C1(); // initialize I2C peripheral
uint8_t received_data[2];
while(1){
I2C_start(I2C1, SLAVE_ADDRESS<<1, I2C_Direction_Transmitter); // start a transmission in Master transmitter mode
I2C_write(I2C1, 0x20); // write one byte to the slave
I2C_write(I2C1, 0x03); // write another byte to the slave
I2C_stop(I2C1); // stop the transmission
I2C_start(I2C1, SLAVE_ADDRESS<<1, I2C_Direction_Receiver); // start a transmission in Master receiver mode
received_data[0] = I2C_read_ack(I2C1); // read one byte and request another byte
received_data[1] = I2C_read_nack(I2C1); // read one byte and don't request another byte, stop transmission
}
}

8 REPLIES 8
stm322399
Senior
Posted on May 12, 2014 at 09:25

My first advice is to move the STOP generation after receiving data. Normally the second START is enough to generate the restart condition (so many I2C slave works like that).

I'am surprised that your logic analyser does not capture anything, when it had captured your *improvisation* test. What is your software doing while you attempt to observe SCL/SDA lines ? does is run the whole infinite loop, or it is locked somewhere, waiting for an I2C event that does not come ?

Bogdan
Senior
Posted on May 12, 2014 at 12:30

Hi,

the code is only what i shown above, and nothing else, no timers, no interrupts In the mean time i removed the i2c read functions, left out only the write functions, like the code bellow, and stil nothing on my scl/sda lines. So far as i read on the internet is this i2c giving users headackes, but i cant explain where i am worng, did everything by the book... the wires are all corectly. PB8 for SCL and PB9 for SDA like declared in init routine, GPIOB and I2C1 clocks enabled. Is there a specified setup order?

int main(void){
SystemInit(); ///// Initialize System Clocks and PLLs
init_I2C1(); // initialize I2C peripheral
while(1){
I2C_start(I2C1, SLAVE_ADDRESS<<1, I2C_Direction_Transmitter); // start a transmission in Master transmitter mode
I2C_write(I2C1, 0x20); // write one byte to the slave
I2C_write(I2C1, 0x03); // write another byte to the slave
I2C_stop(I2C1); // stop the transmission
}
}

chen
Associate II
Posted on May 12, 2014 at 12:37

Hi

Does the circuit have the pull up resistors for the I2C lines?

stm322399
Senior
Posted on May 12, 2014 at 13:50

Run your program and break it. Where it is ? Running the whole loop or locked into a 'while(event...)' ?

What do you observe on the lines ? Are they high/low ?

Can you confirm that simply removing both writes (keep only start and stop) that you can see SDA/SCL to change (attach a capture if yo can do one).

Bogdan
Senior
Posted on May 18, 2014 at 01:27

Hi guys, sorry for my late response, i was a bit out of town.

In the end i managed to make the i2c work a little, and since i am new to IAR ewarm enviorment i did not discover the full power of its debugger.

So far now, my code looks like this, trying to debug, the debug tells me that its continously in the first event master mode select check

while(1){

I2Cx->CR1 |= I2C_CR1_START; /* Start bit = 1 (generate start contidion) */

while(! (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) ); /* check BUSY, MSL and SB flag */ // EV 5   looked in the header files it shows me that the I2C_Event_Master_Mode_Select is defined as:   ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */   That means it checks the SR1 flags? Coz 30001 in hex would mean that are the bits 0 and 16 and 17 wich has nothing to do with busy,msl or sb flags. Can some one explain me what exactly that event monitors?   I`m afraid is something wrong in st`s librarys, some help would be apriciate.  Thanks

while(1){

I2Cx->CR1 |= I2C_CR1_START; /* Start bit = 1 (generate start contidion) */

while(! (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) ); /* check BUSY, MSL and SB flag */ // EV 5

I2C_Send7bitAddress(I2Cx,0xEE,I2C_Direction_Transmitter);

while( !(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))); // EV 6

I2C_SendData(I2Cx,0xAA);

while( !(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING))); // EV8

I2Cx->CR1 |= I2C_CR1_START; /* Generate restart */

while(! (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) ); /* check BUSY, MSL and SB flag */ // EV 5

I2C_Send7bitAddress(I2Cx,0xEF,I2C_Direction_Receiver);

while( !(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) );

while( !(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED | I2C_FLAG_BTF)) ); // ev 7

I2C_ReceiveData(I2Cx);

// while(!(I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_RECEIVED))); /. EV

I2Cx->CR1 |= I2C_CR1_STOP;/* Generate a STOP condition */

}

francescatodiego
Associate II
Posted on May 18, 2014 at 11:12

I2C_CheckEvent check build DWORD with i2c status regs Upper word SR2 lower word SR1

From I2C library code

.....
 .....
 01168 /* Read the I2Cx status register */
01169 flag1 = I2Cx->SR1;
01170 flag2 = I2Cx->SR2;
01171 flag2 = flag2 << 16;
01172 
01173 /* Get the last event value from I2C status register */
01174 lastevent = (flag1 | flag2) & FLAG_MASK;
01175 
01176 /* Check whether the last event contains the I2C_EVENT */
01177 if ((lastevent & I2C_EVENT) == I2C_EVENT)
 .....
 ..... 

This isn't the case but errata also say In case of a repeated Start, the “Setup time for a repeated Start condition�? (named Tsu;sta in the I²C specification) can be slightly violated when the I²C operates in Master Standard mode at a frequency between 88 kHz and 100 kHz. For this reason I use I2c with 75KHz f requency
Bogdan
Senior
Posted on May 18, 2014 at 11:59

Hi,

The loop is stil locked at this line inside the check event function even after seting 75k baud

But here its a strange thing, if i reset the board, the i2c line is ok ( see my atached photo screen), works at 100k baud and even 400 k baud

And if i try to debug step by step, like said before IAR says its locked in that loop, exactly at the line

flag2 = I2Cx->SR2;

i lowerd the baudrate to 70 000, tryied even to raise`it to 300 000 and stil get that locked inside the loop.

And if i exit the debuger and supose the board is not in reset, the line is quiet only if i perform a reset of the board ( unplug usb cable or press the reset button)

Why is this strange behavior?

________________

Attachments :

i2c.png : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I0UA&d=%2Fa%2F0X0000000bd6%2FoiC6uZ_Wqp_mu92frH4gqWWxNWBxMnXDB00PM8oVJs0&asPdf=false
gusonela
Associate II
Posted on May 19, 2014 at 01:28