cancel
Showing results for 
Search instead for 
Did you mean: 

With a STM32L4, I would like to use one channel of DMA1 to read the ADC and another channel to transfer the buffer from memory to UART. Basically, I would like to have some sort of oscilloscope (for positive values only and below 1 MHz signal).

LuizGEnger
Associate II

Which is the best approach to manage the use of DMA and "control" the flow of data? Implementing some interrupt when the buffer is filled? Is it possible to put one channel of the DMA on "hold" while the other has full priority? Or two DMA are required for this?

At the moment, transmitting the buffer through UART without DMA is acceptable, I can have the CPU occupied for this. Should I then leave the ADC DMA as normal or circular? And then call HAL_UART_Transmit after the buffer is filled. I understand that if I leave the ADC DMA in circular mode, I'll lose some data during the UART transmission, but that may also be acceptable at the moment.

The "first version" I think would be to fill the buffer using ADC DMA in normal mode, once the buffer is filled I transfer it through UART without DMA and then "restart" ADC DMA scan. Which are the key functions to do so?

I'm new to STM32 programming, so any help is appreciated.

Thanks in advance!

Kind regards,

Luiz Guilherme Enger

7 REPLIES 7
LuizGEnger
Associate II

I can already read the signal and fill a buffer using ADC DMA, but then I'm now sure how to proceed.

TDK
Guru

The typical way is to put the ADC into circular DMA mode, and process each half of the buffer (i.e. transmit over UART) during the half- and full-complete flags/callbacks.

Multiple DMA streams can work at once.

Get the ADC part working alone, then get the UART part working alone, then put them together.

If you feel a post has answered your question, please click "Accept as Solution".

> Basically, I would like to have some sort of oscilloscope (for positive values only and below 1 MHz signal).

Be aware of the voltage limits on ADC inputs and the amount of bandwidth available in UART. The UART will have considerably lower bandwidth than the amount of data you can generate.

If you feel a post has answered your question, please click "Accept as Solution".
Nikita91
Lead II

Some math:

For a 1 MHz signal the Nyquist theorem require at least 2 mega samples per second.

If your samples are 8 bits you gets 16 M bits/s

On UART 1 byte need 10 bits (start + stop) => 2M * 10 = 20 Mbits/s.

Are you sure you can manage to transmit 20 Mbits/s to your PC?

LuizGEnger
Associate II

I already got ADC and UART transmission working by itself. Just putting them together sounds a little more complicated. What I would like to do is:

  • start ADC DMA reading/conversion, while CPU do other stuff
  • HAL_ADC_ConvCpltCallback calls another function that will make the CPU perform some calculations on the full buffer. During this time, my buffer should be "intact" (maybe copy the ADC DMA buffer into another one), so I thought I could "halt" the reading/conversion
  • Once the calculation is done, I transmit the results via UART and launch back the ADC DMA reading/conversion

At the moment I have something like this:

#define ADC_BUF_LEN 4096

uint16_t adc_buf[ADC_BUF_LEN];

uint8_t buffer_filled = 0;

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, ADC_BUF_LEN);

in the while(1) loop:

if (buffer_filled){

HAL_UART_Transmit(&huart2, ... )

buffer_filled = 0;

(now of course outside the while loop)

void HAL_ADC_ConvCpltCallback() {

buffer_filled = 1;

}

If ADC DMA is set to circular, I get UART data non-stop (I added some delays inside the while loop). Then I don't know if ADC DMA already started to overwrite the first half of the buffer... If I set ADC DMA to normal, I get only one reading, even if I put HAL_ADC_Start_DMA( ) again after buffer_filled = 0

About the 1 MHz frequency, that was just a lambda value. I am interested in signals around 10 kHz, so having an effect 1 Msps from ADC is more than enough. I'm aware of Nyquist theorem, thank you.

Nikita91
Lead II

You need a kind of pipe line: On each ADC buffer you need to do 4 operations:

  • Receive ADC data (DMA)
  • Copy ADC data to a work buffer (DMA)
  • Compute on the work buffer
  • Transmit the work buffer to UART (DMA)

The ADC DMA only have 2 flip/flop buffer on circular mode. Then you need temporary work buffers. You need 3 temp buffers because you have 3 operations to do on ADC data:

It     ADC       Copy       Comp        UART
 
       A0         x         x           x
ht     A1         A0->W0    x           x
ct     A0         A1 >W1    W0          x
ht     A1         A0 >W2    W1          W0
ct     A0         A1 >W0    W2          W1
ht     A1         A0 >W1    W0          W2
etc
 
A0/A1 = ADC circular buffers
W0/W1/W2 = work buffers

This works only if the compute task and the DMA transmit task are no longer than 1 ADC data buffer acquisition.

Thanks! I implemented part of it and it works. I decided to copy only the first half of the ADC DMA buffer to a work buffer (HalfConvCplt sets a flag to start the copy in the while(1) loop). At the moment the second half is ignored, just to ensure that the values the work buffer is copying won't change during the copy process. Then I'll take a look on how to transmit this work buffer through UART DMA. Using only half the ADC buffer is some waste of resource but it seems that it will ensure that the computation and transmission will be faster than the complete ADC buffer acquisition.