arduino / ArduinoCore-avr

The Official Arduino AVR core
https://www.arduino.cc
1.25k stars 1.06k forks source link

Wire.endTransmission(false) #451

Closed Palingenesis closed 2 years ago

Palingenesis commented 2 years ago

New at using GitHub so apologies if I am doing this wrong.

Using the Wire Library. Wire.endTransmission(false); As I understand it this command should not send a STOP "P" after sending/attempting_to_send data in the current transition. I have visited Arduino Forums to try and find an answer with no joy. Please take a look, it will explain what my issue is. Wire Library [repeated start]

What I would also like to achieve is what is written in the i2c_bus_specification_1995.pdf Section 9.1.2 START byte.

I don't think the Wire.endTransmission(false); is working correctly, or perhaps needs another overload. May be we need: Wire.endTransmission(Boolean, Boolean); first Boolean = Stop bit, second Boolean = Dummy ACK.

Palingenesis commented 2 years ago

I do apologise for not closing this issue earlier. I closed Wire Library [repeated start] after achieving a solution. I have found that the protocol used by the device I was trying to communicate with, is a bespoke version I2C. I have managed to use and edit a soft version of Arduino I2C Library to communicate with the device. Control Cybot from Web Browser using ESP32-CAM

Ragebone commented 2 years ago

Hey awesome that you got it working for your project. please excuse my commenting twice and then deleting them shortly after. I made the mistake of assuming things and not reading the thread to its end.

Now with that out of the way, i am still not convinced that the described behavior is within or outside of the standard. My initial thought was that it definitively is not within spec but i'm not sure anymore. Main reason for that is that i could not yet find a statement that a STOP has to follow on a NACK. The obvious and totally reasonable implementation is what is currently the case with a STOP following a NACK immediately.

But what if you want to have one transaction where you address multiple different slave devices and read or write data to them? At least that is a thing i remember from tracing the SMBus activity of an BMC on a motherboard. But that might currently work as long as none of the addressed slaves unexpectedly NACK, which they shouldn't?

Another thing i find weird is that i don't think there should be a difference in your observed behavior between having STOP conditions and having none. The only way i can get myself to make sense of it is that the controllers protocol expects to be addressed exactly or at least twice within a single transaction with only the second being ACKed?

Weirdness for sure. Maybe that was meant as a protection feature to prevent being found on a bus by scanning and dumping? Well, what ever.

Palingenesis commented 2 years ago

I got a bit lost on the way to which is right and which wrong. When I first looked at it, having the option to not send a stop made sense in that: If the programmer used this option, the programmer had full control on when the bus is released to other devices connected to the bus. The programmer would have to add some sort of timeout in there code to release the bus to other devices, whether a NACK is invoked or not. But when I looked at the signals with the data logger, when the master invoked a NACK if no reply was received, in doing so it added a STOP. If a STOP has been sent, the data logger interpreted the next START as a normal START not a RE-START. Also if a STOP has been sent the bus is open to any other device on the bus.

This is how I saw it at the start, My brain crashed towards the end, so I just modified a soft library so that if the option to not send a stop was used, it never sent a stop until I wanted it to. There was some other tweaks to set the condition of the two wires.

I was not able to modify the Hard Wired I2C library because you only have access to the address of registers that have set tasks, I was not able to work out how to individually change the state of the two wires.

PaulStoffregen commented 2 years ago

But what if you want to have one transaction where you address multiple different slave devices and read or write data to them?

I believe the I2C terminology for this is "general call address". It is not my intention to express any opinion on this issue. I only wish to offer this phrase which may help you better search for relevant I2C info.

Ragebone commented 2 years ago

I got a bit lost on the way to which is right and which wrong.

I noticed that but i made similar mistakes so that is not an issue i think.

When I first looked at it, having the option to not send a stop made sense in that: If the programmer used this option, the programmer had full control on when the bus is released to other devices connected to the bus.

Yes, i see how that might have been the case. I have some gripes with the official documentation as well that could be improved in certain areas.

If a STOP has been sent, the data logger interpreted the next START as a normal START not a RE-START. Also if a STOP has been sent the bus is open to any other device on the bus.

Exactly, because there is no difference between a normal START and a REPEATED-START other then state the bus is in. But that makes me question why your controller didn't react to the individual transactions that got stopped.

so I just modified a soft library so that if the option to not send a stop was used, it never sent a stop until I wanted it to. There was some other tweaks to set the condition of the two wires.

Again congratulations on getting your project to work.

I was not able to modify the Hard Wired I2C library because you only have access to the address of registers that have set tasks, I was not able to work out how to individually change the state of the two wires.

Hence your switch to the soft-lib, i remember that from the thread and agree. Your result speaks for themself.

The question i am left with is if the intention of your original issue is a valid one that should actually be pursued. Again, i am not sure about that myself right now but i fear that it might become a YES in the near future once i look closer into it.

@PaulStoffregen

I believe the I2C terminology for this is "general call address". It is not my intention to express any opinion on this issue. I only wish to offer this phrase which may help you better search for relevant I2C info.

Thank you for the attempt but i mean something different then the general call on address 0. I have only seen this in use once so far on a Motherboard where multiple read and write operations to different slave devices were chained together. A imaginative bullshit example i came up with: S 0x40 W ACK 0xFF NACK SR 0x28 R ACK 0x00 NACK SR ... P

One reason i can come up with why someone might want to do it like this, is to have everything bundled into a tight time-frame instead of having 20 individual transactions that can be blocked and delayed by other activity on the bus.

But just because i have seen that somewhere, even if its on a motherboard, that does not mean that it is a spec compliant way of doing things. Especially considering that SMBus and PMBus are the relevant specifications.

Palingenesis commented 2 years ago

After getting things to work, I have come up with a theory why this protocol on this robot has been modified this way. There is some history to this robot, it is 20 years old and its parts where published in issues of a magazine over a time. I think there where some issues with it that still needed to be fixed when it went into publication and some quick fixes where done. As I have mentioned this robot has several processors that talk to each other TW (bodged two wire). This is after some time and several issues of the publication. The motor processor was the first processor to be issued and at this stage it needed to work on its own. It was set into modes like follow line and react to light using DIP switches. Then the motor processor was then replaced when IR processor was added so it could takes commands on the TW and changes the state of the motors accordingly. All good normal. :) But because the original motor processor had modes for following line and reacting to light, these needed to be kept on this processor. The sensors connect to this processor. You would think that you would just send the command to put it into one of these modes, haha no. The processor is already in a mode, the mode being to receive motor control commands, and you cant get it out of this mode without a reset. So this is what happens, sitting comfortable? When the motor processor was replaced (upgraded) the new board had a wire that went to the IR board, this was so the IR processor could physically reset the Motor processor. So when you send a command to change the mode of the motor processor, the IR processor takes the command holds the buss so none of the other processors can use it, hard resets the motor processor via the new wire and waits for the motor processor to stat back up and send an Acknowledgement to the constant poling from the IR processor. When the IR processor receives acknowledgement from the motor processor, the IR processor tells the motor processor which mode it is to go in. Every time you want to change the mode of the motor processor you have to repeat the above. This is why the bus is held (no stops) so the motor processor can do a hard reset.

Hope I have explained that OK. Pulled some hair out working that shit out hahaha.

The thing is at the start I assumed it was I2C. It was so close.