Author Topic: STM32 - USART interfacecan't keep up with SPI  (Read 4428 times)

0 Members and 1 Guest are viewing this topic.

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
STM32 - USART interfacecan't keep up with SPI
« on: February 11, 2022, 05:08:44 am »
I'm reading 32bit values from an external ADC using SPI (DMA) at a rate of 25.6kHz. I want to forward these values to my PC via USART for diagnostic purposes during development but seem to be limited on this interface to about 16kHz or 512kbps independent of what I set the baud rate to.

I previously tested sending and receiving random data over this interface at 819kbps with USART set to 1.7Mbps baud with no issues so I'm a bit confused. Previously I was making a call to HAL_UART_Transmit() once every 39us within the ISR of a TIM. Each HAL call would send 4 bytes.

Now however I am in the ISR of the DMA (SPI) which triggers every 640ms. I'm looping over 65532 bytes and making a baremetal call to USART1->TDR, after adding a CRC bit. Code attached at the end.

I think I might be able to use DMA to transfer directly between SPI and USART but I'm worried this is overkill in terms of effort since it's a temporary interface (and it was a tough enough battle getting DMA working between SPI and my memory buffer) plus then I'd lose CRC. If I can do it in software with a blocking call I'd rather that.

Should I arrange my SPI DMA to make smaller transfers so I can write smaller batches of samples more frequently to USART? Or is it hopeless and I should go straight to a DMA implementation of USART?

Code: [Select]
        //DMA ISR, triggers every 640ms
        uint16_t count = 65532; //size of RxBuffer in bytes
uint8_t *pdata8bits = (uint8_t *)RxBuffer;

while (count > 0U) {

//replace the 1st bit of each word with CRC
if (count % 4 == 0) {
WRITE_REG(CRC->DR, (uint32_t)(*pdata8bits & 0xFFU) << 8*3 |\
(uint32_t)(*(pdata8bits + 1) & 0xFFU) <<8*2 |\
(uint32_t)(*(pdata8bits + 2) & 0xFFU) <<8 |\
(uint32_t)(*(pdata8bits + 3) & 0xFFU));
*pdata8bits = (uint8_t)READ_REG(CRC->DR);
}

while (READ_BIT(USART1->ISR, UART_FLAG_TXE) != UART_FLAG_TXE)
{}

//send byte via USART
WRITE_REG(USART1->TDR, (uint8_t)(*pdata8bits & 0xFFU));
pdata8bits++;
count--;
}

while (READ_BIT(USART1->ISR, UART_FLAG_TC) != UART_FLAG_TC)
{}

 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #1 on: February 11, 2022, 12:23:31 pm »
Did you enable the fifo? Should make the dma transfers a lot more efficient, it also greatly helps when "breaking" big integers into smaller ones, ex. 32 bit->4x 8-bit transfers.

https://www.st.com/resource/en/application_note/dm00046011-using-the-stm32f2-stm32f4-and-stm32f7-series-dma-controller-stmicroelectronics.pdf
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #2 on: February 11, 2022, 02:10:34 pm »
Did you enable the fifo? Should make the dma transfers a lot more efficient, it also greatly helps when "breaking" big integers into smaller ones, ex. 32 bit->4x 8-bit transfers.

https://www.st.com/resource/en/application_note/dm00046011-using-the-stm32f2-stm32f4-and-stm32f7-series-dma-controller-stmicroelectronics.pdf

Please excuse if my post wasn't clear or I didn't understand yours. I'm not using DMA for memory-to-USART currently, I'm only using it for SPI-to-memory. The USART xfer is done in software.

I don't think there's any issue with the DMA throughput since if I comment the USART code the SPI DMA ISR gets called at the expected rate. It's only when I add the blocking USART calls that it can't keep up.

I'm thinking first of all I shouldn't be doing the blocking USART call within the SPI DMA ISR but rather within the main loop, so that it isn't delaying the start of the next SPI xfer. I'm not sure with this though how to ensure that the memory buffer (written to by SPI, read from by USART) doesn't get clobbered by SPI midway through the USART reading from it. The DMA transfer-complete ISR is the only time I'm aware I can be sure the buffer is safe to read from. Perhaps within the ISR instead of sending to USART I should make a memory copy of the buffer in the ISR so that USART in the main loop can safely read from this? Is this common practice?
« Last Edit: February 11, 2022, 02:40:48 pm by davegravy »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #3 on: February 11, 2022, 04:22:26 pm »
Oh, I didn't understand completely either  ;).
Yes, that's a bad thing, don't block the ISR.

Copy the spi value to some variable and set a flag.
Both (variable and flag) must be declared as volatile.
Check that flag in the main loop and send the data when detected.
If each spi value is 32bit, are you breaking it into 4x 8bit values for the uart?
Every uart byte transfers 10 bit (start+byte+stop), so that's 25600x40=1.024.000 baudrate minimum.
Since you have some delay between transfers you need a faster baudrate.
You said you got 896Kbit using 1.7Mbit baudrate, should work then, but you might want to use 2Mbit or faster if possible.
Such frequencies need really short connections between the stm32 and the serial converter.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #4 on: February 11, 2022, 07:26:31 pm »
65532 bytes is 16383 32-bit samples, and with CRC 81915 bytes on the UART wire.
With 10-bit per byte transferred, you get at 819150 bits.

But this is transferred every 640ms, so in a whole second the system is sending 1280 kbps. Perhaps this is capping out somewhere in your system.

If the echo test maxed out at 819kbps, I would have a look at that. UART is full duplex, so if you used buffers to transmit the data to the device with async transmit/receive on the STM32, it should get much closer to the theoretical maximum of 1700kbps of 1.7MBaud.
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #5 on: February 11, 2022, 08:04:37 pm »
65532 bytes is 16383 32-bit samples, and with CRC 81915 bytes on the UART wire.
With 10-bit per byte transferred, you get at 819150 bits.

To be clear the ADC sends a 24bit sample and then one additional byte that is static and represents the configured downsampling factor. I'm replacing this one byte with the CRC, so not adding any additional bytes.

If the echo test maxed out at 819kbps

I never actually found the maximum, I stopped testing when I achieved 819kbps. I should see if I can establish the limit.
« Last Edit: February 11, 2022, 08:09:32 pm by davegravy »
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #6 on: February 18, 2022, 05:34:52 am »
OK So by moving the UART Tx code out of the ISR of the SPI DMA I was able to keep up with transmit using a 2073600 baud rate. Unfortunately I keep detecting CRC errors periodically, so I thought I'd try to do the UART xfer with DMA to hopefully eliminate some overhead of doing it in software and then be able to reduce the baud rate.

I implemented 2 buffers: while SPI DMA is writing to Buffer A, UART DMA is reading from Buffer B and then they swap. Unfortunately with this  I needed a higher UART baud rate (2480400) to keep up  |O. Why might this be? Is it because SPI and UART are sharing a common DMA port and SPI has already saturated it?

I'm wondering if I'm just abusing UART here and there's a better way to go. The STM32U5 NUCLEO board I have has a USB type-c port, I have no clue how much effort that would be to get working. I just need a way to get the ADC samples to my PC cleanly for evaluation purposes.
« Last Edit: February 18, 2022, 05:39:36 am by davegravy »
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 4957
  • Country: si
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #7 on: February 18, 2022, 06:32:28 am »
The proper way to debug this is to add GPIO toggling calls into the critical parts of your code. That way you can use a scope to measure exactly how long each thing takes and how frequency it executes. You can also see really nicely how much of the available UART bandwidth you are actually using on a scope.

The UART peripheral on the STM32 was unfortunately designed by an idiot. It only has enough buffer for 1 character so it needs to be spoon fed data constantly. Using DMA for UART TX works pretty well tho so that should be fine.

I would take a guess you are having some sort of race condition when configuring DMA transfers. The DMA ping pong buffer mode can be useful for this since you get a DMA interrupt every time half of the buffer was done transferring, while you are servicing that interrupt the DMA has already automatically started using the other half of the buffer. This is useful for the SPI RX side.

To then send it out what you can do is start a DMA transfer from this buffers address into the UART TX register. This area of the buffer will not be written to for as long as the SPI DMA is filling the other half, giving the UART this time window to pump it out. As a result the UART should be going pedal to the metal for the duration of the transfer then pause for a bit as it waits for the other half of the buffer to be ready to send.
 
The following users thanked this post: davegravy

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #8 on: February 18, 2022, 09:01:29 am »
I'm wondering if I'm just abusing UART here and there's a better way to go. The STM32U5 NUCLEO board I have has a USB type-c port, I have no clue how much effort that would be to get working. I just need a way to get the ADC samples to my PC cleanly for evaluation purposes.

Are you using the UART provided by the STLink?

They are far from bulletproof at high throughputs. I don't know exactly what the performance of that board is, but I had a lot of trouble with my F7xx nucleo that even lost data in bursts at 115200 Baud. I update STLink's firmware, and that resolved the issue a bit, but it still has it's limitations
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #9 on: February 18, 2022, 10:46:23 am »
Also remember SWO, which is able to send data a lot faster than most UARTs.
You can redirect Printf output there, too: https://www.codeinsideout.com/blog/stm32/swv/
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #10 on: February 18, 2022, 08:58:42 pm »
Thanks, this looks more promising than UART indeed.

I got this working with the ITM Data Console in a new CubeMX project fairly easily, (although somehow it doesn't work in my existing baremetal project - more investigation needed here, maybe a clock config mismatch).

I will need programmatic access to this data stream, preferably in Python. I found this script which seemingly accesses ITM data via OpenOCD via a TCP socket. I was looking into installing OpenOCD for windows but I see in the CubeMX debug config there's an option for STLink OpenOCD, with a couple configurable port parameters (one for GDB, one for SWV). Am I on the right track?
« Last Edit: February 18, 2022, 09:01:40 pm by davegravy »
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #11 on: February 19, 2022, 06:15:56 am »
Unfortunately having no better luck with ITM in terms of throughput. Beyond about 10k samples/sec (320mbit/s ignoring ITM overhead) I start seeing pretty frequent corruption.

For this test I configured SWD with a frequency of 24MHz (the highest CubeMX permits), core clock is 160MHz. I'm connected to the TCP socket of OpenOCD using python socket and reading from this in batches of 2024 bytes.

Should I start to suspect my USB cable?

Is there another interface option I should consider? Is there anything synchronous that's easy between PC and uC?
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #12 on: February 19, 2022, 06:24:58 am »
** Edit **
What's the exact STM32U5 model?
Checking the RM, it seems the GPDMA is a lot more flexible than in F4 series. Looks like any dma stream can take any dma request?
I guess you're using a timer TRGO signal to drive the ADC SMA requests, you might be able to use the adc1_dma request to drive a mem->UART DMA.
So the order would be TRGO->ADC starts->ADC ends->ADC DMA request occurs->Starts DMA UART TX transfer
UART DMA should be configured in non-incrementing mode, so it always reads the same ADC register, memory-to-peripheral and needs FIFO enabled to splits large integer into 8-bit transfers for the UART.
The only downside is that FIFO supports 8, 16 and 32bit words, but not 24.
You'lll need testing it, I've never used this STM32 series.

Otherwise, consider using dma + dual buffering. Each sample you get, copy the value to the buffer.
When the buffer is full, trigger uart tx dma, and start filling the second one, swicthing the buffer everytime.
That should free up a lot of resources, the only downside is that you don't really get realtime output, but slightly delayed due the buffer filling.

Usually starting/stop the peripheral wastes quite a lot of time, so sending one byte at a time could cause it to be very inefficient, but DMA should completely saturate your uart doing gap-less transfers.

I don't know if memory-to-memory dma can be used for ITM (SWO), found little no nothing about that.
Although it's memory mapped, it seems it's can't manage dma requests, so, if working at all, the dma would write over it without any control of the busy flag, like normal memory transfer, overflowing and/or crashing it.
« Last Edit: February 19, 2022, 07:47:18 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: davegravy

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #13 on: February 19, 2022, 03:33:26 pm »
What's the exact STM32U5 model?

STM32U575ZIT6Q

Checking the RM, it seems the GPDMA is a lot more flexible than in F4 series. Looks like any dma stream can take any dma request?
I guess you're using a timer TRGO signal to drive the ADC SMA requests,

Not quite. I'm starting the SPI in software with SPI Rx generating the DMA request. I start the first SPI xfer this way in main() and subsequent in the transfer-complete ISR. I'm using a TIM to generate a READY signal to gate the SPI transfer so that it only happens in the conversion period of each ADC convert/acquire cycle. Transfer size is 65535, so the ISR only runs approx every 640ms.


you might be able to use the adc1_dma request to drive a mem->UART DMA.
So the order would be TRGO->ADC starts->ADC ends->ADC DMA request occurs->Starts DMA UART TX transfer
UART DMA should be configured in non-incrementing mode, so it always reads the same ADC register, memory-to-peripheral and needs FIFO enabled to splits large integer into 8-bit transfers for the UART.
The only downside is that FIFO supports 8, 16 and 32bit words, but not 24.
You'lll need testing it, I've never used this STM32 series.


In the future I'll be implementing DSP (a chain of configurable IIR and FIR filters) in software (unless I can figure out how to leverage FMAC on 24/32bit samples) to process the ADC samples and my hope was to use UART/ITM/something to be able to probe the data stream between filters, not just data right off the ADC. This scheme I think this would not work with direct peripheral->peripheral DMA. If it works it's better than nothing ("beggars can't be choosers") just not ideal for my use case.


Otherwise, consider using dma + dual buffering. Each sample you get, copy the value to the buffer.
When the buffer is full, trigger uart tx dma, and start filling the second one, swicthing the buffer everytime.
That should free up a lot of resources, the only downside is that you don't really get realtime output, but slightly delayed due the buffer filling.


Latency is no problem for this application. I think what you described is what I'm already doing:

  • Start SPI-DMA Rx to buffer A
  • SPI-DMA transfer-complete ISR: start UART-DMA Tx of buffer A and SPI-DMA Rx to buffer B
  • SPI-DMA transfer-complete ISR: start UART-DMA Tx of buffer B and SPI-DMA Rx to buffer A

The problem is that in the ISR I check to see if the previous UART-DMA Tx has completed before I start the SPI-DMA Rx targeting the same buffer and unless I up the baud rate to ~2.4Mbps I have to wait for UART (ie drop ADC samples). Strangely if I do the UART Tx in software (a tight loop sending each byte to the UART peripheral) I only need 2.0Mbps baud to finish in time.


Usually starting/stop the peripheral wastes quite a lot of time, so sending one byte at a time could cause it to be very inefficient, but DMA should completely saturate your uart doing gap-less transfers.


Understood, which is why I'm suspicious that something is wrong with my DMA code if sending byte-by-byte in software is faster.

FWIW this was my ITM testing code:

Code: [Select]
void ITM_SendWord (uint32_t word)
{
if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) &&      /* ITM enabled */
((ITM->TER & 1UL               ) != 0UL)   )     /* ITM Port #0 enabled */
{
  while (ITM->PORT[0U].u32 == 0UL)
  {
__NOP();
  }
  ITM->PORT[0U].u32 = word;
}
}

uint32_t RateControl_thresh = 200; //raise to throttle send-rate
uint32_t RateControl_count = 0;

int main(void)
{
  while (1)
  {
  RateControl_count++;
  if (RateControl_count == 1) {
  ITM_SendWord(0xFEDCBA98); //dummy sample
  }
  if (RateControl_count == RateControl_thresh)
  {
  RateControl_count = 0;
  }
  }
}

I had no trouble hitting the send rate I need using ITM, with MCU cycles to spare, just not without a ton of corruption. Doesn't this implicate the physical transport or interface as opposed to the MCU being overloaded?

I think I'm going to run a similar test with DMA-UART using a big buffer of dummy samples sending on repeat. If that doesn't work there would seem to be little point in trying to get anything to work with UART.
« Last Edit: February 19, 2022, 03:35:43 pm by davegravy »
 

Offline errorprone

  • Contributor
  • Posts: 39
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #14 on: February 19, 2022, 04:38:39 pm »
Adding another buffer if you have the memory can help with having the UART finish before the SPI transfer.  https://en.m.wikipedia.org/wiki/Multiple_buffering
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #15 on: February 19, 2022, 05:34:08 pm »
Just for curiosity, I tried to do it in a STM32F411 (A lot more basic that yours).

The main problem here is that you can't do it with a single dma request.
Ex. a single ADC trasnfer (might be 16 or 32bit transfer) needs to be broken into multiple 8-bit transfers for the UART.
And the UART DMA needs a request for each one of these transfers.

So you must do somthing to generate those multiple DMA requests between each ADC transfer. I used timers.

This is how I got it working without any cpu intervention, no buffers, no interrupts:

- Timer 2 TRGO signal drives the ADC DMA request, transfers the adc result into "ADC_VALUE" variable.
   - DMA: Circular, peripheral to mem, half-word both, no increase (Updates the same memory variable).

- Timer 3 is configured to run exactly at (n sending bytes) times faster the speed of Timer2, uses the dma stream associated with T3_Update signal.
   - DMA: Circular, mem to peripheral, byte size in both, mem increase.รง

- Timer 3 DMA belongs to DMA1, this controller can only access to APB1 peripherals (At least in F4 series), so in this case USART2 was used.

- At 96MHz, the timer period for 25.6KHz is 3750-1.
  To send 2 bytes, Timer 3 period is 1875-1, but 4 isn't a clean divisor (You need to have both timers perfectly synced).
  So in this example I send 5 bytes for every ADC conversion, Timer 3 period is 750-1

Then:
- Start ADC (Doesn't really start, waits for TRGO signal)
- Clear both timers counters and flags.
- Start DMA channel attached to Timer 3 (Doesn't start until timer3 triggers it)
- Enable Timer3 DMA update signal
- Start Timer2 and Timer 3 (Doesn't need interrupts)

Now you'll get this:
Code: [Select]
#< -------------- ADC  CONVERSION  ------------- >#< -------------- ADC  CONVERSION  ------------- >#
#< TX[4] >|< TX[3] >|< TX[2] >|< TX[1] >|< TX[0] >#< TX[4] >|< TX[3] >|< TX[2] >|< TX[1] >|< TX[0] >#

Pros:
- Absolutely zero cpu intervention, no interrupts required, not even for the DMA (In circular mode, it will reset itself)
- I've tested it using 1.5MBit USART, 25.6Ksps ADC, and sending 5 bytes for sample (5*25.6K=1.28Mbit), worked perfectly fine.

Downsides:
- Transmits in Little-endian (lower bytes first).
- The sync between timers must be perfect, or it will drift and start sending mixed old/new readings.


Your STM32 is a lot more powerful, so it might be able to do it in a better way, it should be pretty easy to adapt the method to your needs.

Attached simple demo as reference.
« Last Edit: February 19, 2022, 06:09:56 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #16 on: February 19, 2022, 07:33:42 pm »
I haven't had a chance yet to review errorprone's link or DavidAlfa's latest reply in detail, reporting for now some testing I just finished.

I removed the SPI Rx side of the equation for simplicity to see what throughput I could achieve with UART Tx alone, using DMA. I set this up in a blank project: I sent 65532 dummy bytes per transfer triggered by software (and retriggered in the transfer-complete ISR). I was able to increase the configured UART baud to  5.76Mbps and my measured throughput was 141700 samples/sec (32bits + 8 start/stop = 40bits per sample). No CRC errors.

So I think I've satisfied myself that the interface is capable of doing what I need, but created some confusion: why in my other project am I getting corruption at much lower baud rates?

A big difference in the original project is that SPI and UART are sharing DMA  - could be a bottleneck and/or source of corruption? Possibly there's an issue with my control code managing the two buffers and I need to debug with, as an earlier commenter suggested, GPIO toggling and inspecting via scope.
« Last Edit: February 19, 2022, 07:36:34 pm by davegravy »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #17 on: February 19, 2022, 11:37:49 pm »
* Edit*
I missread something, cleaned this up as it was completely useless and leaded to confusion.


Is the sample frequency critical? Must be exactly 25.6Ksps or can it be slightly flexible, ex.27ksps or 24ksps?
« Last Edit: February 20, 2022, 04:10:50 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #18 on: February 20, 2022, 12:04:47 am »
For sure, you need great control there to avoid any issues.
But the worst is that you can't run spi and uart DMA transfers concurrently.

That's good information to know, I've been trying to have the DMA transfers be concurrent. I assumeed since they occupied different channels this would be possible, and that the MCU would time-multiplex as necessary across a shared bus. My control code is focused on making sure SPI isn't writing to the buffer UART is currently reading from, not on making sure only one DMA transfer is happening at a time.

All the time you're reading 64K from spi, you can't use the uart.
And when uart is sending data, spi will be idling.

If this is true then I guess it doesn't make sense to try to read more than one sample at a time from SPI and send more than one sample at a time to UART.

I expected ADC samples to come constant in time to reflect what's really happening to the signal, not in bursts.

They are constant, or evenly spaced in time.

DMA half-transfer and transfer complete interrupts are very useful, so you can processs each half of the bufer while keeping a smooth transfer rate.
I'd definitely use different DMA channels for each peripheral.
Is the sample frequency critical? Must be exactly 25.6Ksps or can it be slightly flexible, ex.27ksps or 24ksps?

As long as the sample rate is constant it is flexible. This is an external ADC and the sample rate is driven by an external crystal osciallator, so I don't have much control over the sample rate without making hardware changes. Why do you ask about slight changes to sample rate?
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #19 on: February 20, 2022, 12:06:26 am »
I ask mainly for the posibility of using a timer to trigger the spi transfers, or setting the spi frequency lower and letting the dma in free-running, circular mode, so it's the spi frequency limit who makes the sampling rate by itself.

I'd definitely use different DMA channels for each peripheral, allowing this:
  • Start SPI-DMA Rx to buffer A in circular mode
  • Wait for SPI DMA half-transfer complete interrupt
  • Start UART TX transfer from buffer A, pointing at first half, sending 1/2 its size, not circular mode so it stops by itself
  • Wait for SPI DMA full-transfer complete interrupt
  • Start UART TX transfer from buffer A, pointing at second half, sending 1/2 its size, not circular mode so it stops by itself
  • Every SPI half/full transfer interrupt, set some flag to let the software know it's time to process 1st or 2nd part of the buffer.
« Last Edit: February 20, 2022, 12:14:31 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #20 on: February 20, 2022, 04:16:20 am »
I ask mainly for the posibility of using a timer to trigger the spi transfers, or setting the spi frequency lower and letting the dma in free-running, circular mode, so it's the spi frequency limit who makes the sampling rate by itself.

I'd definitely use different DMA channels for each peripheral, allowing this:
  • Start SPI-DMA Rx to buffer A in circular mode
  • Wait for SPI DMA half-transfer complete interrupt
  • Start UART TX transfer from buffer A, pointing at first half, sending 1/2 its size, not circular mode so it stops by itself
  • Wait for SPI DMA full-transfer complete interrupt
  • Start UART TX transfer from buffer A, pointing at second half, sending 1/2 its size, not circular mode so it stops by itself
  • Every SPI half/full transfer interrupt, set some flag to let the software know it's time to process 1st or 2nd part of the buffer.

If I understand correctly the transfer size is 1 word, so I am sending 16 bits, then in the SPI-DMA half-complete ISR initiating the UART-DMA transfer for those 16 bits, then in the full-complete ISR initiating the UART-DMA for the final 16 bits? How does this avoid SPI and UART colliding for DMA? At half-complete isn't SPI still using DMA since the transfer is not finished? Or is it OK to have this (I read a few posts saying two simultaneous DMA xfers are ok and will interleave themselves on the bus)?

I thought about your STM32F411 example from a few posts ago (which is a different idea than the above, right?). I am not using an integrated ADC so would need to adapt this a bit. My ADC generates a Data Ready Line (DRL) pulse and the falling edge indicates new SPI data available. I already trigger my SPI receive with this input but I could also have it trigger a one-pulse TIM with an appropriate width which could trigger the UART DMA Tx after enough time when SPI DMA should be complete. Since it would be referenced to the DRL pulse there should be no issue with sample rate synchronization like you worried.
« Last Edit: February 20, 2022, 04:28:47 am by davegravy »
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #21 on: February 20, 2022, 04:49:06 am »
The idea of doing smaller more frequent transfers made me think to try my existing code scaled to do one sample instead of chunks of ~13k. It seems to be running fine now at 25600samples/sec (UART baud = 2.1Mbps) even though there are many more interrupts this way.

I suppose the way my code is setup with the SPI DMA ISR triggering UART, with only four 1 byte transfers to do UART completes before the next SPI Rx so no DMA sharing occurs.
« Last Edit: February 20, 2022, 04:54:59 am by davegravy »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #22 on: February 20, 2022, 07:54:47 am »
Multiple DMA can work simultaneously, there's a bus arbitrer for that.
What's the ADC chip? The ready signal would be useful if the sampling rate is close to your needs.
But if much faster, I guess you can simply read at any time, the value of the last conversion result should be there.

Anyways, that's not so easy for spi read.
For uart, it's easy as writing to DR, but spi requires sending dummy first to generate the clock pulses, then reading DR.

You can run a spi DMA receive shot of n bytes, but again this will work as you're actually doing: Superfast burst of data, instead constant sampling rate.

As I said earlier, if you can adjust the spi clock close to 820KHz, that would give an effective transfer rate of 25.625KHz.
Perhabs 500KHz (15.62Ksps) or 1MHz (31.25Ksps) clocks are easier to configure. Will have a look to the RM later.

But if that's possible, that would remove a lot of hassle regarding the timing control, removing the need of using timers or external signals, simply free-running the spi dma.

For example, this is how I2S sampling rate works. No timer or anything like that, just adjusting the clock to match the final rate.

Edit: Hmm, at 160MHz, the closest options are prescaler 128(1.25MHz, 39Ksps) or 256(625KHz, 19.5Ksps).
« Last Edit: February 20, 2022, 10:20:11 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #23 on: February 20, 2022, 03:40:17 pm »
Multiple DMA can work simultaneously, there's a bus arbitrer for that.

Maybe I misunderstood you before, I thought you suggested only one DMA transfer should be active at a time. I get better performance this way for some reason.

What's the ADC chip? The ready signal would be useful if the sampling rate is close to your needs.
But if much faster, I guess you can simply read at any time, the value of the last conversion result should be there.

It's the LTC2512-24. Note that I'm using a 32x decimation factor (819.2kHz MCLK divided by 32 is 25.6kHz). All SPI SCK activity has to occur within about 720ns after the falling edge of DRL or else it causes noise. So SCK needs to be 45MHz or higher to complete 32bits in this time window.

I implemented as a single free-running SPI DMA transfer with receive-only (simplex). Normally SCK would be continuous in this scenario but it works here because I gate the transfer with READY. READY is generated by TIM one-pulse triggered DRL falling edge and the pulse width is ~720ns or SCK period*32.  See attachments.

Your example made me think I could use a 2nd TIM one pulse (longer pulse) to trigger UART for each sample if I do this sample-by-sample instead of in frames of samples.


 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: STM32 - USART interfacecan't keep up with SPI
« Reply #24 on: February 20, 2022, 03:59:41 pm »
Edit: I missread you here:
A big difference in the original project is that SPI and UART are sharing DMA  - could be a bottleneck and/or source of corruption?

Somehow I had read you were sharing the same DMA channel.
Obviously that was a big no-no, cleaned up upper post.

Sorry for the confusion!

I was trying to use a timer to read from the spi but couldn't get it working... Yet trying.
« Last Edit: February 20, 2022, 04:17:45 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf