STM32 I2C slave mode address left shift

STM32 i2c slave mode address requires a left shift as the HAL does not do it.
in MBED OS slave.address() does not handle this internally as most other MCUs doesn’t seem to require this or handle it in their respective HALs.
this results in the actual i2c address being wrong.
reference: ST Community
STM32 IDE’s code generator generates something like this when using the GUI configuration tool and i2c address is set to 0x32.

  hi2c1.Init.OwnAddress1 = (0x32*2);


You shared a link to post from ST community. The man was wrong because, he probably did not understand I2c addressing. Or?

Br, Jan

The issue is that address() function within I2CSlave class should handle the left shift, otherwise the example code here I2CSlave - API references and tutorials | Mbed OS 6 Documentation results in the wrong i2c address.
For example this is how STM32Duino folks do it within the begin() function:
Arduino_Core_STM32/Wire.cpp at 4e220184c9734313771e69e5646942085fc693a2 · stm32duino/Arduino_Core_STM32 · GitHub

So for mbed OS, since the STM32 HAL does not handle this, the address() function within I2CSlave should check if STM32 HAL is called and if true left shift the address value.

Otherwise this is how I have to use that function now: slave.address(0x25<<1);

Why it should be? When it is documented then it is OK. So we look to Mbed documentation

  • master’s I2C::write - address 8-bit I2C slave address [ addr | 0 ]
  • master’s I2C::read - address 8-bit I2C slave address [ addr | 1 ]
  • slave’s I2CSlave::address - The address to set for the slave (least significant bit is ignored).

Therefore, all methods must already have a shifted value as an address parameter. Lets test it.

I2C Master (Nucleo-F446RE):

#include "mbed.h"

int main(){
    printf("Mbed OS version %d.%d.%d\n\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);
    printf("\nI2C start...\n");
    I2C i2c(PB_9, PB_8);
    char cmd[3] = {0x41, 0x42, 0x43};
    // lets say  some real address from datasheet in 7bit format is 0x50h
    uint8_t addr7bit = 0x50; 
    printf("7bit address: 0x%02X\n",addr7bit);    
    // bit shift to left because parametr address of write/read must be byte = 8bits
    // result will be 0xA0h - 7bits of slave address and 1 bit of direction          
    uint8_t addr8bit = addr7bit << 1;
    printf("8bit address: 0x%02X\n", addr8bit);
    // we will put 8bit into but it is still 7b
    if (i2c.write(addr8bit, cmd, 3) == 0 )  {
        printf("ACK at (7bit) address: 0x%02X\n", addr7bit);
    printf("Program end\n");

Master debug output:

Mbed OS version 6.15.1

I2C start...
7bit address: 0x50
8bit address: 0xA0
ACK at (7bit) address: 0x50
Program end

Slave code (Nucleo-L152RE) is unchanged I2CSlava example what you also mentioned above.

Slave debug output:

Read A: ABC

That mean the slave device was addressed for write. So it seems be OK.

I can imagine that connecting an Arduino I2C master to an Mbed I2C slave can be chotick for you because both sides use different rules, but because Mbed doesn’t do it like Arduino doesn’t mean it’s wrong, or?

BR, Jan

your testing methodology results in a double negative, you are testing STM32 HAL i2c slave and STM32 HAL i2c master. So in both cases you are not left shifting the address value causing the result to be a false positive.

Now test this against linux. Say a Pi or anything else you prefer. And use i2c-detect and you’ll get the wrong value that when left shifted will be the value you passed to the address() function.

It’s not because arduino does it, its because STM32 CUBE IDE itself does it. My example in the original post of hi2c1.Init.OwnAddress1 = (0x32*2); was not written by me but generated by the IDE.

Check attached screenshots for what STM32 CUBE IDE GUI configuration generates automatically.


And btw found a couple other rtos that handle this as a sandard prctice:

Zephyr: zephyr/i2c_ll_stm32_v2.c at 1d189487b4d5c6cba0be8c2856e738ec44e6562d · zephyrproject-rtos/zephyr · GitHub

MicroPython: micropython/i2cslave.c at eb9674822b4d3be308beae2fa238ad85de9f09fa · micropython/micropython · GitHub

There is one difference. Your examples does bit shifting automatically and Mbed does not and must be done manually. That is not a bug because it is functional and working like that for many years (longer than a Zephyr exist).

Address is always bit-shifted by user in Mbed, as I wrote. The value 0xA0 in I2CSlave example is 0x50 already bit shifted address.

I already did it two years ago. I had Nucleo-F303R8 with Mbed(6.2) I2CSlave example connected to Raspbery PI - here. I used exactly same example as above and again it works as expected. RPI saw 0x50 that is 0xA0.

You placed this to section Bugs but that is better for Feature requests, because that is not a bug, it was a decision of developers. But best place for that is ARMmbed/mbed-os: Arm Mbed OS is a platform operating system designed for the internet of things ( than here on community forum.

BR, Jan

Sure, its fine of it was a dev decision, but at a minimum it should be mentioned here: I2CSlave - API references and tutorials | Mbed OS 6 Documentation that the address needs to be left shifted manually (or mentally while passing the value).
The issue is that a lot of folks come from Arduino IDE and or are used to STM32 Cube IDE handling it for them. Now with Zephyr and more importantly CircutPython and MicroPython in the mix, this decision looks like an exception and not a rule.
Just a feedback from someone who tried to use MBedOS for the first time having had generous experience with the other platforms and it lead me to genuinely believe this was a bug/oversight.