STM32F103C8 - Arduino Mega FAST MODE(400000L) i2c communcation not working

Hey guys,

I'm trying to connect a stm32f103c8 board (master) to an arduino mega (slave) over i2c. I have a master and slave program which runs perfectly fine when the clock frequency for i2c communication is set to standard (100000L) which is default.

I wanted the communication to happen a bit faster. So I changed the clock frequency to fast mode i.e., 400000L. I am seeing some weird behavior.

I can send data from master to slave (I can see the data appearing on slave) but when the master requests data from the slave, I don't receive any data on master.

I have tried the same code out on an arduino uno (master) and mega (slave). The code works fine for both standard and fast mode.

I run the code in standard mode by commenting the following line in the master code.
Wire.setClock(400000L);

Here's the code I have for the Master and Slave:

//Master Code

#include <Wire.h>

int data = 1;
void setup() {

  Wire.begin(); 
  Wire.setClock(400000L);
  Serial.begin(9600);
}
void loop() {
  Wire.beginTransmission(9);
  Wire.write(data);
  Wire.endTransmission();
  
  Wire.requestFrom(9, 1);
  int x = Wire.read();
  Serial.print(x);
  delay(500);

}




//Slave Code

#include <Wire.h>

int send_data = 1;
void setup() {

  Serial.begin(9600);

  Wire.begin(9);
  
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}
void requestEvent()
{
  Wire.write(send_data);
}

void receiveEvent(int bytes) {
  int x = Wire.read();
  Serial.println(x);
}
void loop() {
delay(500);
}

I'm having hard time understanding what's causing the issue. Please help me make sense as to why it's doing this and how I can resolve it.

The error is in this part of the code:

void receiveEvent(int bytes) {
  int x = Wire.read();
  Serial.println(x);
}

receiveEvent is called in interrupt context, so all interrupts are disabled. For every byte received the code writes 3 bytes into the serial buffer. This is done at a rate of about 10000 bytes per second. The serial interface may send about 1000 bytes per second, so the buffer fills up with a rate of 9 bytes/ms. In about 8ms the serial buffer is full. The code then waits for the UART interrupt to empty some bytes in the buffer. As the fill command is sent from an interrupt handler this interrupt will never happen and the Arduino freezes.

There's a delay in each loop of 500ms. It sends one character in each loop. So will the serial buffer still get full? I don't think so.

The receiveEvent function is called at the slave. I can see the data there. I mean I can see the output of the receiveEvent. Data is correctly printing to the console.

I'm facing issue with receiving data on the master. When I ask for data from slave using below lines and print it to the console, I don't see any data.

Wire.requestFrom(9, 1);
  int x = Wire.read();
  Serial.print(x);

I'm facing issue with receiving data on the master. When I ask for data from slave using below lines and print it to the console, I don't see any data.

You should always see data, maybe not the data you expect but you should see some. Post the actual output you get!

I wasn't seeing any data at the master.

The problem is fixed now. I just added a delay of 10 microseconds between the "write" and "request" calls. But I'm still confused why it was doing that and why adding the delay solved the issue. Any idea?

sushant_rasalkar:
I wanted the communication to happen a bit faster. So I changed the clock frequency to fast mode i.e., 400000L. I am seeing some weird behavior.

  1. Your Slave code is missing this instruction: Wire.setClock(400000L);. Try again by including the said code in the Slave sketch.

  2. I would like to see your Slave Codes in the following style where the printing is done in the loop() function.

//Slave Code

#include <Wire.h>

byte send_data = 1;//int send_data = 1;
bool flag1 = LOW;
volatile byte x;

void setup()
{

  Serial.begin(9600);

  Wire.begin(9);
  Wire.setClock(400000L);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}
void requestEvent(int howMany)
{
  Wire.write(send_data);
}

void receiveEvent(int bytes)
{
  x = Wire.read();
  flag1 = HIGH;
  //Serial.println(x, HEX);
}

void loop()
{
  //delay(500);
  if(flag1 == HIGH)
  {
    Serial.println(x, HEX);
    flag1 = LOW;
  }
}
  1. Your Slave code is missing this instruction: Wire.setClock(400000L);. Try again by including the said code in the Slave sketch.

Wrong. The clock is provided by the master, the slave doesn't need the clock rate set as it's synchronizing to the signal from the master.

But I'm still confused why it was doing that and why adding the delay solved the issue. Any idea?

Without having seen a logic analyzer picture of the actual I2C lines I'm simply guessing. My guess is that the master is already requesting the data from slave while that one is still try to print to the serial interface. That may delay the I2C interrupt so much that it is too late to do the clock stretching to let the master wait for the answer.
Another possibility is that the STM32 is not supporting clock stretching in which case you would have to take extreme care when implementing the requestEvent() routine for actually sending productive values.

pylon:
Wrong. The clock is provided by the master, the slave doesn't need the clock rate set as it's synchronizing to the signal from the master.

Let us note that the Slave is an Active Slave -- the MEGA; it has I2C Logic Circuitry which should be initialized to match with the Master like the TX/RX of UART System. There could be a situation for the MEGA (as a Master) to address the Master (as a Slave) when the I2C Bus frequency should be the same.

Let us note that the Slave is an Active Slave -- the MEGA; it has I2C Logic Circuitry which should be initialized to match with the Master like the TX/RX of UART System. There could be a situation for the MEGA (as a Master) to address the Master (as a Slave) when the I2C Bus frequency should be the same.

Bullshit. There is no concept of an Active Slave on I2C. A UART system is asynchronous and therefor needs to have the same speed on both sides. I2C is a synchronous serial bus where only one node (the master) is responsible to generate and provide the clock signal to all nodes connected to the bus. Please don't mix asynchronous and synchronous serial communication in this regard.

OP shouldn't even think about making the slave a master and the master a slave because that ends in a multi-master setup and such setups never run reliable except if there's no traffic on the bus or an external locking mechanism. But even if (s)he decides to try the multi-master setup, the I2C bus frequencies don't have to be the same, every master can have it's own frequency.

pylon:
Bullshit. There is no concept of an Active Slave on I2C. A UART system is asynchronous and therefor needs to have the same speed on both sides. I2C is a synchronous serial bus where only one node (the master) is responsible to generate and provide the clock signal to all nodes connected to the bus. Please don't mix asynchronous and synchronous serial communication in this regard.

UART system is asynchronous because there is no information to the Receiver when the Transmitter has initiated the 'Transmission Session'. However Bd should be identical between the Transmitter and Receiver for the extraction of true data. (We should not forget that we have USART system!)

In I2C based UNO-1 (Master) + UNO-2 (active slave)
In I2C based UNO (master) + BME280 (passive slave)

It is not always true that there has to be a prevailing concept; sometimes, it is needed to advocate on behalf of a 'to be concept/terminology'. Therefore, there is nothing such as bullshit; it is upto you to devise a way for differentiating the UNO-2 slave from the BME280 slave when the UNO-2 can generate SCL pulses but not the BME280; the UNO-2 can handle the receiveEvent()/sendEvent() interrupt contexts and not the BME280; the UNO-2 can be assigned a programmable I2C Bus address and not the BME280.

UART system is asynchronous because there is no information to the Receiver when the Transmitter has initiated the 'Transmission Session'. However Bd should be identical between the Transmitter and Receiver for the extraction of true data. (We should not forget that we have USART system!)

I absolutely agree.

In I2C based UNO-1 (Master) + UNO-2 (active slave)
In I2C based UNO (master) + BME280 (passive slave)

Bullshit, as I already wrote, I2C doesn't know a concept of an active slave. In both cases one side is the master, the other is the slave. And the slave doesn't have to know anything about the frequency of the communication (it just has to be inside the limits the slave is able to handle).

it is upto you to devise a way for differentiating the UNO-2 slave from the BME280 slave when the UNO-2 can generate SCL pulses but not the BME280;

Although the UNO-2 is technically able to generate SCL pulses, it must not do so if it is the I2C slave. It has to behave identically to the BME280 if it is slave.

the UNO-2 can handle the receiveEvent()/sendEvent() interrupt contexts and not the BME280

Wrong, the BME280 does the same, just in hardware instead of in software.

the UNO-2 can be assigned a programmable I2C Bus address and not the BME280.

This is true for the BME280 but there are many I2C chips (including sensors) that have a programmable I2C address, so this differentiation doesn't show anything.

It is not always true that there has to be a prevailing concept; sometimes, it is needed to advocate on behalf of a 'to be concept/terminology'. Therefore, there is nothing such as bullshit;

We are talking about standards and you're alleging things which contradicts the standard.
If we were talking about SPI and you allege that an SPI bus needs pull-up resistors I would say the same: bullshit.

pylon:
Wrong, the BME280 does the same, just in hardware instead of in software.

I am not aware that the BME280 has a processor capable of handling interrupts?

I am not aware that the BME280 has a processor capable of handling interrupts?

No, it doesn't have a general processor that can handle software but it has hardware the does the same, that's what I have written.
It makes no sense to argue about such details and terminology stuff. I2C doesn't have anything like an active slave, it just has a master and one or several slaves. Regarding the slaves it makes no difference if that is a software programmable processor or a piece of hardware, all have to follow the same rules that the standard dictates. And the standards says that the master provides the clock signal, the slave has to adapt as far as it's inside the range the slave is built for. On the slave you must not configure the speed. I hope you can agree on that.

pylon:
I hope you can agree on that.

There is no good reason to disagree with you on much of the points of your Post#12. Learning is a continuous process, and one must have instinct to convert his intuitive assumptions into more rational perspectives in the light of convincing arguments placed in this Forum by the veterans.