cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F10xxx - I2C master receiving 2 bytes - NACK after first read byte

marcobascetta9
Associate II
Posted on November 09, 2015 at 14:59

Hello,

I'm trying to write an I2C application that should read/write one or more bytes as master with polling mode. I'm able to write one ore more bytes and read one byte, but when I try to read 2 bytes after a single byte read the first call goes wrong; next 2 bytes read works! I'm try to explain better:
  • Perform a read of a byte.
    • Start bit -> Send address 7bit (W) -> (receive ACK) -> Write register (1byte) -> (receive ACK) -> Stop
    • Start bit -> Send address 7bit (R) -> (receive ACK) -> (Read value (1byte)) -> send NACK -> Stop
  • Perform a read of two bytes. (This FAIL)
    • Start bit -> Send address 7bit (W) -> (receive ACK) -> Write register (1byte) -> (receive ACK) -> Stop
    • Start bit -> Send address 7bit (R) -> (receive ACK) -> (Read value (1byte)) ->

      send NACK (WRONG) -> (Read value (1byte) => WRONG)

      -> send NACK -> Stop
  • Perform a read of two bytes. (Same request now works)
    • Start bit -> Send address 7bit (W) -> (receive ACK) -> Write register (1byte) -> (receive ACK) -> Stop
    • Start bit -> Send address 7bit (R) -> (receive ACK) -> (Read value (1byte)) ->

      send ACK (OK) -> (Read value (1byte))

      -> send NACK -> Stop
In order to write my code I use RM0008, AN2824, and I2CRoutines.c example retrieved from STM32F10x_AN2824_FW_V4.0.0 package. In those documents and in FAQ #87 ( https://my.st.com/public/Faq/Lists/faqlst/DispForm.aspx?ID=87 ) the 2 bytes read sequence has little differences. In RM0008 like in AN2824, Page 6:
  • Send START
  • Send ADDR
  • Set POS and ACK

  • Wait ADDR flag

  • Clear ADDR and ACK
  • Wait BTF flag
  • Program STOP
  • Read DR twice
In AN2824, Page 8, Figure 2:
  • Send START
  • Send ADDR
  • Wait ADDR flag

  • Set POS

  • Disable interrupts
  • Clear ADDR and ACK
  • Enable interrupts
  • Wait BTF flag
  • Disable interrupts
  • Program STOP
  • Read DR (byte 1)
  • Enable interrupts
  • Read DR (byte 2)
  • Wait STOP flag is cleared
  • Reset POS
  • Set ACK
My code for two bytes read could be synthesized as:


I2Cx->CR2 |= I2C_IT_ERR;


I2C_GenerateSTART(I2Cx, ENABLE);

WAIT_FOR_CONDITION_OR_RESET_ON_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB) == SET);


I2C_Send7bitAddress(I2Cx, (uint8_t)(i2c_addr << 
1
), dir);

WAIT_FOR_CONDITION_OR_RESET_ON_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR) == SET);


/* Clear ACK bit */

I2C_AcknowledgeConfig(I2Cx, DISABLE);

/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3

software sequence must complete before the current byte end of transfer */

__disable_irq();

/* Clear ADDR flag */

CLEAR_REGISTER(I2Cx->SR1); //TODO:BASCETTA:TEST: Aggiunto da me

CLEAR_REGISTER(I2Cx->SR2);

/* Program the STOP */

I2C_GenerateSTOP(I2Cx, ENABLE);

/* Re-enable IRQs */

__enable_irq();



WAIT_FOR_CONDITION_OR_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BTF) == SET);


/* Disable IRQs around STOP programming and data reading because of the limitation ?*/

__disable_irq();

/* Program the STOP */

I2C_GenerateSTOP(I2Cx, ENABLE);

/* Read first data */

bytes[i] = I2C_ReceiveData(I2Cx);

i++;

/* Re-enable IRQs */

__enable_irq();


/* Read second data */

bytes[i] = I2C_ReceiveData(I2Cx);

i++;


/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */

//WAIT_FOR_CONDITION_OR_TIMEOUT((I2Cx->CR1 & I2C_CR1_STOP) == I2C_CR1_STOP);

WAIT_FOR_CONDITION_OR_TIMEOUT((I2Cx->CR1 & I2C_CR1_STOP) == 0);

/* Enable Acknowledgement to be ready for another reception */

I2C_AcknowledgeConfig(I2Cx, ENABLE);

/* Clear POS bit */

I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

Could you help me to find my mistake?? Thank you for your replies, Regards #i2c #master-receiving #stm32
1 REPLY 1
marcobascetta9
Associate II
Posted on November 11, 2015 at 18:31

After many tries this is a synthesizedversion of my solution

I2C_GenerateSTART(I2Cx, ENABLE);
WAIT_FOR_CONDITION_OR_RESET_ON_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB) == SET);
I2C_Send7bitAddress(I2Cx, (uint8_t)(i2c_addr <<

1

), dir);
WAIT_FOR_CONDITION_OR_RESET_ON_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR) == SET);
switch( read_bytes ) {
case 0: //DO NOTHING
CLEAR_REGISTER(I2Cx->SR1);
CLEAR_REGISTER(I2Cx->SR2);
break;
case 1:
/* Clear ACK bit */
I2C_AcknowledgeConfig(I2Cx, DISABLE);
/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
software sequence must complete before the current byte end of transfer */
__disable_irq();
/* Clear ADDR flag */
CLEAR_REGISTER(I2Cx->SR1);
CLEAR_REGISTER(I2Cx->SR2);
/* Program the STOP */
I2C_GenerateSTOP(I2Cx, ENABLE);
/* Re-enable IRQs */
__enable_irq();
WAIT_FOR_CONDITION_OR_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_RXNE) == SET);
bytes[i] = I2C_ReceiveData(I2Cx);
WAIT_FOR_CONDITION_OR_TIMEOUT((I2Cx->CR1 & I2C_CR1_STOP) == 0);
I2C_AcknowledgeConfig(I2Cx, ENABLE);
break;
case 2:
/* EV6_1: The acknowledge disable should be done just after EV6,
that is after ADDR is cleared, so disable all active IRQs around ADDR clearing and
ACK clearing */
// Vedi ''AN2824''@16
I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Next);
__disable_irq();
/* Clear ADDR by reading SR2 register */
CLEAR_REGISTER(I2Cx->SR1);
CLEAR_REGISTER(I2Cx->SR2);
/* Clear ACK bit */
I2C_AcknowledgeConfig(I2Cx, DISABLE);
/*Re-enable IRQs */
__enable_irq();
WAIT_FOR_CONDITION_OR_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BTF) == SET);
/* Disable IRQs around STOP programming and data reading because of the limitation ?*/
__disable_irq();
/* Program the STOP */
I2C_GenerateSTOP(I2Cx, ENABLE);
/* Read first data */
bytes[i] = I2C_ReceiveData(I2Cx);
i++;
/* Re-enable IRQs */
__enable_irq();
/* Read second data */
bytes[i] = I2C_ReceiveData(I2Cx);
i++;
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
WAIT_FOR_CONDITION_OR_TIMEOUT((I2Cx->CR1 & I2C_CR1_STOP) == 0);
/* Enable Acknowledgement to be ready for another reception */
I2C_AcknowledgeConfig(I2Cx, ENABLE);
/* Clear POS bit */
I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);
break;
default: { //Read 3+ bytes
CLEAR_REGISTER(I2Cx->SR1);
CLEAR_REGISTER(I2Cx->SR2);
uint16_t remain = size;
// Scorro tutti i byte fino a rimanere a 3
for (i = 0, remain = size; remain > 3; remain--, i++) {
WAIT_FOR_CONDITION_OR_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BTF) == SET);
bytes[i] = I2C_ReceiveData(I2Cx);
}
// Qui certamente sono rimasto con 3 byte da leggere.
WAIT_FOR_CONDITION_OR_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BTF) == SET);
/* Clear ACK bit */
I2C_AcknowledgeConfig(I2Cx, DISABLE);
/* Disable IRQs around data reading and STOP programming because of the limitation ? */
__disable_irq();
/* Read Data N-2 */
bytes[i] = I2C_ReceiveData(I2Cx);
i++;
/* Program the STOP */
I2C_GenerateSTOP(I2Cx, ENABLE);
/* Read DataN-1 */
bytes[i] = I2C_ReceiveData(I2Cx);
i++;
/* Re-enable IRQs */
__enable_irq();
/* Wait until RXNE is set (DR contains the last data) */
WAIT_FOR_CONDITION_OR_TIMEOUT(I2C_GetFlagStatus(I2Cx, I2C_FLAG_RXNE) == SET);
/* Read DataN */
bytes[i] = I2C_ReceiveData(I2Cx);
i++;
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
WAIT_FOR_CONDITION_OR_TIMEOUT((I2Cx->CR1 & I2C_CR1_STOP) == 0);
/* Enable Acknowledgement to be ready for another reception */
I2C_AcknowledgeConfig(I2Cx, ENABLE);
break;
}
}