embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.52k stars 767 forks source link

STM32 SPIv2 + BDMA fullduplex transfers missing data #510

Closed lulf closed 2 years ago

lulf commented 2 years ago

I've been debugging SPI v2 + BDMA for a while now, and I'm raising this issue both as a flag that something might be wrong with the DMA impl and as a generic issue. The application in particular is the stm32l0 lorawan example, which uses SPI to talk to the radio peripheral. The goal is to use DMA for SPI instead of blocking SPI.

The application/driver is doing a transfer sequence to read a register on a radio peripheral using SPI.

Example MOSI: [0x1D, 0] MISO: [0x00, 0x72].

In blocking mode, I can read the expected response (0x72) from the buffer after transfer completes.

However, when using DMA (FullDuplex read_write async), the buffer contains all zeros after the future completes.

So somehow DMA is not correctly transferring that 0x72 from SPI peripheral to the memory buffer.

Analyzer output of blocking:

image

Analyzer output of DMA:

image

After reading the RM, the BDMA implementation seems to to the right thing, so I'm not sure if there are other things I should look into. Off the top of my head, I was wondering if it's somewhat a DMA vs. SPI synchronization issue (clocks?), but changing SPI freq doesn't change the behavior.

lulf commented 2 years ago

The bug was related to the lora driver's mix of write and read_write. Turns out mixing write() and read_write() is not a good idea, especially when the peripheral is sending data back after the write and I'm not reading it! The blocking API implementation does read back each value, while the DMA one doesn't, and just porting over to the DMA variant naively didn't work.