AnsweredAssumed Answered

I2C master-slave communication problem

Question asked by Tichenko.Anthony on Dec 20, 2016
Latest reply on Dec 21, 2016 by waclawek.jan

Hello,

I have got a problem with I2C communication between two STM32F0 MCUs. One MCU is I2C master and the other is I2C slave. Initialisation seems to be OK, however after the first 8bits are being transferred, there appears to be no acknowledgement from the slave and the communication ends with a NACK flag. 

In the attachment there is a screenshot from the logic analyser. The transmitting code goes through to the point of enabling the START bit, however after than TC bit never comes up, instead error occurs. Hence, there is a long delay before the next communication attempt. If I comment out the delay then the communication will look like the second screenshot. That is just the address looped infinitely, no data. 

On receiver end, the relay does not switch of course as there is no data. However, if the toggling function is moved outside of if (temp == 0x02) condition, then it does switch, but only once , indicating that the RXDR is not empty. It should however switch on and off with a one second delay in a loop, but as I said it switches only once. 

Hope my description of the problem is understandable,

Many Thanks!

 

I2C master code:

 

int main (void)
{

   I2C_master_init();

   while( 1 )

   {
         I2C_Write(0x10, 0x02); // address, data

         if (I2C_error == 1)

         {
            delay(60000);
            I2C_error = 0;
         }

    }

}

 

void I2C_master_init (void)
{
   SystemCoreClock = HSI_VALUE; // 8MHz

   RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
   RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
   RCC->CFGR3 |= RCC_CFGR3_I2C1SW; // I2C1 clock source SYSCLK (8MHz)

// setup alternate functions
   GPIOB->AFR[ I2C_SDA / 8 ] |= 1 << ( ( I2C_SDA % 8 ) * 4 ); // AF1 mode ( I2C1_SDA )
   GPIOB->AFR[ I2C_SCL / 8 ] |= 1 << ( ( I2C_SCL % 8 ) * 4 ); // AF1 mode ( I2C1_SCL )

// setup port pin modes
// default port settings: push/pull output, 2MHz speed, no pullup/pulldown (external pullup used)
   GPIOB->OTYPER |=
   ( OC_OUT << I2C_SCL ) |
   ( OC_OUT << I2C_SDA );

   GPIOB->MODER |=
   ( AF_MODE << ( I2C_SCL * 2 ) ) |
   ( AF_MODE << ( I2C_SDA * 2 ) );

// I2C1->OAR1 |= (1<<15); // enable OAR1
// I2C1->OAR1 |= (0x01<<1); // set own address

// setup I2C for 100kHz with digital filter of 0.25us ( 2 / 8 MHz )
   I2C1->CR1 = I2C_CR1_ANFOFF | ( ( 2 << 8 ) & I2C_CR1_DFN ) | I2C_CR1_TCIE | I2C_CR1_TXIE |    I2C_CR1_NACKIE;
   I2C1->TIMINGR =
         ( ( 0x01 << 28 ) & I2C_TIMINGR_PRESC ) |
         ( ( 0x00 << 20 ) & I2C_TIMINGR_SCLDEL ) |
         ( ( 0x05 << 16 ) & I2C_TIMINGR_SDADEL ) |
         ( ( 0x10 << 8 ) & I2C_TIMINGR_SCLH ) |
         ( ( 0x0F << 0 ) & I2C_TIMINGR_SCLL );
   I2C1->CR1 |= I2C_CR1_PE; // enable I2C

   GPIOB->MODER |= (1<<(PIN_STB * 2)); // enable main transformer
   GPIOB->ODR |= (1<<PIN_STB); // exit standby
}


ErrorStatus I2C_Write (uint8_t address, uint8_t data)
{
   while( I2C1->ISR & I2C_ISR_BUSY ){;} // wait if busy
   I2C1->CR1 |= I2C_CR1_SWRST; // reset I2C

   I2C1->CR2 = I2C_CR2_RELOAD | (uint32_t) (1 << 16) | (uint32_t) (address << 1);

   if (( I2C1->ISR & I2C_ISR_TXE ) == I2C_ISR_TXE ) // make sure TX register is empty
      {
         I2C1->TXDR = data;
         I2C1->CR2 |= I2C_CR2_START;
      }

while( !( I2C1->ISR & I2C_ISR_TC ) )
   {
      if( I2C1->ISR & ( I2C_ISR_NACKF ) )
         {
            I2C_error = 1;
            I2C1->ICR |= I2C_ICR_NACKCF;
            return ERROR;
         }
   }

 

   while(!(I2C1->ISR & I2C_ISR_STOPF)){;}
   I2C1->ICR = I2C_ICR_STOPCF;

   return SUCCESS;

}

 

void delay( uint32_t cycles )
   {
      while( --cycles );
   }

 

I2C slave code:

 

int main (void)
{
   uint8_t temp = 0;


   SystemCoreClock = HSI_VALUE; // 8MHz

   RCC->AHBENR |= RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOCEN;
   RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
   RCC->CFGR3 |= RCC_CFGR3_I2C1SW; // I2C1 clock source SYSCLK (8MHz)

// setup alternate functions
   GPIOB->AFR[ I2C_SDA / 8 ] |= 1 << ( ( I2C_SDA % 8 ) * 4 ); // AF1 mode ( I2C1_SDA )
   GPIOB->AFR[ I2C_SCL / 8 ] |= 1 << ( ( I2C_SCL % 8 ) * 4 ); // AF1 mode ( I2C1_SCL )

// setup port pin modes
// default port settings: push/pull output, 2MHz speed, no pullup/pulldown (external pullup used)
   GPIOB->OTYPER |=
      ( OC_OUT << I2C_SCL ) |
      ( OC_OUT << I2C_SDA );

   GPIOB->MODER |=
      ( AF_MODE << ( I2C_SCL * 2 ) ) |
      ( AF_MODE << ( I2C_SDA * 2 ) );

   I2C1->OAR1 |= (1<<15); // enable OAR1
   I2C1->OAR1 |= (0x10<<1); // set own address

// setup I2C for 100kHz with digital filter of 0.25us ( 2 / 8 MHz )
   I2C1->CR1 = I2C_CR1_ANFOFF | ( ( 2 << 8 ) & I2C_CR1_DFN ) | I2C_CR1_RXIE;
   I2C1->TIMINGR =
      ( ( 0x01 << 28 ) & I2C_TIMINGR_PRESC ) |
      ( ( 0x00 << 20 ) & I2C_TIMINGR_SCLDEL ) |
      ( ( 0x05 << 16 ) & I2C_TIMINGR_SDADEL ) |
      ( ( 0x10 << 8 ) & I2C_TIMINGR_SCLH ) |
      ( ( 0x0F << 0 ) & I2C_TIMINGR_SCLL );
   I2C1->CR1 |= I2C_CR1_PE; // enable I2C

   GPIOC->MODER |= (1<<0); // enable relay

   while (1)
        {
            if (I2C1->ISR & I2C_ISR_RXNE)
               {
                  temp = I2C1->RXDR;

                  if (temp == 0x02)

                     {
                        GPIOC->ODR ^= (1<<0); // toggle the relay
                        delay (2000000); // 1s delay

                     }
               }
         }
}


void delay( uint32_t cycles )
   {
      while( --cycles ); // 1ms = 2000 cycles @ 8MHz
   }

Outcomes