cancel
Showing results for 
Search instead for 
Did you mean: 

Trying to use Repeated Start I2C on STM32F103RCT6 but the MCU doesn't send a NACK at the end.

PPiro.1
Associate II

I am trying to communicate with a commonly used Accelerometer form NXP (MMA8451) which uses repeated start sequence over I2C protocol, which should look like this:

0693W0000000AzvQAE.png

My problem is that the MCU doesn't seem to send a NACK at the end of transmission and may cause the communication to fail.

I am using I2C to DMA to read data form the accelerator and maybe the DMA has something to do with it?

This is what I am reading using a logic analyzer

0693W0000000B0eQAE.png

and here is my code:

byte i2cBuff[8];
 
#define MMA845_ADDRESS      0x1C
#define MMA8451_REG_WHOAMI  0x1A
 
uint8_t MMA8451_readRegister(uint8_t reg) {
 
	i2cBuff[0] = reg;
	I2C_WriteOneByte(MMA845_ADDRESS,1,&i2cBuff,I2C_NO_STOP_CONDITION);
	i2cBuff[1] = I2C_DMA_ReceiveData(MMA845_ADDRESS,1,I2C_START_CONDITION_YES);
    return i2cBuff[1];
}
 
#include "common.h"
 
void I2C1_Init(void){
 
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
 
  I2C2->CR2 |=  24;   // APB1 Bus frequency in MHz
 
  /* Set speed of 100kHz by
   * this number is a division of I2C period(1/88kHz = 10us) by 2
   * later divided by APB1 Bus period (1/24MHz = 41.6ns), which comes up to 135
   * 1/88000kHz   /
   * ----------   / 1/24000000  = 135
   *    2      /                          */
  I2C1->CCR = 135;  // speed 88kHz (standard mode,less than max due to Silicon limitations pointed out in Errata sheet PDF, higher freq avail in fast mode)
 
  I2C1->TRISE = 15; //25; // (300ns / "CR2 period" (1/24MHz)) + 1 = TRISE
 
  I2C1->CR1 |= I2C_CR1_ACK; // Enable ACKs
  I2C1->CR1 |= I2C_CR1_POS;
  // stretch mode enabled by default, nothing to do here
 
  // 7 bit addressing mode enabled by default, nothing to do here
 
  /* GPIO Init */
  GPIO_InitTypeDef I2CPinsInit;
 
  I2CPinsInit.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  I2CPinsInit.Mode = GPIO_MODE_AF_OD;
  I2CPinsInit.Speed = GPIO_SPEED_FREQ_HIGH;
  I2CPinsInit.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB,&I2CPinsInit);
 
  I2C1->CR1 |= I2C_CR1_PE; // Enable the peripheral, needs to be done last
}
 
void I2C_WriteOneByte(uint8_t device_address,uint8_t len,uint8_t *data,i2c_condition_enum stop){
  uint16_t temp;
 
  I2C1->CR1 |= I2C_CR1_START; // generate start condition
 
  while(!(I2C1->SR1 & I2C_SR1_SB)){
    //do nothing until status register reads that Start condition was sent
  }
 
  I2C1->DR = (device_address << 1); // send 7 bit address shifted one bit to left to keep least significant bit for a read/write bit
 
 
  while(!(I2C1->SR1 & I2C_SR1_ADDR)){
    //do nothing until status register reads that there was a match for the address
  }
 
  temp = I2C1->SR2; //reading the Status register to clear a flag in SR1 (both SR1 and SR2 need to be read so this tricks it, to clear hardware flag)
  for (int i = 0; i < len; ++i) {
 
    I2C1->DR = data[i]; // data to be written in given address
 
    while(!(I2C1->SR1 & I2C_SR1_TXE)){
      // do nothing until the transfer is complete
    }
    len--;
  }
 
  if(stop == I2C_START_CONDITION_YES){
    I2C1->CR1 |= I2C_CR1_STOP; // send STOP condition
  }
}
 
uint8_t I2C_DMA_ReceiveData(uint8_t device_address,uint8_t len,i2c_condition_enum start){
  uint16_t temp;
  uint8_t data;
  //DMA1 Request 7 for I2C1 RX
  RCC->AHBENR |= RCC_AHBENR_DMA1EN;
  I2C1->CR2 |= I2C_CR2_DMAEN;
  I2C1->CR1 |= I2C_CR1_ACK;
  DMA1_Channel7->CMAR = (&data);
  DMA1_Channel7->CPAR = (uint32_t)&I2C1->DR;
  DMA1_Channel7->CNDTR = len;
  DMA1_Channel7->CCR = DMA_CCR_TCIE | DMA_CCR_MINC | DMA_CCR_EN;
 
  if(start == I2C_START_CONDITION_YES){
    I2C1->CR1 |= I2C_CR1_START;
 
      while(!(I2C1->SR1 & I2C_SR1_SB)){
      //do nothing until status register reads that Start condition was sent
      }
  }
 
 
  I2C1->DR = (device_address << 1) + 1; // send 7 bit address shifted one bit to left to keep least significant bit for a read/write bit, and add 1 for the read bit
 
  while(!(I2C1->SR1 & I2C_SR1_ADDR)){
    //do nothing until status register reads that there was a match for the address
  }
 
  temp = I2C1->SR2; //reading the Status register to clear a flag in SR1 (both SR1 and SR2 need to be read so this tricks it, to clear flag)
 
//  HAL_Delay(2);
  while((DMA1->ISR & DMA_ISR_TCIF7) == 0){
    //Do nothing until the DMA transfer is complete
  }
  DMA1_Channel7 ->CCR &= ~(DMA_CCR_EN);
  I2C1->CR1 |= I2C_CR1_STOP;
 
  
  return data;
}
/**************************************************************************/
/*!
    @brief  Setups the HW (reads coefficients values, etc.)
*/
void main(void) {
 
I2C1_Init();
 
while(1){
  HAL_delay(10);
  MMA8451_readRegister(MMA8451_REG_WHOAMI);
  }
 
}

While there doesn't seem to be a problem, the MMA8451_readRegister() function is never allowed to repeat because it is stuck waiting for a

while(!(I2C1->SR1 & I2C_SR1_SB)) condition.

I don't know what am I doing wrong, when I use stops after starts the condition clears and the MMA8451_readRegister() is allowed to repeat.

I need to use repeated start to read the data from chip, but the flags don't clear.

3 REPLIES 3
PPiro.1
Associate II

Here is what I am reading with a logic analyzer without the repeated start and when I send Stop after Start

0693W0000000B6XQAU.pngThe while(!(I2C1->SR1 & I2C_SR1_SB)) condition clears and lets the MMA8451_readRegister() to be repeated infinitely every 10 ms

PPiro.1
Associate II

Thanks for your answers lol,

for those who may run into the same problem, in order to enable NACKs at the end of Receiving data form I2C via DMA, the Bit 12 in register I2C_CR2 must be SET to 1 and I believe that the DMA has to receive at least two bytes.

Where do you put code to set bit 12 of I2C_CR2 to 1?

before I2C1->CR1 |= I2C_CR1_STOP?

Can you share the working I2C read function?

Thx.