dotnet / iot

This repo includes .NET Core implementations for various IoT boards, chips, displays and PCBs.
MIT License
2.16k stars 582 forks source link

I2C to support Start and Stop without sending/reading bytes #582

Open Ellerbach opened 5 years ago

Ellerbach commented 5 years ago

I tried to implement an RFID/NFC reader thru I2C: PN532. Documentation of the device is here: https://www.nxp.com/docs/en/user-guide/141520.pdf Page 55 and Following describes the way to wake up the device when in I2C mode. In short, you need to pull the transmission up for couple of milliseconds without sending anything. Then you release the line and the chip will be awake. In this situation, nothing is sent to the device. And if Something is sent to the device, it won't wake up. So far, in the .NET Core implementation, it is impossible to have this behavior. Here is an example from the Arduino Library showing the idea behind (the wakeup function): https://github.com/Seeed-Studio/PN532/blob/master/PN532_I2C/PN532_I2C.cpp#L20 I'm not sure what the Linux kernel propose for that. What I've tested is the libnfc which does support the PN532 and which does wake it up correctly as I can use the sensor.

shaggygi commented 5 years ago

without sending anything. Then you release the line and the chip will be awake.

Have you tried sending a Write command without any data?

krwq commented 5 years ago

@shaggygi not sure how would that add a delay in between Start/Stop but perhaps device will be forgiving enough

shaggygi commented 5 years ago

@krwq i'm not really sure either and was just throwing out ideas to try. i recall seeing something similar to this a few years back with a device using WinRT where I just sent a Write message with an empty array.

krwq commented 5 years ago

worth trying before we add a new API for that :smile:

Ellerbach commented 5 years ago

@shaggygi, yes I tried. I get an exception:

Unhandled Exception: System.IO.IOException: Error 22 performing I2C data transfer.
 at System.Device.I2c.Drivers.UnixI2cDevice.ReadWriteInterfaceTransfer(Byte* writeBuffer, Byte* readBuffer, Int32 writeBufferLength, Int32 readBufferLength)
   at System.Device.I2c.Drivers.UnixI2cDevice.Transfer(Byte* writeBuffer, Byte* readBuffer, Int32 writeBufferLength, Int32 readBufferLength)
   at System.Device.I2c.Drivers.UnixI2cDevice.Write(ReadOnlySpan`1 data)

Looking at the source, if the buffer is empty with 0 lenght, it still tries to send it like if it has at least 1 data in it. The code only check for null buffer. Good news is that I found another way to wake up the device. I basically send a long buffer with just 0 and it makes it waking up somehow.

Ellerbach commented 5 years ago

Now, I have another issue. When I do ReadByte() and then another ReadByte(), the device reset the data it has the send. It's for it another request. All data should be read at once. But issue is that the size of what needs to be read dépends of what we read. Yes, there is a maximum size for a read but would prefer to avoid using it. Reading the spécifications page 42, it says that the I2S START shoudl be maintained to read the data and then I2C STOP should be generated to signify reading is over. And when looking at the source code, doing a ReadByte() generates the START and STOP conditions.

krwq commented 5 years ago

I'm wondering if we should have additional APIs for Read/Write/Stop/Start which do not produce Start/Stop automatically (but leave the existing ones as they are since they cover most of the use cases).

@Ellerbach could you share minimum code reading some random PN532 register/data (with spec pointers) where you hit this limitation and write what you expect to happen and what happens?

Would be useful to inspect with oscilloscope what should happen and what happens and if there is any way we can workaround it with existing APIs.

If the issue post above is different/unrelated issue please create a new one so it doesn't get lost.

joperezr commented 5 years ago

When we checked, the grand majority of devices require knowing how many bytes need to be read beforehand. I wouldn't add a whole new set of APIs for only one device that doesn't follow protocol. It is fine to have bindings for it, but I would have the binding make the PInvokes directly instead since it is an edge case.

krwq commented 5 years ago

I agree if this is a single case but I suspect there might be more devices like this which we haven't encountered yet. Regardless we should investigate this more and see how common these scenarios are and if there are ways to fix it without adding APIs but if we find that this is scenario described by I2C and we don't support it and there is few percent of devices using this feature then I think we should have a way of having more granular control over what happens on the wire

Ellerbach commented 3 years ago

[Triage] Related to #1525 and will be closed once the PR will be merged.

krwq commented 1 year ago

[Triage] Do we still need this? It seems nobody has a solid use case for this

Ellerbach commented 1 year ago

There are always ways around. But it does not mean we can't make the life easier :-)