2024-01-23 11:53 AM
I want to solicit the advice of those more experienced with the STM32 processors. I'm experienced in general with embedded software and peripherals such as SPI but only about 6 months in on the STM32 processors and only in the last few weeks have I been delving into the SPI and DMA. My first dives were into timers and USB. In specific, I'm using a STM32H723.
I'm using a 4 channel SPI based ADC. The TI ADS131M04 in specific. SPI communication is 6 words with a default word size of 24 bits. The word size can be changed to 16 or 32 bits but initial size after reset is 24 bits. It has a data ready line to indicate samples are available.
I realize 24 bits is an awkward size but so many ADC's support it. I would assume there are efficient ways for the STM32 to handle this size of SPI data. While it isn't absolutely mandatory in this application for data to be handled the most efficient way, I have future projects that will need the efficiency. So this is a good time to learn.
What I would like to implement is a totally DMA based data transfer. The DRDY signal would initiate the transfer of the 6 word frame. In that frame are the 4 samples of the specified word size plus 2 words of overhead information. The RX DMA would go into a double buffer with multiple frames per buffer to reduce interrupt frequency. The TX DMA just needs to output 6 words of 0's to create the SPI clocking. I've seen references to a timer capture input being used to trigger a DMA. So I currently have DRDY going into a timer capture input. I haven't figured out the details of this yet. Is this a workable direction? I'm ok with an interrupt to deinterleave the data periodically probably scaling it in the process. I'm using a GPIO for the chip select.
I have basic SPI with DMA working (that was a learning experience). What I ran into the ADC expects the SPI data most significant byte first while the STM32 appears to be little endian. With 24 bit data, I am manually ordering the bytes I put into the transmit buffer so the order is correct. When I tried 32 bit data directly from an int, I realized the byte order was wrong. I know I can manually order the bytes but that seems very inefficient. I made a brief attempt to define a 24bit SPI word size but that messed with the DMA so I had to turn off the FIFO. I got to where 6 words of 24 bits shifted out correctly but the completion interrupt didn't happen and no change to the byte order. I gave up and went back to what works and wrote this message. Since the byte order didn't change, this direction didn't seem useful.
So far I'm using the HAL interface. I expect I will have to get to the register level to realize some of these ambitions but I'm trying to walk before I run.
After setting the context, here are some specific questions.
1) Are their any optimizations for handling 24 bit data words in the SPI and/or DMA controller setup?
2) Any examples of using a timer input to trigger an DMA SPI transfers?
3) Any way other than coding to deal with the byte ordering of SPI data?
4) Would the serial audio interface be better than the SPI for this? This is an SPI based ADC.
I am using a Nucleo board with a prototype IO board I've developed. I can modify this board or do something different for the production board if needed.
Any suggestions and advice will be appreciated.
Solved! Go to Solution.
2024-01-23 01:37 PM - edited 2024-01-23 01:40 PM
>I'm not sure the DMA understand 24 bit word lengths.
No. Only 32b are transferred. (24b in a 32b frame )
Did you set the dma to word (=32b) ? Do it.
And have your 24b data in an int32 array , it makes everything more clear and easy.
btw
I have (for audio) to deal with 16, 24 or 32 bit data ; first : i convert all to 32b , then only 32b is to handle.
2024-01-23 12:13 PM
1) Are their any optimizations for handling 24 bit data
Just set the SPI to 24 bit data , the DMA anyway transfers it as word 32b .
2) Any examples of using a timer input to trigger an DMA SPI ...
many. But should be no problem to set it in Cube.
3) Any way other than coding to deal with the byte ordering of SPI data?
Try it - with correct setting data should be ok without gambling.
4) Would the serial audio interface be better than the SPI for this? This is an SPI based ADC.
Is about same - but SPI is more flexible, in I2S you have to keep to standard.
>but I'm trying to walk before I run.
Good idea - i do same.
>I realized the byte order was wrong
Pffft - the "endianess problem" - maybe. Let me look at the ds first.
2024-01-23 12:29 PM
Ok, ADC msb first - you can set this in SPI parameters , like this:
ADC:
Should work.
2024-01-23 01:32 PM
I do have MSB set in the SPI but that is the bit order. When I tried setting the SPI word length to 24, I got an error with the DMA using the FIFO. I disabled the FIFO and got the configuration errors to go away. Data was shifted out on the SPI but no change to the byte order. Something to be aware of, I'm using a byte array and manually packing the 24 bit values into ie 3 bytes into the array. I'm using HAL_SPI_TransmitReceive_DMA to transmit and receive the response. I'm not sure the DMA understand 24 bit word lengths.
I tried the CPU based blocking HAL SPI call to see if acts differently with 24 bits SPI length set. Using a logic analyzer and looping the transmit into the receive, the bytes receives seem to be trying to be written as 4 byte integers (I speculate). I'm sending out 0, 1, 2, 3, 4, 5, and so on. I see that as expected. I'm getting back 0, 0, 1, 2, 0, 4, 5, 6 and so on. With DMA, I'm seeing the output echoed back exactly.
2024-01-23 01:37 PM - edited 2024-01-23 01:40 PM
>I'm not sure the DMA understand 24 bit word lengths.
No. Only 32b are transferred. (24b in a 32b frame )
Did you set the dma to word (=32b) ? Do it.
And have your 24b data in an int32 array , it makes everything more clear and easy.
btw
I have (for audio) to deal with 16, 24 or 32 bit data ; first : i convert all to 32b , then only 32b is to handle.
2024-01-24 06:56 AM
Thanks. I think I see the direction to head. Looking at HAL_SPI_TransmitReceive, SPI word sizes >16 are expected to be in be uint32_t. Lengths 9 through 16 through in uint16_t and 8 bits or lower are uint8_t. This makes sense. I expect DMA will need similar size configuration.
I was planning on doing any processing with 32 bit integers as well. It looks like 24bit SPI words will be handled as 32 bit integers which is what I want.
2024-01-24 10:19 AM
To summarize, the variable size depends on the SPI word length. For 4 thru 8, a byte is expected. For 9 thru 16, a short (16b int) is expected, and for 17 thru 32, a 32b integer is expected. The SPI peripheral will use the defined number of bits when transmitting. The receive will accept the defined number of serial bits and present them as a byte, short, or integer as appropriate. The DMA data alignment should be configured as the previous sizes.
This also handles the bit and byte ordering.
Another thing of note, the HAL_SPI transfer functions size argument really seems more appropriate to be count. It defines the number of SPI data length transactions to transfer from the buffer. For example, if you are transferring 6 SPI words of 24 bits each, the buffer is 6 integers while the size would seem to be 24 bytes. These routines expect 6 in this case.
There does not seem to be the option to sign extend the 24 bit SPI word that is written to the 32 bit integer. I could not find anything in the reference manual. So it seems signed values would require some additional processing.
2024-01-24 12:15 PM
Now you understand the SPI , at least partly . :)
About sign extend the 24 bit SPI word that is written to the 32 bit integer : you dont need this, if (what you can choose) it is msb /left in the 32bits, the msb is the sign then; the rightmost 8 bits are filled with zero.
If you use it in audio mode, the I2S mode, even more is possible (depends on cpu series and the more or less complex "spi-thing" , thats implemented), it can 16bits extend or have msb first, but right aligned and then also msb sign extended. This was the (now old) PCM Sony-mode, used in their CD players etc.
You just can try the SAI module (no problem in Cube, just try different settings and see, what Cube shows as options to set), this is the most flexible thing , can even multi-channel protocols (2 "slots" = stereo, 6 -> 6 channels ...) and frames up to 256 bit length ( if you de-select I2S/PCM standard mode).
On top : the DMA (if fifo is enabled) can put together 8 or 16 bit to 32 bit words and on some (the new H563 or similar) can ever reorder 8 or 16bit wise. But the downside is : the more complex , the more you have to read and do the try and error game - no free lunch here. So the DMA in H563 manual (called GPDMA) has 80 pages to read .... :flushed_face:
2024-09-30 07:47 AM
Hi @tec683. I am also using the TI ADS131M04. Were you able to solve the issues you had? Which settings did you end up using to get it working? I would greatly appreciate any help you can give.
I'm also curious as to what you did with the status word - did you just chuck it, or did you occasionally process it with non-DMA transactions?
2024-09-30 09:58 AM
It's been a while since I wrote this so I had to review the code a bit. I would say I solved it well enough for my needs.
My sample rate is fairly low so I was able to get by using an interrupt for DRDY that kicks off DMA transfers and receives for the SPI transfers. I did route DRDY into a timer channel so it would seem possible to use the timer input capture to initiate a DMA transfer to start the SPI DMA transfer. I've seen references to doing this but no good examples. When I get time, I do plan to revisit that aspect mainly to understand more about timers and DMA transfers.
It looks like I ignore the status word. The status register is the response for the NULL command that is used to read the 4 ADC registers so it gets updated with each SPI DMA transfer. It's there but I didn't see a need to use it. In my usage, all channels are clocked the same so DRDY happens at the same time and I have not enabled the CRC checking.
Something in general to be aware of when using DMA. Different DMA controllers and channels can only access specific memory regions. You need to make sure the cache is disabled for those regions so when the processor tries to access the data, it gets the memory and not the cache version. This varies by processor family.