stm32duino / Arduino_Core_STM32

STM32 core support for Arduino
https://github.com/stm32duino/Arduino_Core_STM32/wiki
Other
2.76k stars 962 forks source link

Wire (I2C) library: support 10 bits address mode #2468

Closed blazej222 closed 2 weeks ago

blazej222 commented 1 month ago

Describe the bug When STM32 is I2C master, any beginTransmission(x) call for x like 0b1111 0xxx without write() call in between will cause subsequent endTransmission() call to return I2C_NACK, despite slave device sending ACK on SDA line.

This is due to the fact that STM32 automatically detects addresses starting with 0b1111 0xxx as 10 bit addresses, and in that case, does not set I2C_FLAG_ADDR flag which is being checked after sending the first byte of address.

https://github.com/stm32duino/Arduino_Core_STM32/blob/5e4f22065273339bbd74c01b8209e9592d2c30bd/system/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_i2c.c#L3578-L3605

To Reproduce

I2C master code:

#include <Arduino.h>
#include <Wire.h>

HardwareSerial Serial1(PA10,PA9);

void setup() {
    Serial1.begin(115200);
    Wire.begin();    

}

void loop() {
        Wire.beginTransmission(0x79); 
        //Wire.write(1); //uncomment to receive ACK, but receive garbage on the other end
        int result = Wire.endTransmission();   
        Serial1.println(result);
        delay(1000);
 }

I2C slave code:

#include <Arduino.h>
#include <Wire.h>

HardwareSerial Serial1(PA10,PA9); //remove when testing on Arduino

void handleData(int bytes){
  byte data = Wire.read();
  Serial1.println(data); //change to Serial if testing on Arduino
}

void setup() {
  Serial1.begin(115200);  //change to Serial if testing on Arduino
  Wire.begin(0x79);
  Wire.onReceive(handleData);
  Serial1.println("Starting");  //change to Serial if testing on Arduino
}

void loop() {
}

Steps to reproduce the behavior:

  1. Connect STM32F103C8T6 (I2C Master) to another STM32F103C8T6/Arduino (I2C slave). (GND,SDA,SCL, RX/TX lines)
  2. Program STM32F103C8T6 as I2C master, another one/arduino as I2C slave.
  3. I2C master will report NACK despite having received ACK from slave device.

Additionally, if you uncomment Wire.write(1) call in I2C master code, I2C slave will report receiving a 242 byte, but only once and won't report again until reset.

This causes a simple example of I2C scanner ineffective for those addresses and ultimately makes communciation with those addresses impossible without modifying underlying framework's core code.

Expected behavior I2C master should report ACK. With additional Wire.write() call uncommented, a slave device should report receiving 1 every one second.

fpistm commented 4 weeks ago

Hi @blazej222 For me ther is no error: https://www.i2c-bus.org/addressing/

The address you used rely on the 10 bits addressing: 1111 0xxx

Moreover this is the HAL and hardware which handle that so nothing could be done here. If you really think it is an issue (don't think so) you can file an issue on the STMicroelectronics GitHub organization. Mainly on STM32CubeF1 repository.

blazej222 commented 3 weeks ago

My apologies, I might have worded this issue badly. Let's assume we have a slave device on I2C that has a 10bit address of 0x208 (010 0000 1000). To communicate with the device, the following two bytes must appear on I2C bus (as seen with oscilloscope): 0xF2 0x08 (1111 0010 0000 1000).

Normally on arduino, you can communicate with this device simply by doing something like this:

Wire.begin(0x79);
Wire.write(0x08);
//insert additional communication here
Wire.endTransmission()

However, when using the same code on STM32Duino this communication won't succeed. First of all, 0xF2 (0x79 << 1) byte will appear twice on the bus (because we are configuring HAL's I2C interface in 7bit mode, however the MCU itself recognizes this address as the beginning of 10 bit address). As 10bit mode uses different flags than 7bit mode(for example, it doesn't set the I2C_FLAG_ADDR flag HAL's checking for) it produces glitchy results in the communication.

As a result, there's no proper way to communicate with devices that use 10bit addressing from STM32duino.

The problem itself isn't present in HAL library - on native STM32 cube all you have to do is configure the interface into I2C_ADDRESSINGMODE_10BIT and the communication will be handled properly.

I understand that Arduino library does not explicitly provide 10bit addressing support - however, on native Arduino library it's possible to communicate with 10bit I2C devices by simply splitting the address into two bytes. In case of STM32duino it's not possible to communicate at all.

Probably a check would have to be implemented in STM32duino that configures HAL into 10bit addressing mode when applicable.

fpistm commented 3 weeks ago

Hi @blazej222 OK thanks for the feedback so your request is to have 10 bits address support. Currently, the Wire library support only 7 bits address.

Ref: https://www.arduino.cc/reference/en/language/functions/communication/wire/