Skip to main content
Associate
July 20, 2023
Solved

DMA to DAC transfer in normal mode too slow

  • July 20, 2023
  • 6 replies
  • 8710 views

Hi,

I'm using STM32G431RB on the NUCLEO-G431RB developement board.

I want to generate a signal from an array each time an external trigger is received.

The signal is sampled with the TIMER6 trigger, which is rised every 1us. (MCU clock is 170MHz, TIMER6 PSC is 0 and ARR is 170-1).

TIMER6 configurationTIMER6 configuration

The DMA is set to normal mode (because I want to generate a signal only when the trigger comes, and not continuously)

DAC DMA configurationDAC DMA configuration

 The DAC is configured like this:

DAC configurationDAC configuration

 I'm calling the HAL_DAC_Start_DMA function each time the external trigger rises, but, even if the trigger is repeated each 1ms, the signal is generated each 2ms. The minimum interval between a signal generation and another one is 2ms and basically I need to reduce it as much as possible.

SignalSignal

 

Here the trigger is faster than 2ms but the signal is generated each 2msHere the trigger is faster than 2ms but the signal is generated each 2ms

 The code:

/* Private variables ---------------------------------------------------------*/

DAC_HandleTypeDef hdac1;

DMA_HandleTypeDef hdma_dac1_ch1;

TIM_HandleTypeDef htim6;

/* USER CODE BEGIN PV */
uint8_t dac_flag = 0;
uint32_t data[41] = {2048, 2148, 2248, 2348, 2448, 2548, 2648, 2748, 2848, 2948, 3048, 2948, 2848, 2748, 2648, 2548, 2448, 2348, 2248, 2148, 2048, 1948, 1848, 1748, 1648, 1548, 1448, 1348, 1248, 1148, 1048, 1148, 1248, 1348, 1448, 1548, 1648, 1748, 1848, 1948, 2048}; //Signal lookup table
/* USER CODE END PV */

int main(void)
{
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* Configure the system clock */
SystemClock_Config();

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_DAC1_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim6);

//Initial value 
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);
HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);

 

while (1)
  {
     if(dac_flag)
    {
        dac_flag = 0;
        HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, data, 41, DAC_ALIGN_12B_R);
     }
  }
 
...
 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   if(GPIO_Pin == STAGE_TRIG_Pin)
  { 
      dac_flag = 1;
  }
}
 
It seems that the DMA "recharge" is blocking the next transfer. Is there something to do in order to speed up this process?
 
Thank you in advance.
This topic has been closed for replies.
Best answer by MasterT

I see. Still there is option to set HAL_DAC_DMA only ones ( assuming it's most slow subroutine) , and in the interrupt do start/stop Timer-6. Or even better configure tim-6 to trigger on external event in one time mode or with one more timer as 50 usec pulse generator for tim-6 waiting in Gated-mode.

6 replies

MasterT
Lead II
July 20, 2023

"The DMA is set to normal mode (because I want to generate a signal only when the trigger comes, and not continuously)"

I see misunderstanding how dma works. DMA is not a master, but submissive in this configuration. TIM -> DAC - DMA.

So, dma is waiting request from the dac, and setting "circular" mode is the right way to do . Than you start all process only ones, speed limits I tested with G-474re about ~14 msps with 170MHz clock

Associate
July 21, 2023

Thank you MasterT for your answer.

By setting the DMA in circular mode I don't obtain the wanted behaviour. I mean: I don't want to generate continuously the waveform, I want to generate the signal only when the external trigger rises. The signal duration is about 50 us, and the trigger can come in any moment. Actually if the trigger comes before 2ms from another one it will be ignored which is bad form my application.

MasterT
MasterTBest answer
Lead II
July 21, 2023

I see. Still there is option to set HAL_DAC_DMA only ones ( assuming it's most slow subroutine) , and in the interrupt do start/stop Timer-6. Or even better configure tim-6 to trigger on external event in one time mode or with one more timer as 50 usec pulse generator for tim-6 waiting in Gated-mode.

waclawek.jan
Super User
July 21, 2023

Set a GPIO pin when you set dac_flag, and clear it in main() when you test it; and observe.

An interesting test might be also to toggle a pin in the main() loop and observe.

JW

Associate
July 21, 2023

I tried to continuously call HAL_DAC_Start_DMA in the while loop followed by a HAL_GPIO_TogglePin and the result is:

0.PNG

 

1.PNG

 

waclawek.jan
Super User
July 21, 2023

> I tried to continuously call HAL_DAC_Start_DMA in the while loop followed by a HAL_GPIO_TogglePin 

How? I don't understand. Please post relevant code.

JW

MMatj.1
Associate II
August 17, 2023

Hi!

I had this problem also and it is because of insane 1ms(SysTick) delay ( ! BUG ! ) in HAL_DAC_Start_DMA() !
This bug even prevent using this function in interrupt with higher priority than SysTick .. 
I had to use own copy implementation of this function with commented out "HAL_Delay(1);".
It allowed me to start next DAC DMA transfer in transfer completed callback without any noticeable delay in output signal.

 

 

 

...
 if (status == HAL_OK)
 {
 /* Enable the Peripheral */
 __HAL_DAC_ENABLE(hdac, Channel);
 /* Ensure minimum wait before using peripheral after enabling it */
 // HAL_Delay(1); // Commented out insane delay !
 }
 else
 {
 hdac->ErrorCode |= HAL_DAC_ERROR_DMA;
 }
...

 

 

 

 

waclawek.jan
Super User
August 17, 2023

WHAT?

@Amel NASRI, can this please be explained/fixed?

Thanks,

JW

waclawek.jan
Super User
August 17, 2023

It's apparently consequence of incorrectly handled/misunderstood remark from the RM:

waclawekjan_0-1692284143462.png

The delay according to 'G431 DS is:

waclawekjan_1-1692284532642.png

but, as the highlighted remark above indicates, this delay is only in the analog part, nothing prevents the digital part to work normally.

So, to generate a quasi-continuous waveform, it's not just matter of removing the delay, but you should also avoid switching the DMA [EDIT see post below] DAC [/EDIT] off and on.

But, at the end of the day, this is just another case to repeat my mantra: Cube/HAL - as inevitably any "library" - implements only a miniscule fraction of what the hardware is capable of, the arguable "usual cases". And Cube/HAL specifically is primarily a vehicle to allow clicky configuration in CubeMX. If your use case is outside of these "usual cases", Cube/HAL gets into way more than helps. This is not fault of Cube/HAL; it is what it is and using it you agreed to accept its shortcomings.

If you mean developing with STM32 seriously, you are better off using registers directly (no, not Cube/LL, that's mostly just renaming registers thus another unnecessary obstacle). DAC is trivial, timers are relatively easy, DMA/DMAMUX is maybe a bit more involved but in the long run the investment in learning pays off.

JW

MMatj.1
Associate II
August 17, 2023

@waclawek.jan wrote:
So, to generate a quasi-continuous waveform, it's not just matter of removing the delay, but you should also avoid switching the DMA off and on.

I disagree! It's normal to turn Off and On DMA, especially when transfer completed.
It's impossible to change configuration without turning DMA off (memory address / size / etc).
And there are DMA callbacks (interrupts) of Half Complete/Complete, to prepare new data, to switch buffers, etc.

I would agree the same about DAC , it's better to initialize / turn it on once and later update values when needed, or add wait check for DAC become ready after turning it on.

@waclawek.jan 
Thank you for pointing right way DACxRDY in DAC_SR register for implementing delay instead of insane 1 SysTick :grinning_face:

waclawek.jan
Super User
August 17, 2023

Sorry, my bad, I meant, you should not switch *DAC* off/on (i.e. you should remove __HAL_DAC_ENABLE() / __HAL_DAC_DISABLE() from the functions you are using to start/stop DMA). I've corrected above.

Btw. I went through the Cubes and I found the delay only in 'G4 and 'U5 HAL, whereas the analog part setting (tWAKEUP) is - logically - present and of similar duration in other STM32 families which have DAC, too.

JW