Electronic – How to setup STM32 as I2C slave device

i2cslavestm32

I've been trying to set up MCU (STM32 L432KC) as an I2C slave device. I have previously tested it as a master device to interface with a port expander(MCP2307) and it works perfectly fine.

The following is the code I am running. The master sends 1,2,3 every 500 ms and the slave device is supposed to a glow a common anode RGB led (rgb led program works fine too, tested as SPI slave device before)

#include "stm32l4xx.h"                  // Device header
 
#define RED_ON (GPIOB->ODR &=~(1<<1))
#define RED_OFF (GPIOB->ODR |=1<<1)
 
#define BLUE_ON (GPIOB->ODR &=~(1<<6))
#define BLUE_OFF (GPIOB->ODR |= 1<<6)
 
#define GREEN_ON (GPIOB->ODR &=~(1<<7))
#define GREEN_OFF (GPIOB->ODR |= 1<<7);
 
 
void i2c1_init(void);
 
 
int main()
{
    i2c1_init();
    uint8_t c;
    
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
    
    GPIOB->MODER &=~(3<<2 | 3<<12 | 3<<14);
    GPIOB->MODER |= 1<<2 | 1<<12 | 1<<14;
    
    GPIOB->ODR |= (1<<1 | 1<<6 | 1<<7);
 
    NVIC_EnableIRQ(I2C1_EV_IRQn);
    
    while(1)
    {
    }
    
}
 
void i2c1_init(void)
{
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
    RCC->APB1ENR1 |= RCC_APB1ENR1_I2C1EN;
    
    GPIOA->MODER &=~(3<<18 | 3<<20);
    GPIOA->MODER |= 2<<18 | 2<<20;
    
    GPIOA->AFR[1] &=~(0xF<<4 | 0xF<<8);
    GPIOA->AFR[1] |= (4<<4 | 4<<8);             //PA9, PA10 - AF i2c
    
    GPIOA->OTYPER &=~(1<<9 | 1<<10);
    GPIOA->OTYPER |= (1<<9 | 1<<10);            //configure as Open Drain
    
 
    I2C1->CR1 &=~(I2C_CR1_PE);
    
    I2C1->TIMINGR = 0x00400D10;         //100khz, 1000ns tr, 100ns tf
    
    
    I2C1->OAR1 |= 0X40;     //own address
    I2C1->OAR1 |= 1<<15;    //enable own address
    
    I2C1->CR1 |= I2C_CR1_RXIE | I2C_CR1_ADDRIE;         //enable addr match and RXNE interrupts
    
    
    I2C1->CR1 |= I2C_CR1_PE;
    
}
 
 
 
void I2C1_EV_IRQHandler(void)
{
    uint8_t c;
    
    
    if(I2C1->ISR & I2C_ISR_RXNE)
    {
        c = I2C1->RXDR;
        
        switch(c)
        {
            case 1: GREEN_OFF;
            BLUE_OFF;
            RED_ON;
            break;
            
            case 2: RED_OFF;
            BLUE_OFF;
            GREEN_ON;
            break;
            
            case 3: RED_OFF;
            GREEN_OFF;
            BLUE_ON;
            break;
            
        }
            
        I2C1->ICR |= I2C_ICR_ADDRCF;
        
    }
        
    
}

I've tried configuring Slave Byte Control (SBC) in CR1 and NBytes in CR2 registers too, but it didn't change anything. From the reference manual, the only step to configure as slave is enable Own address. Are there any other steps or event interrupts I need to configure too?

By the way, I've tried using polling too instead of interrupt previously and had no luck either.

PS: TIMINGR value was generated through xls file by ST (and verified in cubemx) and I've tried different clock frequency and different timing values too, just in case that comes as a suspicion. And I'm using external pullups on SCL and SDA lines.

Best Answer