-
Notifications
You must be signed in to change notification settings - Fork 121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Requesting multiple bytes: the correct way #36
Comments
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! |
@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:
|
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 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. |
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. |
Not anymore... When in doubt, use the source (Luke) https://github.com/rambo/TinyWire/blob/master/TinyWireS/TinyWireS.cpp#L51 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: a503849#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 Master (Arduino Micro):
Slave (Digispark USB Board):
Output with most recent commit (returns always 255 aka "nothing"): Output with |
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). |
This reverses the change done in a503849. That change changed the API (even if it wasn't in a way visible through the methods) and failed to match the way the library was intended to be used. This once again makes it possible to send bodies larger than the buffer.
Just a note for future wanderers who stumble upon the problem with multiple bytes:
|
@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:
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 following repository works for ATTiny84 as slave and makes it possible to transfer short data streams: |
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
The text was updated successfully, but these errors were encountered: