Help with STM32 SPI slave and variable lenght - Page 1
Forum section: electronics / general

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 16, 2021, 07:44:10 pm
content: I'm doing a STM32 SPI slave with DMA and hardware NSS. I want to be able to read an arbitrary lenght of data. When the SPI transfer size and the DMA size matches all goes well. Example I have a buffer:Code:uint8_t data = { 1,2,3,4,5,6,7,8,9,10,11,12,13, 14, 15, 16};If I keep calling:Code:HAL_SPI_Transmit_DMA(&hspi2, data, 13);and try to read exactly 13 bytes for SPI transfer all goes well... I receive 1,2,3,4,5,6,7,8,9,10,11,12,13.But If I callCode:HAL_SPI_Transmit_DMA(&hspi2, data, 16);and read 13 bytes then I get scrooling data (1..13 in first transfer, then 14, 15, 16,1,2,... in the next and so on).So I tried to restart the DMA after NSS goes up by calling:Code:HAL_DMA_Abort(&hdma_spi2_tx);HAL_SPI_DMAStop(&hspi2);then I get 13,1,2,3,4,5,6,7,8,9,10,11,12 in all transfers. Seems a last byte got stuck in the shift register. Any ideas about how to solve it?

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 16, 2021, 11:53:15 pm
content: This is your slave?HAL_SPI_Transmit_DMA(&hspi2, data, 16);What about your master? Sounds like you are using also DMA for receiving, but not setting 16 bytes in the transfer length.So your master sends clock for 13 bytes, the slave is still waiting for more, and in the next transfer you get the remaining.

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 17, 2021, 01:18:15 am
content: Yes, that's the idea. I want the option of reading just the first 13 bytes OR reading more (like 1k bytes). The slave will cope with both options on demand... It just needs to keep sending data while the master asks, but when CS is deasserted I need that the next transaction starts again from byte 0 and that's where I'm failing. Any hints?

author: MasterT
author_rank: Frequent Contributor
timestamp: July 17, 2021, 03:47:06 am
content: I read somewhere on st.com thatHAL_StatusTypeDef HAL_SPIEx_FlushRxFifo(SPI_HandleTypeDef *hspi);may help, though I didn't tested it yet.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 17, 2021, 04:37:19 am
content: I had serious issues in slave spi if any clock was lost.Nothing would reset the spi module.Until I found __HAL_RCC_SPIx_FORCE_RESET()Where x is your spi module.You'll need to completely initialize spi after that, calling MX spi init, etc...

author: Siwastaja
author_rank: Super Contributor
timestamp: July 17, 2021, 06:18:42 am
content: Every STM32 SPI peripheral is different, we would need to know the exact part number to give specific advice.This being said, I have written variable-length SPI communication (using DMA, of course) for a few, including an older F7 and a newer H7, and can say for quite high certainty thatvariable-length slave DMA SPI is impossible, this isn't even a HAL limitation, but a hardware limitation.The only workaround really is to register an EXTI interrupt for the nCS pin, and in that handler, completely reset the SPI peripheral. In the newest H7, this was possible by disabling the SPI from the SPI peripheral control register itself; in the earlier F7, it required a reset through the RCC reset register! (Looking back at my code, even the new H7 requires reset through RCC in some cases, such as if CRC is turned on. Because the SPI peripherals are full of silicon bugs and they maybe fixoneper revision, it's best to always just reset the whole thing through RCC.)Luckily, such total peripheral reset, even when it sounds radical, is not an expensive operation, but I'm not sure this is easy, efficient, or even possible using the HAL because now the total peripheral init becomes a part of your interrupt handler so every clock cycle matters and you can't afford any bloat.

author: Kjelt
author_rank: Super Contributor
timestamp: July 17, 2021, 07:19:43 am
content: Just a thought, if you want variable length transfers than you need to address this in your protocol.Eg first sent a fixed length header, which contains for instance the command but also the lenghth of the next datapacket perhaps CRC etc and next use that info to receive that datapacket.ORjust receive packets of 1 byte, not very optimal for dma transferORMake a ring buffer large enough to receive the largest packets you will transfer, eg packetsize limitation, and fix the packagesize to fixed sizes.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 17, 2021, 07:23:53 am
content: Quote from: Kjelt on July 17, 2021, 07:19:43 amNo you don't, the whole idea of the SPI is that includes hardware delimiting signal that separates packets. Compare with streams like UART or TCP where such signal is not available, so you indeed need to tell how many bytes of payload follow, and add error handling for cases when the stream can go "out of sync". SPI self-corrects on the next packet because the cycle starts over.Falling nCS resets the receiver's state machine and sets the byte counter to 0. Then, each received byte increases the counter.It's a miracle how such simple scheme is difficult to understand to ST's engineers.With DMA, the only thing youshouldneed is to guarantee somemaximumsize for a packet.

author: Kjelt
author_rank: Super Contributor
timestamp: July 17, 2021, 07:29:50 am
content: I agree but when the hardware does not (always) work like it supposed to, you need to fallback to something that does work.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 17, 2021, 07:37:35 am
content: Quote from: Kjelt on July 17, 2021, 07:29:50 amYes, but (almost) every MCU (at least those capable of DMA) should have a working GPIO where the nCS pin can be configured to generate just a regular rising edge (or level change) interrupt. This way, you can reset the SPI and DMA manually with a few wasted clock cycles. The benefit is added robustness because a single missed clock doesn't break the whole system until power-down.

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 17, 2021, 01:32:01 pm
content: That's my idea. Will try using the functions suggested on Monday.

author: MasterT
author_rank: Frequent Contributor
timestamp: July 17, 2021, 02:17:46 pm
content: TI_mode is better option than Motorola, tried on F4/F7/H7 SPI-slave. Issue is it requires +1 clock pulse, 33 with 32-bits data frame.

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 17, 2021, 06:34:55 pm
content: I'm using a stm32f407 both as master and slave. I'm checking the results with a logic analyzer to be sure if the problems are in the master or slave side. For now I'm just dealing with the slave.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 17, 2021, 11:14:59 pm
content: The nCS pin causes a lot of troubles, and the spi module doesn't reset properly by just disabling/enabling it.Just try the HAL RCC Reset. It's a life saver.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 18, 2021, 07:04:51 am
content: Quote from: DavidAlfa on July 17, 2021, 11:14:59 pmThe nCS pin, in itself, is the best thing since sliced bread.I think what you mean is: STM32 SPI peripheral does not implement a working hardware nCS management mode despite bragging about it in the manual. This is indeed true. RCC reset being indeed needed to operate the device is a disgrace, as is the total inability of the ST engineers getting the SPI peripheral fixed, they have had years and years and it's still broken in every new device, year after year.

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 19, 2021, 01:40:04 pm
content: Quote from: MasterT on July 17, 2021, 03:47:06 amSeems the STM32F4 has no such function, but thanks for your help.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 19, 2021, 02:11:20 pm
content: FIFO flushing function likely just does read operations of the data register until FIFO is empty, I'd guess.If you require FIFO "flushing" that's a sign that you are not doing correct amount of reads of the FIFO. SPI is highly synchronous in nature so if your program logic is correct, FIFO should not accumulate anything "in excess".OTOH, if the SPI peripheral "breaks down" / gets stuck like it does in certain cases on ST controllers, then just flushing the FIFO is rarely helpful, you need actual peripheral reset, in a lucky case through disabling and enabling SPI on its own CR, but usually through RCC is most effective.

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 19, 2021, 05:14:18 pm
content: It's a variable lenght reading function, so it's acceptable that the DMA engine will read the next byte from the buffer and get stopped before SPI accepts the data (because the master ended the transaction). I did several tests and i'm 99% sure that the byte is stuck in the DMA but I don't know how to flush it. CallingCode:HAL_SPI_DMAStop(&hspi2);to stop a transaction and thenCode:HAL_SPI_Transmit_DMA(&hspi2, data, 13);Makes the last unsent byte from the terminated transaction to go as the first byte in the next transaction. Any idea about how to flush a DMA?

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 19, 2021, 06:00:32 pm
content: Ok, got it working. Earlier in the day I tried all the sugestions left in the forum and some ideas I had in the weekend. After exausting all options I revisited theCode:__HAL_RCC_SPIx_FORCE_RESET()I tried it alone before and it completely stopped the SPI. I read more about it and found that after calling it you must callCode:__HAL_RCC_SPI2_RELEASE_RESET();To enable the SPI again. Man, i felt like I hit the chip with a hammer, but surelly it workedThanks you all.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 19, 2021, 06:12:47 pm
content: I haven't used SPI in slave mode on STM32's so far, but having to reset the peripheral looks horrible to me. Does that affect all STM32 MCUs? A particular series? Or a particular model?Also, do you need to reset it after each transaction? Or what is the condition?

author: Siwastaja
author_rank: Super Contributor
timestamp: July 19, 2021, 07:31:42 pm
content: Quote from: SiliconWizard on July 19, 2021, 06:12:47 pmAfter each transaction (or "packet" delimited by nCS), i.e., when nCS goes to deactive state.Obviously, if you just read bytes and don't use DMA, nothing of this matters, there is no need to reset anything; just get a FIFO not empty interrupt and read out there.DMA with nCS hardware management theoretically "just works" without resetting if* All transactions are of the correct predetermined length,* Clock skipping never occurs, nor does excess clock from EMI,* You catch the very first communication correctly from the startSo even with fixed-length messaging, it isn't robust.Affects at least F7 and H7 series but likely most of the older MCUs as well. When I migrated code from F7 to an even newer H7, I noticed theytriedto fix the thing but failed. In any case, in a very basic case I was able to reset the SPI without RCC, but that broke down when I enabled CRC mode so clearly it's still best to reset with RCC reset register.It isn't that bad in practice, just reset it, won't take too many clock cycles. But indeed it tells a story about ST's struggles making such simple thing work.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 19, 2021, 07:39:10 pm
content: OK I see. Well, to be honest, I've found the handling of the CS line in implementations of SPI (either master or slave, for different reasons) either lacking or just plain buggy on many different MCUs.Not that it makes things better, but ST is not doing much worse than others here...

author: dmendesf
author_rank: Frequent Contributor
timestamp: July 20, 2021, 12:37:23 am
content: Your description matches exactly what I found out. Exact DMA size = OK, all other cases = FAIL without Reset.Quote from: Siwastaja on July 19, 2021, 07:31:42 pm