-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
STM32: DMA Module and SPI Peripheral #3619
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add comments to the pyi stubs in shared-bindings about how the functions are used.
Is it possible to read a command and change the response within a single transaction? That's a common way for SPI devices to work.
@tannewt so by that you mean the peripheral device takes incoming data, performs operations based on it, and adds to the ongoing TX buffer all during a single transaction (as in, one uninterrupted clock stream)? I hadn't considered that use case, that might require a DMA stream, I'd have to check. Can you provide an example of a device that does that so I could check out the protocol? |
I don't know what kinds of devices you were hoping to implement/emulate, but for instance when you have a RAM-like device on the SPI bus a transaction looks like this (from the datasheet of https://learn.adafruit.com/adafruit-spi-fram-breakout/downloads) this can be tough or impossible for a microcontroller to implement, since there's no clock stretching in SPI. |
I suspect we may want specific styles of SPI peripherals that aren't timing sensitive. A simple example is shared memory where the native code implements two standard commands, read and write, and the python code can just read and write that memory. This would work for simple sensors where the python code just updates a value that can be read over SPI. |
My particular application is as follows - every 8 ms, the master device initiates a transaction and begins shifting bytes of data over to the peripheral. The peripheral is expected to have staged a return packet as well, which it sends in return. Then the CS is returned to high and the peripheral is expected to gather all the information it needs for the next packet, which in this case is acquiring and staging hardware inputs, handling errors, and loading everything into the output buffer, all within the 8ms before the master initiates a transaction again. For my current API, you have two tools for this process - checking the status of an ongoing transaction with spi.ready() and variants, or using spi.wait_for_transaction() to hang out in an interrupt-able state until the CS line is lowered and the process is ready to begin again. I've considered other methods, such as simply starting the process to run in parallel to the python code, automatically delivering whatever data is in its object buffers at the time it receives a transaction. This would let the user update the buffers at their discretion without worrying about the timing, but it lacks any ability to dynamically change output data such as implementing an error checker or packet-ID auto-increment, which are things my application needs. I suppose we'd have to hear from other users who want this feature about what they'd actually be using it for. |
@hierophect Interesting! I think that makes a lot of sense. I don't think it should be called SPIPeripheral though. Is there a name for this specific technique? We should keep this new class specific to this style of use. That way we can add a second class later for different approaches. |
This has morphed into an implementation of SPI DMA. Unfortunately, it only outputs garbage data, and I can't figure out why. If anyone is interested in the the STM32 DMA module, and would like to help test why this occurs, it would be greatly appreciated. |
Why did you request my review? What did you change? What do you want me to review? |
Sorry, it was a misclick, I mixed it up with another PR I had up. Nothing to review unless you can spot some obvious source of memory corruption that would be messing up the arrays. |
I've had difficulty with the low level implementation of this, which does not seem to be able to deliver outgoing data properly. Closing until I have time to revisit. |
This PR adds SPI Peripheral/Slave APIs to shared-bindings, and implements a SPI Peripheral implementation in the STM32 port. Most of the setup and objects in the common-hal implementation are borrowed from the SPI class, since the two implementations are extremely close.
The primary means of accessing SPI Peripheral is
wait_for_transaction()
, which will wait in an interrupt-able state until CS is brought low, and then begin a non-blocking SPI transaction. Users can check whether the transaction is complete by checking spiperipheral.ready(), after which they can access incoming data.Tested on the STM32F405 Feather, with an M4 Express master/host. Mostly successful - my only issue has been that the MISO line seems to be dragged low by the M4 Express, and I'm not sure whether that's the fault of this PR or not. When MISO is disconnected from the Feather M4, it outputs correct data when observed with a logic analyzer.
Keeping this PR in a draft state for now, as the implementation and API might change a lot. Open to any and all suggestions!