Open dhunink opened 6 years ago
You code looks correct to me on the first look (apart from the i2c_regs being declared as uint16_t (it should of course be uint8_t since we're transferring single byte at a time, but that should not really affect anything but efficiency)
Do you have a logic analyser to trace the actual signals on the I2C bus ?
Thanks for your swift response! I do not own a logic analyzer I’m afraid. Up to this pint I never considered needing one. What is it you would like to be messeaured with it? Perhaps I can find other ways to debug...
@rambo The master is requesting 4 bytes and the slave should send 4 bytes. Is it possible to use TinyWireS.send four times in the request handler ?
@Koepel that’s something that can be done. It did it by sending all bytes from the current register up to the last register. It’s no problem for the slave to send more bytes then requested. But for me, although it is working, it still feels like a workaround that should be avoided and therefore I opened this issue.
I've added the workaround code to the initial post:
//WORKAROUND
/*int n = reg_size - reg_position;//Number of registers to return
for(int i = reg_position; i < n; i++)
{//Return all bytes from the reg_position to the end
TinyWireS.send(i2c_regs[i]);
}*/
I mean not calling the request handler 4 times, but sending 4 bytes in the request handler. If that is working, that I don't understand what the problem is.
A slave can always send more that the master requested. It is even valid. Suppose the master requests 1 byte or more, up to 8 bytes. The slave does not know how much data the master wants and can put all 8 bytes in the buffer. That is okay and that is normal. It is a result of how i2c works.
When the master requests data, the master defines how much bytes are transferred (from the slave to the master). The slave has no clue. That is i2c.
Note that like in Wire .send()
just puts stuff to the send-buffer, the data is actually sent when master starts clocking data bits. There should be a requestEvent for each byte the master is clocking for (the first one comes right after the slave read-address, then once every byte the master is clocking, master never tells slave beforehand how many bytes it wants it just gives the slave read-address and starts clocking bits until it signals a STOP condition (or REPEATED START).
If for each requestEvent you put 4 bytes to the send buffer you will soon find yourself running out of buffer space since you're putting 4 times as much data to the buffer as is actually getting clocked out. The code will probably block when buffer is full (or it might overrun old values [ring buffer] and super weird bugs ensue)
if you know for certain that after each write there will be X reads, you can in receiveEvent push the bytes the master is going to read to the send-buffer, no need to receiveEvent at all then, but should the master ever for any reason request less or more bytes -> super weird bugs.
I see. Thanks. I will add a link to your answer in this list: https://github.com/Testato/SoftwareWire/wiki/Arduino-I2C-libraries
@dhunink's problem is still not fixed I think.
There should be a requestEvent for each byte the master is clocking for
Are you sure about that? When I run my code, the event handler only seems to be called once rather than for each byte. Reading usiTwiSlave.c brings me to the same conclusion: the callback is only called once, which is when the address is found in combination with a read bit. Even the examples don't do what you're saying.
There should be a requestEvent for each byte the master is clocking for
Are you sure about that?
Not anymore...
When in doubt, use the source (Luke)
https://github.com/rambo/TinyWire/blob/master/TinyWireS/TinyWireS.cpp#L51
https://github.com/rambo/TinyWire/blob/master/TinyWireS/usiTwiSlave.c#L251
https://github.com/rambo/TinyWire/blob/master/TinyWireS/usiTwiSlave.c#L271 (wait, what...)
https://github.com/rambo/TinyWire/blob/master/TinyWireS/usiTwiSlave.c#L480 (more weird)
which is only called here https://github.com/rambo/TinyWire/blob/master/TinyWireS/usiTwiSlave.c#L576
Short version is that I'm now super-confused about what's actually going on, would have to take a long hard look at the history of changes to that file, someone (maybe even past me) has made some probably misguided changes...
I'm not going to have time for that deep-dive in the foreseeable future though.
Looks to me like this is the offending commit: https://github.com/rambo/TinyWire/commit/a5038499eac527cf994a05548aed400f3e6967be#diff-4fb58037efdfa694d9867fafad8efe2a
In fact, what that commit does is exactly that: change the callback from being called every byte to it being called only once per request (and add a test that relies on this behavior).
It even says so in the commit message: "If you move USI_REQUEST_CALLBACK() to line 583, then the callback is called only once per master request (when the request is initially received)."
Alright, with that change undone, I've got it working locally (based on the interrupt_driven branch, though). I am actually able to send messages longer than the buffer now (which was impossible before).
I'll see if I can send three pull requests your way coming weekend:
Surprise, as of f0eb04f
the communication doesn't work anymore.
Master (Arduino Micro):
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
}
void loop() {
Wire.requestFrom(10, 4); // request 4 bytes
while (Wire.available()) {
uint8_t c = Wire.read();
Serial.print(c, HEX);
}
Serial.println();
delay(500);
}
Slave (Digispark USB Board):
#include <TinyWireS.h>
byte own_address = 10;
volatile uint8_t i2c_regs[] =
{
0xDE,
0xAD,
0xBE,
0xEF,
};
volatile byte reg_position = 0;
const byte reg_size = sizeof(i2c_regs);
void setup() {
TinyWireS.begin( own_address );
TinyWireS.onRequest( onI2CRequest );
TinyWireS.onReceive( onI2CReceive );
}
void loop() {
}
void onI2CRequest() {
TinyWireS.send(i2c_regs[reg_position]);
reg_position++;
if (reg_position >= reg_size)
{
reg_position = 0;
}
}
void onI2CReceive(uint8_t howMany) {
if (howMany < 1)
{
return;
}
if (howMany > reg_size+1)
{
return;
}
reg_position = TinyWireS.receive();
howMany--;
if (!howMany)
{
return;
}
while(howMany--)
{
i2c_regs[reg_position] = TinyWireS.receive();
reg_position++;
if (reg_position >= reg_size)
{
reg_position = 0;
}
}
}
Output with most recent commit (returns always 255 aka "nothing"):
Output with f0eb04f
(returns first byte correctly, the remaining bytes are wrong):
I completely agree with @dersimn, f0eb04f commit broke simplest case with non-i2c-hardware supported devices. I have wemos d1 (esp8266) talking to attiny85. All esp8266 does: reads 1 byte from attiny85, requestEvent method on attiny85 is never getting called and from the code & saleae logs I can see it always returns 255, NACK (can provide SALEAE data if needed).
Just a note for future wanderers who stumble upon the problem with multiple bytes: I am using @acicuc fork (https://github.com/acicuc/TinyWire) which works flawlesly in everyhting but sending multiple bytes from the requestEvent(). However, as pointed on stackoverflow (https://stackoverflow.com/questions/34256847/i2c-stops-transmitting-after-exactly-7-requests-with-arduino) the requestEvent can send only one byte. I added a flag, which is set in the requestEvent and then I'm sending the multiple-byte data in the main loop. It looks this way:
#include <TinyWireS.h>
#define I2C_SLAVE_ADDRESS 0x4 // Address of the slave
int lower=0;
int upper=0;
bool requested = false;
void setup()
{
TinyWireS.begin(I2C_SLAVE_ADDRESS);
TinyWireS.onRequest(requestEvent);
}
void loop()
{
// I have cut out part of code not connected with the issue here.
// Its point is that both "lower" and "upper" variables get their values
if (requested == true){
TinyWireS.send(lower);
TinyWireS.send(upper);
requested = false;
}
}
void requestEvent()
{
requested = true;
}
@sokol07 thanks for the comments, glad to hear the fork is of use.
Just to clarify how master/slave comms work in a multi-byte request scenario: The master would normally sit in a loop sending out a read request per iteration, n reads in total, 1 byte returned from the slave per read - this is how Wire.requestFrom(addr,n) works. This means 'requestEvent' is triggered n times on the slave side during a multi-byte read. You can put your data into an array and send each byte by incrementing a counter, for example:
void requestEvent()
{
// send 1 byte from your data contained in the array i2c_regs
TinyWireS.send(i2c_regs[reg_position]);
// Increment the reg position on each read
reg_position++;
// optional: wrap reg position back to zero if run off end
if (reg_position >= reg_size) reg_position = 0;
// Set request event trigger and start time: This is optional too and only really needed
// if the MCU sleeps. I add a timeout in the main loop before sleeping to make sure all
// 'requested' data has been sent before sleeping...sometimes the MCU can fall asleep
// between events during a multi-byte request, this helps to prevent that; The timeout is
// normally about 1ms.
requestEvent_trigger = true;
requestEvent_start = micros();
}
To reset the 'reg_position' to 0, or whatever position you want, you can use 'receiveEvent', this is triggered when the master writes. Checkout the 'attiny85_i2c_slave' example for more details.
Ah, thank you for clarification. I must have misunderstood discussions I have seen about this topic or they concerned some older version of the code because I thought that the requestEvent is called only once and all bytes meant to be send should be buffered subsequently as multiple send() functions inside the requestEvent.28 paź 2021 01:47 acicuc @.***> napisał(a):
@sokol07 thanks for the comments, glad to hear the fork is of use.
Just to clarify how master/slave comms work in a multi-byte request scenario: The master would normally sit in a loop sending out a read request per iteration, n reads in total, 1 byte returned from the slave per read - this is how Wire.requestFrom(addr,n) works. This means 'requestEvent' is triggered n times on the slave side during a multi-byte read. You can put your data into an array and send each byte by incrementing a counter, for example:
void requestEvent()
{
// send 1 byte from your data contained in the array i2c_regs
TinyWireS.send(i2c_regs[reg_position]);
// Increment the reg position on each read
reg_position++;
// optional: wrap reg position back to zero if run off end if (reg_position >= reg_size) reg_position = 0;
// Set request event trigger and start time: This is optional too and only really needed
// if the MCU sleeps. I add a timeout in the main loop before sleeping to make sure all
// 'requested' data has been sent before sleeping...sometimes the MCU can fall asleep
// between events during a multi-byte request, this helps to prevent that; The timeout is
// normally about 1ms.
requestEvent_trigger = true;
requestEvent_start = micros();
}
To reset the 'reg_position' to 0, or whatever position you want, you can use 'receiveEvent', this is triggered when the master writes. Checkout the 'attiny85_i2c_slave' example for more details.
—You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub, or unsubscribe.Triage notifications on the go with GitHub Mobile for iOS or Android.
I've spent some time looking at this problem because I was unable to make ANY TinyWire work. The main issue is that I2C specification does not allow SCL stretching between the data byte and ACK. This means the ATTiny needs to act very quickly to setup the ACK reading after the data byte has been shifted. Even a minor change in the software may have an impact on performance. I also discovered that the clock was released concurrently with the USI counter loading which in my view is compromising. I adapted a master bitbang software to accept SCL stretching at any time which allows sending streams of data in both directions. It still has a failing ratio of 0.1 % in master to slave transfer, but it should be ok with an extra layer of CRC and handshaking on top. I'm unable to explain why these errors occur.
The following repository works for ATTiny84 as slave and makes it possible to transfer short data streams: https://github.com/ChrisHul/TinyWireSetup
Hi,
I've been spending a few days now on reading into TinyWireS, I2C protocols and standards. After seeing the interesting discussions in different issues I do believe that TinyWireS offers support for multiple bytes to be requested by it's master. Is this correct?
I tried a million ways so far, but can't seem to get more then a single byte returned from the slave, using Wire on the Master. The code is below. If the assumption that TinyWireS is capable of handeling a master calling Wire.requestFrom(0x01, N), where am I going wrong with the code below?
Master (Arduino Uno)
Slave - ATTiny85