I2C repeated start (Arduino as slave)

Hello.

I am working on my project to emulate Bid Chip with Arduino. For this purpose I have "hacked" the transmission between master device and the Bid Chip with logic analyzer. What I found out the communication looks like this:

Master writes data to Arduino and then requests the same data it sent (probably for checking purposes) with usage of repeated start. I wanted to emulate this process with such functions:

void receiveEvent(int howMany){
if (Wire.available())
{
temp = Wire.read();
}
}

void requestEvent()
{
Wire.write(temp);
}

But they don't work.
When I analyze logic states during communication - I am receiving data from master device, but the "temp" value is not sent.

Does anybody know the reason?

Thanks for your help.

Arristrasz:
Hello.

I am working on my project to emulate Bid Chip with Arduino. For this purpose I have "hacked" the transmission between master device and the Bid Chip with logic analyzer. What I found out the communication looks like this:

Master writes data to Arduino and then requests the same data it sent (probably for checking purposes) with usage of repeated start. I wanted to emulate this process with such functions:

void receiveEvent(int howMany){
if (Wire.available())
{
temp = Wire.read();
}
}

void requestEvent()
{
Wire.write(temp);
}

But they don't work.
When I analyze logic states during communication - I am receiving data from master device, but the "temp" value is not sent.

Does anybody know the reason?

Thanks for your help.

We need more of your code posted, Off the top of my head I believe that you have not declared temp a volatile. Both of those routines are called from interrupts. thus any variables they access need to be declared as volatile:

volatile uint8_t temp;

void onReceiveEvent( int howMany){
if(Wire.available())
  temp = Wire.read();
}

void onRequestEvent(){
Wire.write((uint8_t)temp);
}

The compiler could be using a register in the CPU to store temp, and the interrupt is reloading the value from RAM which is a stale value.

Chuck.

Is the BID chip just a Atmel 24Cxx memory chip ?

I can't find a lot about it, but this seems to work to read the BID chip: https://www.rc-heli-fan.org/viewtopic.php?f=63&t=59994&start=75&sid=c923d2355db0c05fe5d182484ba25895#p955301

It is often not possible to emulate a hardware I2C device. A hardware I2C device responds immediate, and the Arduino uses a combination of hardware and software (the Wire library). Therefor, the timings and delays for an Arduino are much longer.

P.S.: The 'howMany' parameter tells how many bytes are received, you don't need Wire.available().

@chucktodd

You were right. I did not declare temp as volatile. I will change that. Unfortunately I am not able to check if it works now right away, but for sure I will let you know tomorrow as soon as I implement this.

About my code. Well, the project is a bit more complicated than I described it earlier.
I just wanted to know the answer to the basics so I could go on with the rest myself, but if you asked - the communication looks like this (or at least I understand it that way):

Master sends one byte (write mode): It sends the number (address) of value which it wants to obtain from Arduino.
Master requests data (read mode): It wants to receive a value of previously sent address.

Master sends two bytes (write mode): First byte - address to which he wants to write data. Second one - the data.

The whole code I wrote (with variables changed to volatile):

#include <EEPROM.h>
#include <Wire.h>

volatile uint8_t temp, temp2;
volatile uint8_t eeprom[1000]; // Data to emulate EEPROM, which is too slow for I2C

void setup(){
Wire.begin((0xA << 3 | 0x0));
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
// Loading EEPROM values to volatile variables
for (int i=0; i<1000; i++){
eeprom[ i ]=EEPROM[ i ];
}
}

void loop(){
}

// Master asks for: access to register bit - sends 1 bit / writing data to this bit - sends 2 bits
void receiveEvent(int howMany){
if (Wire.available())
{
// First bit = register bit
temp = Wire.read();
}

if (Wire.available())
{
// Second bit (if exists) - data bit to be written to EEPROM[temp]
eeprom[temp] = Wire.read();
// Saving received data to EEPROM as well (I need it after shutdown)
EEPROM[temp] = eeprom[temp];
}
}

// Master requests data from the last called register - temp
void requestEvent(){
Wire.write(eeprom[temp]);
}


@Peter_n

Yes, its the one.

About the program in the post you pasted:
I did actually run it a week ago, but it was not really of use. For my project I need to emulate the chip, not to exchange data with it. But running it at least gave me some kind of idea of communication and was a real help.

About your P.S:
Well I know it, but i wanted to collect certain data depending on how many bytes master sends. My previous problem was a bit shortened, because I did not want to waste your time. You are probably right and as I am new to the I2C communication I am making such pointless mistakes. Thank you for pointing it to me. Any kind of help is appreciated as I want to learn more :slight_smile:


Huge thanks for your help :slight_smile:
I will let you know if it worked.

What is "EEPROM [ i ]" ? When you need to read or write the internal EEPROM, you need functions. Perhaps you are using an index to the EEPROM class, but there is just one class that is used for those functions.

You are on the right track, but I doubt if you can fool the Master device. Emulating I2C hardware with an Arduino often fails.

I have written code in the past to read and write data to an Arduion Slave EEPROM. With a emulated address for the EEPROM and so. As far as I remember, reading is no problem, but writing is slow and that slows down the I2C bus and the Master as well..

@Peter_n

This EEPROM[ i ] works as well. It was not like this always, but recently there's been some updates to EEPROM.h library and now one can access it just like a regular array.

:o I didn't know that. Thanks. The EEPROM usage has been improved a lot.

Arristrasz:
@chucktodd

You were right. I did not declare temp as volatile. I will change that. Unfortunately I am not able to check if it works now right away, but for sure I will let you know tomorrow as soon as I implement this.

About my code. Well, the project is a bit more complicated than I described it earlier.
I just wanted to know the answer to the basics so I could go on with the rest myself, but if you asked - the communication looks like this (or at least I understand it that way):

Huge thanks for your help :slight_smile:
I will let you know if it worked.

Arristrasz,
you might run into some timing problems writing to EEPROM duing the onReceiveEvent() routine. Depending on the required speed, you might have to create a shadow 'EEPROM' in RAM and issue the EEPROM write command in the main loop.
The Wire library will stretch the I2C SCL signal until onReceiveEvent() completes, if your I2C master device does not timeout because of the extended time it could be ok. If you get timeout failures from the Master device you might have to re-code where you actually update the EEPROM.

Chuck.

Hello again and thank you for all your replies.

I did exacly as you told me - changed variables to volatile and deleted EEPROM[temp] update from onReceiveEvent().

It still does not work.

Seems like Arduino cannot respond to a repeated start. Again - I collect data from master, but cannot send the value back to it.

Here is how my communication looks like:

As you can see Arduino does not respond to read request.
Do you have any ideas?

Paweł

Test:
Arduino Mega 2560 as Master, with level shifter for a 5V I2C bus and 3.3V I2C bus. One BMP085 and Arduino Slave at the 3.3V I2C bus and two Arduino Slaves (ATmega8L and ATmega328P) at the 5V I2C bus.
My protocol uses two bytes for the (virtual) memory address, so I transmit those two bytes, call Wire.endTransmission(false) for a repeated start and do a Wire.requestFrom().

i2c_scanner : all okay (the I2C bus hardware is okay).
MultiSpeed I2C scanner (limited to 400kHz) : all okay (the I2C bus hardware is okay at 400kHz).
Normal operation with repeated start : my failsafe kicks in, and retries the transmission, the final result is okay.
Normal operation without the repeated start : no retry is needed.

I did have some timing problems in the past, also without the repeated start. For example when the Slave has too many (long) interrupts, or interrupts are disabled too long and so on. I already noticed in the past that with a repeated start I had more errors than without.

Result:
An Arduino Slave will accept a read after a repeated start, but has more timing problems. I'm not using the repeated start yet.

Wow. That's pretty much of information. Thank you alot.
What would you suggest me to do then? Do you think that timeouts are the problem? How do I cope with them? (I am using 8 MHz clock. Should I use 16 MHz one then?)

Thank you again and looking forward to your reply. :slight_smile:

Paweł.

chucktodd already mentioned the clock pulse stretching by an Arduino as Slave. That is a major problem when emulating hardware.

A 16MHz Arduino is of course better. Would a 16MHz Arduino be fast enough for the Master ? Maybe, if the Master is a slow microcontroller.

My test verified that the repeated start is a problem for an Arduino as Slave. But I don't know why. I tried adding a delay in the Master, but that didn't help. Without knowing the cause, this will continue to be trouble.

Could you use a real 24C eeprom ? And perhaps write data to it with an Arduino when the Master is not using the I2C bus.

Hi,
Guess someone else suffering the same problem may get here like me? Just let you know, I replaced the wire library with hardWire library version 1.0.2 which is from thexeno, and seems reading with a repeat start works now.

@Mangoalx, Here i am !! i got stuck with a repeated start issue.

My I2C master device sends out repeated start as below

START - (SLAVE_ADDR +W) - (REG ADDR) - START_REPEAT - (SLAVE_ADDR + R) - STOP

On the slave code, which is as below, we see that OnRequest is not getting called. Which means, the SLAVE_ADDR+R, which is the Read command, which is not detected by the HardWire.h

Any idea whats wrong ? How did you detect the SR (Repeated START) on your slave ?


#include <HardWire.h>

#define SLAVE_ADDR 0x18

byte regaddr = 0;

void ReceiveData()
{
Serial.println("Receive");
while(Wire.available())
{
regaddr = Wire.read();
}
}

void SendData()
{
Serial.println("Request");
Wire.write(0x59);
}

// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(15200);
Wire.begin(SLAVE_ADDR);
Wire.onRequest(SendData);
Wire.onReceive(ReceiveData);
}

// the loop function runs over and over again forever
void loop() {
}