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. 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

Post a Comment

Popular posts from this blog

u8g2 library usage with STM32 MCU