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

The following is the code I am running. The controller sends 1,2,3 every 500 ms and the target device is supposed to a glow a common anode RGB led (RGB led program works fine too, tested as SPI target 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()
    uint8_t c;


    GPIOB->MODER &=~(3<<2 | 3<<12 | 3<<14);
    GPIOB->MODER |= 1<<2 | 1<<12 | 1<<14;

    GPIOB->ODR |= (1<<1 | 1<<6 | 1<<7);



void i2c1_init(void)

    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;

            case 1: GREEN_OFF;

            case 2: RED_OFF;

            case 3: RED_OFF;
        I2C1->ICR |= I2C_ICR_ADDRCF;

I've tried configuring Target Byte Control (formerly "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 target 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.

  • \$\begingroup\$ @brhans I'm not using HAL library. I'm using Keil and the startup file provided by ST. So there are no pre-defined functions like Receive that you have mentioned, I need to create subroutines myself. \$\endgroup\$
    – Luffy
    Commented Jul 13, 2020 at 15:08
  • \$\begingroup\$ When you set OAR1 you're or-ing the 0x40 address in. It's that intentional? Shouldn't you do I2C1->OAR1 = 0x40; to set the address and then or in the 1<<15? \$\endgroup\$
    – brhans
    Commented Jul 14, 2020 at 2:56
  • \$\begingroup\$ @brhans the reset value of OAR1 register is 0 (all bits 0), so while setting my address 0x40 it doesn't matter if I 'or' it or not. \$\endgroup\$
    – Luffy
    Commented Jul 14, 2020 at 5:37

1 Answer 1


I think I found the problem: the target address configuration is wrong, you have to shift the target address to the left by 1, because the bitfield of the 7-bit address is from bit 1 to bit 7, bit 0 is irrelevant in 7-bit address mode. See page 1138 of the reference manual. Also it is advised by the manual that you need to disable the target address before you set it.

I2C1->OAR1 &= ~I2C_OAR1_OA1EN;
I2C1->OAR1 |= (0x40 << 1);

With that, your code runs perfectly fine, I tried it. I hope it helps!

  • \$\begingroup\$ Hey thanks for the reply. I'll try it out in 2-3 days when I get hands on the dev boards again. \$\endgroup\$
    – Luffy
    Commented Aug 19, 2021 at 8:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.