Usage DMA and DAC in STM32CubeMX


ST released new HAL library, together with STM32CubeMX codegeneration tool. They are good things, but still have too few documentation and articles in the Internet.

I've tried to use DAC in couple with DMA in my board STM32F4Discovery. So, I'll describe the whole process of developing sine wave generator using STM32CubeMX utility and IAR.

First of all, we'll start new project in cube. We have "board Selector" tab, where we select our STM32F4discovery board.


We set all not necessary pins at reset state - we do not neet it.

We set all not necessary pins at reset state - we do not need it. And set PA4 as DAC_OUT1. Now picture is:




What else we need? We need timer, which will send requests to DMA controller. I've chosen TIM6. To enable it, in "Configuration" tree, select "Peripherals->TIM6->Activated" branch and check it.

Ok, now move to configuration tab. We need to tune TIM6, DAC and DMA.
For TIM6 
We need to set:
  • prescaler and trigger event(just press TIM6 button):

For DAC
We need to set:
  • on parameter settings: Trigger = Timer 6 Trigger Out event
  • on NVIC Settings set enabled global interrupt
  • on DMA settings:
    press "add", then set settings as on picture.Do not forget set "Mode" as "Circular" and set "data width" as "half-word"!
        "Circular" means that after the counter reaches the end of array, reading starts on the end. "Data width" - means "on pieces,    of which length source array sliced will be". So, our sine wave will be stored in uint16_t array, so we need half-word mode(16 bit).    32 bit - is a word.
    Now, save project and press on gear button in toolbar - this generates code. Open project in IAR.





So, this is almost all. And now we'll see another wonderful side of CubeMX - the amount of additional code is just 4 lines:
first, we need to make additional global variable at main.c file(look to /* USER CODE BEGIN PV */ section, created by CubeMX code)
here we'll place

 /* USER CODE BEGIN PV */  
 const uint16_t sine_wave_array[32] = {2047, 1648, 1264, 910, 600,  345,   
                    156, 39,  0,  39,  156,  345,  
                    600, 910, 1264, 1648, 2048, 2447,  
                    2831, 3185, 3495, 3750, 3939, 4056,  
                    4095, 4056, 3939, 3750, 3495, 3185,  
                    2831, 2447};  
 /* USER CODE END PV */  


and in main() function we'll add

  /* USER CODE BEGIN 2 */  
  HAL_TIM_Base_Start(&htim6);  
  HAL_DAC_Start(&hdac,DAC_CHANNEL_1);  
  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sine_wave_array, 32, DAC_ALIGN_12B_R);  
  /* USER CODE END 2 */  


  •  first line - starts the timer
  •  second - starts DAC
  •  third - starts DMA and assign our array to DMA.

finally, our main() function looks like:
 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_DAC_Init();  
  MX_TIM6_Init();  
  /* USER CODE BEGIN 2 */  
  HAL_TIM_Base_Start(&htim6);  
  HAL_DAC_Start(&hdac,DAC_CHANNEL_1);  
  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sine_wave_array, 32, DAC_ALIGN_12B_R);  
  /* USER CODE END 2 */  
  /* USER CODE BEGIN 3 */  
  /* Infinite loop */  
  while (1)  
  {  
  }  
  /* USER CODE END 3 */  
 }  

Comments

  1. Great tutorial on the DAC DMA process. Thank you for posting. There is an error for the name of the array in the main loop DMA start function. Should read "sine_wave_array":
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sine_wave_array, 32, DAC_ALIGN_12B_R);

    ReplyDelete
  2. I was wondering if this code is also applicable to Nucleo Boards (such as the STM32F3 in my case) not just the Discovery Boards. I created a code using your steps as a guidance. Everything is the same other than the fact that the only pin I have configured is the the DAC_OUT1 at PA4 (so our pin out diagrams would look different). It compiled without any errors on both an IDE (SW4STM32) and online Mbed Compiler; however, I could not upload the code. Mbed created a .bin file yet LED1 never blinks to indicate it is uploading also no signal out of PA4. For the SW4STM32, LED1 never stops blinking and there is no signal out of PA4. Baud rate was set to 9600bit/sec. I am not sure if you encountered this issue. It would be deeply appreciated if you would reply. Thank you.

    ReplyDelete
    Replies
    1. Did you tested simple "blinker" project with your environment? As I remember, Nucleo and Discovery have different versions of ST-Link. Check them both: Run->Debug Configurations-> Debugger. There you can select "Manual Spec" radiobutton, and select "ST-LinkV2" or "ST-LinkV2-1". Anyway, I have stm32f4disco and nucleo401, both works well in SW4STM32.

      Delete
    2. Thank you so much for your reply. I have not tested a simple "blinker" project since it has not been provided unlike Discovery Boards which come with it. I attempted with both ST-LinkV2 and ST_LinkV2-1. Same results unfortunately. I debug as Ac6 Application with Current toolchain to Ac5 SSTM32 MCU GCC and current builder: Gnu Make Builder. I am not sure if the path is causing this behavior. If you have any more suggestions it would be much appreciated.

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I spent about 10 hours trying to figure this out and failed. Your comment about the poor documentation provided by STMicro is so right! Thank you.

    ReplyDelete
  5. what sampling rate the timer will generate?..
    Kindly help how to generate exact 44.1Khz sampling rate which is very essential in my project.

    ReplyDelete
    Replies
    1. I do not know any other way except calculating prescalser, period and clock of timer by hand. Here https://electronics.stackexchange.com/questions/132000/stm32-series-microcontroller-calculations-of-timer-variables you can found that author wrote code to calculate appropriate values, but I do not know, if it is ok.
      Also, to get perfect clocking, you chose crystal oscillator with another frequency, to get 44.100 as perfect fraction of some audio sampling freq(for example, there is 22.5792 MHz quartz, which is 512×44.1 kHz, or 14.112MHz= 320x44.1 kHz). The full list of oscillator freqs is here: https://en.wikipedia.org/wiki/Crystal_oscillator_frequencies.
      And last option: there is timer clock calculator tool: https://libstock.mikroe.com/projects/view/398/timer-calculator .I have never used I. but you can try and wrote, if you get some results:)

      Delete
  6. Hey, very clear tutorial, thanks! Is there some way I could update value of DAC myself in some interrupts or even main loop, to manipulate frequency during work of program?

    ReplyDelete
  7. This did not work for me using CubeMX Ver. 4.27

    It seems to create a FIFO enable error.

    I ended up ENABLING FIFO to get it to work.

    See here more about it..

    https://community.st.com/s/question/0D50X00009XkgxPSAR/fifo-error-from-dma-to-dac-caused-by-cubemx-when-can-it-be-fixed?t=1541902641663

    ReplyDelete
  8. Working just fine, thanks a lot!

    ReplyDelete
  9. Greetings EveryOne!

    I am having a problem if I don't declare a sine_wave_array table as const. DMA failed to read the data from RAM to DAC. My requirement is to update sine_wave_array table at run time and DMA should take the data from the table and forward to DAC. Any way to do this?

    Any help would really appreciated.

    Thanks.

    ReplyDelete
    Replies
    1. Same occurred for me! Any ideas how to solve this issue?

      The array must be defined as a const in order to allow the DMA to read it.

      Delete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Nice tutorial.
    But I try this with a wave file readed from SD Card. I read step by step 256 Byte in a uint16 array. but it doesn't work, because i cann't set a fix array like you in your example.
    Did you know any solution for this problem?

    ReplyDelete
  12. Thank you for posting.
    It was very helpful.
    Is it necessary HAL_DAC_Start Function?
    It isn't necessary in My Developing Environment (STMF7)

    ReplyDelete
  13. You may want to change up the order here:

    /* USER CODE BEGIN 2 */
    HAL_TIM_Base_Start(&htim6);
    HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sine_wave_array, 32, DAC_ALIGN_12B_R);
    /* USER CODE END 2 */

    to instead do the timer start last.

    It seems to break down if you use a small enough timer divisor to get a high sample rate. For example on a Nucleo STM32H745 with 400 MHz CPU and 100MHz APB clocking, no wave makes it out if the timer is started before the DAC is set up, at a timer period of about 200 or lower. i.e. the DAC start is racing against the timer. By moving the timer start to the end I was able to go to the higher sample rates. Was banging my head seeing docs suggesting that an H7 could do 18MSPS out the DAC, but was not getting anywhere near that until I figured this out.

    Very very helpful sample though!

    ReplyDelete
  14. I think there might be a little off-by-one error in the timer setup.

    On my board with 100MHz(10ns) APB clock, to get to 160ns per sample, which is 16 bus ticks per sample put into the DAC, I had to set the timer period to 15 instead of 16. Is the triggering event generated when the timer counter is *equal* to the period, or *exceeds* it ? Empirical fiddling suggests the latter, at least on this board and chip.

    32-sample sinewave x 160ns per sample out = 5.12 usec sinewave period = 195.3KHz tone
    The only way I can get close to that freq as observed on my DSO is by setting period of the timer to "15" instead of "16".

    ReplyDelete
  15. OK this link is super relevant: the period *and* the prescaler into the timer can both trip you up.

    https://community.st.com/s/question/0D50X0000ArReBK/help-configuring-stm32h7-timer

    ReplyDelete
  16. You may also take a look at the STM32F7 DAC TUTORIAL available on:
    https://www.researchgate.net/publication/354022135_STM32_DAC_TUTORIAL_with_application_to_system_identification

    ReplyDelete

Post a Comment

Popular posts from this blog

u8g2 library usage with STM32 MCU

Use PCM5102 with STM32

RFFT in CMSIS DSP. Part 1.