cancel
Showing results for 
Search instead for 
Did you mean: 

I2C problems

tiche79
Associate II
Posted on March 26, 2014 at 16:10

Hi

I'm trying to write and read some data into a EEPROM (AT24MAC402) with my STM32F417 in I2C but the program loops in the START and don't write or read in the EEPROM. There is a status in ''stm32f4xx_i2c.c'' that stays in ERROR and never past in SUCCES.

I think that the Salve Address is correct so I do not know where the problem may be

Thanks for your help

Here is my program:

/**

 ******************************************************************************

 * @file     Test_I2C/main.c

 * @author   DVC-Co

 * @version V1.0.1

 * @date     04-February-2014

 * @brief   Main program body

 ******************************************************************************

 */

/* Includes */

#include ''stm32f4xx.h''

#include ''stm32f4_discovery.h''

#define SLAVE_ADDRESS 0xA0 

/* Private variables */

uint8_t received_data[2];

uint8_t I2C_read_ack(I2C_TypeDef* I2Cx);

uint8_t I2C_read_nack(I2C_TypeDef* I2Cx);

/* Private function prototypes */

void init_I2C1(void);

void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction);

void I2C_write(I2C_TypeDef* I2Cx, uint8_t data);

void I2C_stop(I2C_TypeDef* I2Cx);

/**

**===========================================================================

**

**  Abstract: main program

**

**===========================================================================

*/

int main(void)

{

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

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

}

}

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_6 | GPIO_Pin_7; // 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_PinSource6, GPIO_AF_I2C1); // SCL

GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, 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);

}

void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction)

{

//I2C_ClearFlag(I2Cx, I2C_FLAG_BUSY);

// 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));

He stuck here

}

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);

}

10 REPLIES 10
stm322399
Senior
Posted on March 26, 2014 at 16:24

I'am pretty sure that you don't need to shift the address 1 bit left when you call I2C_start. Your address (0xA0) look to be already shifted (otherwise the additional shift will throw the MSbit out of the argument).

tiche79
Associate II
Posted on March 26, 2014 at 16:38

I removed the shift but It stuck in the function ''I2C_CheckEvent'' and put the status in ERROR.

ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)

{

  uint32_t lastevent = 0;

  uint32_t flag1 = 0, flag2 = 0;

  ErrorStatus status = ERROR;

  /* Check the parameters */

  assert_param(IS_I2C_ALL_PERIPH(I2Cx));

  assert_param(IS_I2C_EVENT(I2C_EVENT));

  /* Read the I2Cx status register */

  flag1 = I2Cx->SR1;

  flag2 = I2Cx->SR2;

  flag2 = flag2 << 16;

  /* Get the last event value from I2C status register */

  lastevent = (flag1 | flag2) & FLAG_MASK;

  /* Check whether the last event contains the I2C_EVENT */

  if ((lastevent & I2C_EVENT) == I2C_EVENT)

  {

    /* SUCCESS: last event is equal to I2C_EVENT */

    status = SUCCESS;

  }

  else

  {

    /* ERROR: last event is different from I2C_EVENT */

   

status = ERROR;

  }

  /* Return status */

  return status;

}

stm322399
Senior
Posted on March 26, 2014 at 16:55

So the I2C controller does not end in the state you expect. Can you tell us the values of SR1 and SR2, so we can check what's going wrong ?

chen
Associate II
Posted on March 27, 2014 at 13:12

If you have access to one :

Look at the I2C waveforms on an Oscilloscope or logic analyser.

tiche79
Associate II
Posted on March 28, 2014 at 09:38

Now the program look like write in the memory because the functions ''I2C_write'' are running but after it remains in the function ''I2C_read_nack(I2C1)'' and SR1 and SR2 are 0

int main(void)

{

init_I2C1(); // initialize I2C peripheral

    while(1)

{

I2C_start(I2C1, SLAVE_ADDRESS, I2C_Direction_Transmitter);  

OK

I2C_write(I2C1, 0x00); 

OK

I2C_write(I2C1, 0x03); 

OK

I2C_stop(I2C1);

OK

I2C_start(I2C1, SLAVE_ADDRESS, I2C_Direction_Receiver); 

OK

received_data[0] = I2C_read_ack(I2C1); 

OK

received_data[1] = I2C_read_nack(I2C1);

Loop here

I2C_stop(I2C1);

}

}

stm322399
Senior
Posted on March 28, 2014 at 10:09

I2C Master reception is not the easiest part to code, and the way you did it does not look the good one to me.

Basically, I2C reception starts as soon as you acknowledged the address (event I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED).

The RM says that you must prepare the I2C controller for reception *before* this step, in particular when you expect to read only 1 or 2 bytes.

In other words, I2C_start is likely to be modified because it behave differently in function of the data direction and the number of bytes to exchange.

Look at the reference manual section 27.3.3, and pay special attention to what it say under ''Master Receiver'' and ''For 2-byte reception''. The procedure is clearly depicted and should work for you.

tiche79
Associate II
Posted on March 28, 2014 at 16:46

Thanks, I will check

tiche79
Associate II
Posted on April 02, 2014 at 16:38

to be migrated, sourceId: 40192:697285D7-A9CA-445D-B16C-F23BF0E3B1A3

stm322399
Senior
Posted on April 02, 2014 at 17:08

I do not have any code sample for a simple example, but I am pretty sure that there exists a lot of them, and maybe from this forum.

Below is the sequence of operations performed by an I2C driver that uses interrupt. To help you using this code without interrupts I put in comment the flag you must wait to go the next step:

I2C->CR1 = I2C_CR1_START | I2C_CR1_PE;
// Wait here for I2C_SR1_SB to be set
I2C_Send7bitAddress(port->i2c, SLAVE_ADDR<<
1
, I2C_Direction_Transmitter);
// Wait here for I2C_SR1_ADDR to be set
I2C_GetFlagStatus(port->i2c, I2C_FLAG_MSL);
// Wait here for I2C_SR1_TXE to be set
I2C_SendData(port->i2c, 0x9A);
I2C->CR1 = I2C_CR1_START | I2C_CR1_PE;
// Wait here for I2C_SR1_SB to be set
I2C_Send7bitAddress(port->i2c, SLAVE_ADDR<<
1
, I2C_Direction_Receiver);
// Wait here for I2C_SR1_ADDR to be set
I2C->CR1 = I2C_CR1_PE | I2C_CR1_ACK;
I2C_GetFlagStatus(port->i2c, I2C_FLAG_MSL);
// Wait here for I2C_SR1_RXNE to be set
rxbyte[0] = I2C_ReceiveData(port->i2c);
// Wait here for I2C_SR1_RXNE to be set
rxbyte[1] = I2C_ReceiveData(port->i2c);
I2C->CR1 = I2C_CR1_PE | I2C_CR1_STOP;
// Wait here for I2C_SR1_RXNE to be set
rxbyte[2] = I2C_ReceiveData(port->i2c);
I2C->CR1 = 0;

Normally you should succeed in reading those 3 bytes from your device. Note that I assumed that your device has an internal auto-incrementing address for reading those bytes.