cancel
Showing results for 
Search instead for 
Did you mean: 

Problems with I2C register code (STM32G0x1)

SHobb.1
Associate II

I am attempting to write register level code to control the I2C peripheral in an STM32G0x1.

My read function is failing because the RXNE bit is never getting set. I believe I have implemented the state machine described in RM0444 (p. 958). Is there anything obviously wrong in my code?

bool i2c_I2C1_masterReceive(uint8_t addr, uint8_t *pData, uint8_t len, uint32_t timeout)
{
  uint32_t startTicks = rcc_msGetTicks();
 
  uint8_t dataIdx = 0;
 
  //Clear flags
  I2C1->ICR |= (I2C_ICR_STOPCF |I2C_ICR_NACKCF);
 
  I2C1->CR2 &= ~I2C_CR2_SADD;                         //Clear addr bits
  I2C1->CR2 |= ((addr | 0x01) << I2C_CR2_SADD_Pos);   //Set read bit
  I2C1->CR2 &= ~(I2C_CR2_NBYTES);
  I2C1->CR2 |= (uint32_t)len << I2C_CR2_NBYTES_Pos;
 
  I2C1->CR2 |= I2C_CR2_START;   //Send Start condition (automatically waits for BUSY flag)
  while(!(I2C1->ISR & I2C_ISR_BUSY))  //Wait for start condition to be recognized
  {
    //Handle timeout
    if(rcc_msGetTicks()-startTicks > timeout)
    {
      gpio_LED_writeRed(LED_ON);
      return(false);
    }
  }
 
  if(I2C1->ISR & I2C_ISR_NACKF)
  {
    //Clear NACKF
    I2C1->ICR |= I2C_ICR_NACKCF;
    printf("Received nack\r\n");
    return(false);
  }
 
  while(dataIdx < len)
  {
    while(!((I2C1->ISR & I2C_ISR_RXNE) == I2C_ISR_RXNE))
    {
      //Handle timeout -- THIS IS ALWAYS OCCURRING (RXNE NEVER GETS SET)
      if(rcc_msGetTicks()-startTicks > timeout)
      {
        gpio_LED_writeOrange(LED_ON);
        return(false);
      }
    }
    pData[dataIdx] = I2C1->RXDR;
    dataIdx++;
  }
 
  if(!(I2C1->ISR & I2C_ISR_TC))
  {
    gpio_LED_writeGreen(LED_ON);
    return(false);
  }
 
  //Generate Stop condition
  I2C1->CR2 |= I2C_CR2_STOP;
  //Wait for I2C not busy
  while((I2C1->ISR & I2C_ISR_BUSY))
  {
    if(rcc_msGetTicks() - startTicks > timeout)
    {
      //printf("I2C Timeout...\r\n");
      gpio_LED_writeBlue(LED_ON);
      return(false);
    }
  }
 
  return(true);
}

In case it matters, the my configuration code:

/**
 * @brief I2C GPIO pins configuration
 */
void i2c_I2C1_GPIO_config(void)
{
  //SCL: PB6, SDA: PB7
  RCC->IOPENR |= RCC_IOPENR_GPIOBEN;  ///Enable Port B clock
 
  //Set to AF Mode
  GPIOB->MODER &= ~(GPIO_MODER_MODE7 | GPIO_MODER_MODE6);
  GPIOB->MODER |= (GPIO_MODER_MODE7_1 | GPIO_MODER_MODE6_1);
  GPIOB->OTYPER |= (GPIO_OTYPER_OT7 | GPIO_OTYPER_OT6); //Output open-drain
  //Set to high speed
  GPIOB->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED7 | GPIO_OSPEEDR_OSPEED6);
  GPIOB->OSPEEDR |= (GPIO_OSPEEDR_OSPEED7_1 | GPIO_OSPEEDR_OSPEED6_1);
  //Set to AF6 (both PB6 and PB7)
  GPIOB->AFR[0] &= ~(GPIO_AFRL_AFSEL7 | GPIO_AFRL_AFSEL6);
  GPIOB->AFR[0] |= (GPIO_AFRL_AFSEL7_2 | GPIO_AFRL_AFSEL7_1 | GPIO_AFRL_AFSEL6_2 | GPIO_AFRL_AFSEL6_1);
}
 
/**
 * @brief I2C Peripheral configuration
 */
void i2c_I2C1_config(void)
{
  RCC->APBENR1 |= RCC_APBENR1_I2C1EN;               //Enable I2C Clock
 
  I2C1->CR1 &= ~I2C_CR1_PE;                         //Make sure peripheral is disabled
  I2C1->CR1 &= ~(I2C_CR1_ANFOFF | I2C_CR1_DNF);     //Disable Analog and Digital Filters
  I2C1->CR1 |= (3UL << I2C_CR1_DNF_Pos);            //Enable digital filter for 3*Ti2cclk
  //Configure timing
  I2C1->TIMINGR = 0;                                //Reset
  I2C1->TIMINGR |= I2C_TIMINGR_PRESC;               //tpresc = (0xF + 1)*(1/64MHz) = 250ns
  I2C1->TIMINGR |= (4UL << I2C_TIMINGR_SCLDEL_Pos); //tscldel = (4+1)*250ns = 1250ns
  I2C1->TIMINGR |= (2UL << I2C_TIMINGR_SDADEL_Pos); //tsdadel = (2)*250ns = 500ns
  I2C1->TIMINGR |= (15UL << I2C_TIMINGR_SCLH_Pos);  //tsclh = (15+1)*250ns = 4us
  I2C1->TIMINGR |= (19UL << I2C_TIMINGR_SCLL_Pos);  //tscll = (19+1)*250ns = 5us
 
  I2C1->CR1 &= ~I2C_CR1_NOSTRETCH;                  //Keep clear in master mode
 
  I2C1->CR2 &= ~I2C_CR2_ADD10;                      //Configure for 7 bit address mode
 
  I2C1->CR1 |= I2C_CR1_PE;                          //Enable I2C peripheral
}

And my transmit code. Note, this is completing "successfully" (returns true), but I can't confirm the writes are actually happening until I get reads working (i.e., trying to write a register in an external component).

/**
 * @brief I2C Transmit (Master)
 */
bool i2c_I2C1_masterTransmit(uint8_t addr, uint8_t *pData, uint8_t len, uint32_t timeout)
{
  uint32_t startTicks = rcc_msGetTicks();
 
  uint8_t dataIdx = 0;
 
  //Clear flags
  I2C1->ICR |= (I2C_ICR_STOPCF |I2C_ICR_NACKCF);
 
  I2C1->CR2 &= ~I2C_CR2_SADD;                       //Clear addr bits
  I2C1->CR2 |= (addr << I2C_CR2_SADD_Pos);          //SADD[7:1] need to have the addr.  SADD[0] is R/W
  I2C1->CR2 &= ~(I2C_CR2_NBYTES);
  I2C1->CR2 |= (uint32_t)len << I2C_CR2_NBYTES_Pos;
 
  I2C1->CR2 |= I2C_CR2_START;         //Send Start condition (automatically waits for BUSY flag)
  while(!(I2C1->ISR & I2C_ISR_BUSY))  //Wait for start condition to be recognized
  {
    //Handle timeout
    if(rcc_msGetTicks()-startTicks > timeout)
    {
      gpio_LED_writeRed(LED_ON);
      return(false);
    }
  }
 
  if(I2C1->ISR & I2C_ISR_NACKF)
  {
    //Clear NACKF
    I2C1->ICR |= I2C_ICR_NACKCF;
    printf("Received nack\r\n");
    return(false);
  }
 
  while(dataIdx < len)
  {
    if((I2C1->ISR & (I2C_ISR_TXIS | I2C_ISR_TXE)))
    {
      I2C1->TXDR = pData[dataIdx];
      dataIdx++;
    }
 
    //Handle timeout
    if(rcc_msGetTicks()-startTicks > timeout)
    {
      gpio_LED_writeOrange(LED_ON);
      return(false);
    }
  }
 
  while(!(I2C1->ISR & I2C_ISR_TC))
  {
    //Handle timeout
    if(rcc_msGetTicks()-startTicks > timeout)
    {
      gpio_LED_writeGreen(LED_ON);
      return(false);
    }
  }
 
  //Generate Stop condition
  I2C1->CR2 |= I2C_CR2_STOP;
  //Wait for I2C not busy
  while((I2C1->ISR & I2C_ISR_BUSY))
  {
    if(rcc_msGetTicks() - startTicks > timeout)
    {
      //printf("I2C Timeout...\r\n");
      gpio_LED_writeBlue(LED_ON);
      return(false);
    }
  }
 
  return(true);
}

Note: I realize this code isn't efficient (I should combine multiple RMW operations to the same registers). I have it this way right now for clarity and because I am still learning the registers.

4 REPLIES 4

The I2C is notoriously ​quirky. Show waveforms on the bus. Read out and post registers content.

JW​

CErle.1
Associate II

Everything looks alright on the scope? SCL & SDA timing?

SDA sends what it should, slave really ACKs?

And STM32 ACKs in case slave sends?

Maybe it's the timeout?

I can't remember right now, maybe the enable bit in CR1 must be set after setting the address in CR2?

SHobb.1
Associate II

Thanks for the responses.

I do not have access to a scope today. I'll take some measurements next week and post...

@Community member​ : Can you explain the middle part of your comment: "And STM32 ACKs in case slave sends?" I don't expect my slave to send anything unless I send a Read command. But I don't know if I should be setting a bit to send Acks...

You're right, that master ACK comment might not really make sense.

If your STM32 master sends a read command, check if the slave acknowledges it (some NACK bit in ISR? Still don't have the manual here...).

If it doesn't the STM will probably not throw out any more SCL clocks and you won't get any data and RXNE will stay low.

But first of all, get a scope and check what's really going on.