STM32 DAC Complete Guide: Theory, Configuration, and Example

STM32 DAC Complete Guide: Theory, Configuration, and Example


6 minute read

Introduction

Digital-to-Analog Converters (DACs) are essential peripherals in embedded systems, enabling microcontrollers to generate analog signals from digital values. In STM32 MCUs, the STM32 DAC peripheral allows for precise voltage output, making it useful for applications such as audio signal generation, waveform synthesis, and control systems. Unlike PWM-based methods, which require filtering to produce smooth analog signals, the DAC provides direct and continuous voltage output.

This article covers the fundamentals of the DAC peripheral, its configuration using STM32CubeMX, and how to generate a sine wave using DMA and a timer trigger.

STM32 DAC Main Characteristics

The DAC peripheral in STM32 microcontrollers converts digital values into corresponding analog voltages. It operates with a reference voltage (Vref), typically tied to the supply voltage (Vdd), and produces an output voltage proportional to the input digital code.

Key Features of STM32 DAC

  • Supports 8-bit and 12-bit resolution, allowing fine control over output voltage.
  • Depending on the MCU variant, it can operate in single or dual-channel mode.
  • Multiple trigger sources: Software, Timer, DMA, or external events.
  • Buffered output to drive external circuits with stable voltage.
  • DMA compatibility for continuous waveform generation with minimal CPU load.

Basic Working Principle

The DAC works by converting a digital input (D) into an analog output voltage (Vout) using the formula:STM32 DAC Formula

Where:

  • D is the digital value (0 to 2-1). We will use 12-bit resolution, so this value will vary between [0, 4095]
  • n is the resolution (8-bit or 12-bit). In this tutorial, we will use 12-bit resolution
  • Vref is the reference voltage. Usually, it is equal to 3.3 V

For example, in a 12-bit DAC with Vref = 3.3V, a digital value of 2048 (midpoint) results in:

Vout=2048×3.3V / 4096=1.65V 

The DAC output can be automatically updated continuously through software or DMA, making it ideal for waveform generation.

STM32 DAC Basic Configuration and Usage

To configure the DAC, we open the CUbeMx file and choose DAC among Analog peripherals. Then, we have to connect the output of DAC to an external pin. In my case, I connected both channels to the external pin. Afterward, we can use parameter settings to configure the DAC, but default values are sufficient for using the STM32 DAC.

STM32 DAC CubeMx Configuration

Then, you can open the GPIO settings to find the pins where the analog signal will be generated. 

STM32 DAC Pins

Then, we can generate code and turn our attention to coding. As shown in the code snippet below, we start the DAC by calling the HAL_DAC_START function. The first argument is the DAC handler, which is hdac1 in our case. The second argument is the channel you want to use: DAC_CHANNEL_1 or DAC_CHANNEL_2. In the code below, I am enabling channel 2

To set a value, we use HAL_DAC_SetValue. As discussed before, the DAC resolution is 12-bit, and the value 4095 corresponds to the maximum voltage the DAC can produce. Intermidiary values provide analog signals ranging from zero to the reference voltage. 

Within the while loop, I set the maximum and zero voltage with a 2-second delay. This way, I can verify that DAC is functional. You can set any value between 0 and 4095 to obtain the analog voltage you need. 

uint16_t dac_value; 
HAL_DAC_Start(&hdac1, DAC_CHANNEL_2); 
/* USER CODE END 2 */ 
/* Infinite loop */ 
/* USER CODE BEGIN WHILE */ 
while (1) 
{ 
    dac_value = 4095; 
    HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, dac_value); 
    HAL_Delay(2000); dac_value = 0; 
    HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, dac_value); 
    HAL_Delay(2000); 
    /* USER CODE END WHILE */ 
    /* USER CODE BEGIN 3 */ 
}






STM32 DAC DMA and External Trigger for Sine Generation

In the last section, we learned how to enable DAC and set the analog signal value. However, we aim to stream the analog signal with perfect timing instead of just setting a single value. For example, generating a sine signal or streaming audio data are applications when data streaming to DAC is essential. This will be our main topic in this section of the article. 

To implement this approach, we open the DAC parameter settings and enable the Trigger, as shown in the figure below. I will use TIMER 4 to periodically update the DAC value.  

STM32 DAC Trigger enable


In addition, it is essential to enable DMA. Enable the DMA (I did it for channel 2 of DAC) within DMA settings and choose circular mode. You can keep the data width to "Half Word." The circular mode means that DMA will keep streaming the data buffer once started.

STM32 DAC DMA Enable

The next step is to configure the Timer to trigger the DAC. Since I am using TIM4 for that purpose, I define the timer source as "Internal Clock" to enable the timer. Afterward, setting the right prescaler and counter period values is essential for the correct functioning of the DAC. Because the clock frequency is 80 MHz, prescaler 79, and counter period 999 provide the update event frequency 1 kHz (80,000,000 / ((79 + 1) * (999 + 1)) = 1000). To understand more about these parameters, you can refer to an article about timers:

STM32 Timer tutorial

Finally, we must enable the trigger by selecting "Update Event" as TRGO. So, when we enable the timer, it will trigger DAC at 1 kHz. Check the screenshot below to see the summary of all these steps.

STM32 Timer Trigger Enable

Finally, we can generate the code and start coding. To stream data, I will create a buffer holding a sinusoidal signal. 

First, I define complimentary macros: DAC frequency (1 kHz as we discussed), signal frequency, and the buffer length. Also, do not forget to include math.h library for later use

#include <math.h>

#define DAC_FREQUENCY     1000 
#define SIGNAL_FREQUENCY  5 
#define DATA_LENGTH       DAC_FREQUENCY / SIGNAL_FREQUENCY






To trigger the DAC, I enable Timer 4. Then, I define the sine function using the for loop (sin(2*pi*f*n/Fs)). In addition, I have to shift up to avoid negative values. 

Finally, I start DMA by calling HAL_DAC_Start_DMA function.

HAL_TIM_Base_Start(&htim4); 
uint16_t dac_value[DATA_LENGTH]; 
for(int i = 0; i < DATA_LENGTH; i++) 
{ 
    dac_value[i] = 4095 / 2 + (4095 / 2) * sin(2 * 3.1415 * SIGNAL_FREQUENCY * i / DAC_FREQUENCY); 
} 
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_2, (uint32_t *)dac_value, DATA_LENGTH, DAC_ALIGN_12B_R);

Finally, we can check this code by connecting the LED to the corresponding pin of DAC output. The LED has to blink at 5 Hz. 

ALSO, PAY ATTENTION THAT I USED DAC_CHANNEL_2, not DAC_CHANNEL_1. It could be DAC_CHANNEL_1 in your case.


Other Relevant STM32 Programming Articles




« Back to Blog