DAC STM32F4 Discovery Board – Generate Waveforms with Digital to Analog Converter

In this tutorial, we will learn to configure a DAC module of STM32F4 discovery with HAL DAC drivers and Keil uvision IDE. Moreover, we will learn to configure the STM32F4 DAC module manually without DMA and any trigger conversion event such as timers. We will learn to use DAC in DMA mode with a timer as a trigger conversion source in the coming articles. 

STM32F4 DAC Introduction

A Digital to analog converter is a device that converts a constant number into an analog value. Unlike ADC, a DAC produces an analog signal from a digital value. The output of a digital value depends on the Vref and digital value input to a DAC module. As you can see in this block diagram, the input signal to this converter is a digital signal and a reference voltage with respect to the ground terminal. The output is an analog signal.

DAC Block diagram - digital to analog converter

The DAC modules of STM32F4 are R-2R ladder based. Hence, the following equation is used to calculate an analog output from a numeric value (D). Here N is a width of DAC which in the case of STM32F407VG microcontroller is 12-bit. 

Vout = (Vref x D) / 2^N 

You can refer to this article to find more about digital to analog converters and their types: 

DAC Module STM32F4

STM32F4 discovery board development board comes with an STM32F407VG microcontroller. This microcontroller has two 12-bit digital to analog converters such as DAC1 and DAC2. Both these DACs can be used independently or concurrently for dual-mode. 

DAC Output Pins

Now the next question is through which GPIO pins digital to analog converter produces output? Suppose you examine the STM32F4 discovery board’s datasheet and refers to table 7 in the pinout and pin description section. In that case, you will notice that PA4 and PA5 pins provide additional functionality for DAC1 and DAC2 channel output, respectively.

  The output of DAC channel 

  • DAC channel1 with DAC_OUT1 (PA4) as output
  • DAC channel2 with DAC_OUT2 (PA5) as output

 Getting Analog Output from Digital Input Signal

As discussed in the last section, this equation is used to obtain analog voltage output from a digital numeric input value:

Vout = (Vref x D) / 2^N 

The reference voltage is usually the operating voltage of the microcontroller with reference to the ground terminal. The operating voltage of STM32F4 disovery board is 3.3V. Hence, Vref will be 3.3V. 

Note: The output voltage value depends on the Vref voltage. The analog output signal will be between 0-3.3V ( Maximum = Vref).  

The DAC module of STM324F microcontroller is of 12-bit. But it is programmable to configure in 6-bit, 8-bit, 10-bit and 12-bit mode. For example, we are using a 12-bit DAC. Let’s see the analog output voltage value for a constant value of 1000. 

For 12-bit DAC = 2^12 = 4096
Vout = (Vref x D) / 2^N = (3.3 x 1000)/4096 = 0.806V

The numeric value (D) can be any value between 0 and 4095. Hence, the output voltage will be between the minium value 0 ( D =0 ) and the maximum value 3.29V (D=4095). 

STM32F4 DAC Accuracy and Minimum step voltage

The accuracy and the minimum step output voltage of a DAC module depend on the reference voltage and size of a DAC. For example, we take 12-bit DAC and Vref equal to an operating voltage of the STM32F4 discovery board. Then the minimum step voltage will be: 

Minimum Step Voltage= (Vref x D) / 2^N = (3.3 x 1)/4096 = 0.8mV

HAL DAC Driver STM32F4 

After understanding digital to analog converters and their working, now let’s move to the programming part of STM32F4 DAC modules. In this section,we will discuss HAL DAC library of STM32F407VG microcontroller. 

To use the HAL DAC driver, HAL provides two important C structures such as DAC_HandleTypeDef and DAC_ChannelConfTypeDef. 

Let’s first discuss DAC_HandleTypeDef C struct. This structure is used to configure and manipulate the DAC peripheral of STM32F4. 

typedef struct
{
  DAC_TypeDef     *Instance;     /*Pointer to DAC base address discriptor  */ 
__IO HAL_DAC_StateTypeDef   State;     /*DAC communication state */
  HAL_LockTypeDef     Lock;          /* DAC locking object */
  DMA_HandleTypeDef     *DMA_Handle1;  /*Pointer to DMA handler for channel 1 */
  DMA_HandleTypeDef    *DMA_Handle2;  /*!< Pointer to DMA handler for channel 2 */
  __IO uint32_t         ErrorCode;     /*< DAC Error code*/
}DAC_HandleTypeDef;

Now let’s discuss members of this C struct which we will use in this tutorial. The remaining members will be discussed in the later tutorial. 

Instance : It is a pointer to the DAC module that we want to use. For example, if we want to use DAC2 or DAC1, we will pass DAC to this member. 

DAC Channel and Trigger Event Selection

The second important C struct for DAC configuration and channel selection is DAC_ChannelConfTypeDef. 

typedef struct
{
uint32_t DAC_Trigger; /*Selects the external trigger for the selected DAC channel.
 */
uint32_t DAC_OutputBuffer;  /*Specifies whether the DAC channel output buffer is enabled or disabled. */
}DAC_ChannelConfTypeDef;

DAC_Trigger:  This member selects the source of trigger for DAC conversion. In STM32F4, the source of trigger for DAC can be manual, DAM, external, or timer trigger. For example, if we set the DAC_Trigger to DAC_TRIGGER_NONE, the DAC conversion will be done manually by using HAL_DAC_SetValue() function. We will discuss it later. 

The value of different DAC Trigger selection options are listed in this table: 

ValueFunction
DAC_TRIGGER_NONE Conversion is automatic and not by external any trigger ( manual) 
DAC_TRIGGER_T2_TRGO TIM2 TRGO selected as external conversion trigger for DAC channel
DAC_TRIGGER_T4_TRGO TIM4 TRGO selected as external conversion trigger for DAC channel
DAC_TRIGGER_T5_TRGO TIM5 TRGO selected as external conversion trigger for DAC channel
DAC_TRIGGER_T6_TRGO TIM6 TRGO selected as external conversion trigger for DAC channel
DAC_TRIGGER_T7_TRGO TIM7 TRGO selected as external conversion trigger for DAC channel
DAC_TRIGGER_T8_TRGO TIM8 TRGO selected as external conversion trigger for DAC channel
DAC_TRIGGER_EXT_IT9 EXTI Line9 event selected as external conversion trigger for DAC channel
DAC_TRIGGER_SOFTWARE Conversion started by software trigger for DAC channel

DAC_OutputBuffer: Enables or disables the DAC output buffer.

ValueFunctions
DAC_OUTPUTBUFFER_ENABLE Enable DAC Buffer
DAC_OUTPUTBUFFER_DISABLE Disable DAC Buffer

DAC Initialization and Configuration Functions

Two Functions are used to configure and initialize DAC with the help of above-defined C structs such as HAL_DAC_Init() and HAL_DAC_ConfigChannel().

HAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef* hdac)

The input argument to HAL_DAC_Init() function is a instance to the pointer of struct DAC_HandleTypeDef and return type is HAL_StatusTypeDef which is initialized in this enum struct: 

typedef enum 
{
  HAL_OK       = 0x00U,
  HAL_ERROR    = 0x01U,
  HAL_BUSY     = 0x02U,
  HAL_TIMEOUT  = 0x03U
} HAL_StatusTypeDef;
HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef* hdac, DAC_ChannelConfTypeDef* sConfig, uint32_t Channel)

HAL_DAC_ConfigChannel() functions initialize the selected ADC channel. The first two input argument to this function are pointers to the instance of  DAC_HandleTypeDef and DAC_ChannelConfTypeDef structs, respectively. The third input argument is a DAC channel number. We can select on of these two values depending on channel that we want to use.

ValueFunction
DAC_CHANNEL_1 Selects DAC channel 1
DAC_CHANNEL_2 Select DAC channel 2

As we mentioned earlier, the DAC module can be used manually without any trigger source. In this tutorial, we will learn to use it manually without any trigger source. These three functions are used to start, stop, set value of STM32F4 DAC in DAC_TRIGGER_NONE mode. 

DAC Conversion Function

After initialization and configuration of DAC is to start the DAC conversion by using HAL_DAC_Start(). The input argument to HAL_DAC_Start() function is an instance to the pointer of struct DAC_HandleTypeDef and the return type is HAL_StatusTypeDef enum which is given above. A second argument is a channel number. Similarly, we can stop the conversion by calling HAL_DAC_Stop(). 

HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel);
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef* hdac, uint32_t Channel);

After DAC starts conversion by calling HAL_DAC_Start() function, we can set the value of DAC by passing numeric value to HAL_DAC_Start() function. 

HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data);

Here the first two arguments are a pointer to the instance of DAC_HandleTypeDef and the channel number, respectively. The third input argument is a data allignment parameter the value which should be selected according to DAC width. 

#define DAC_ALIGN_12B_R                    0x00000000U
#define DAC_ALIGN_12B_L                    0x00000004U
#define DAC_ALIGN_8B_R                     0x00000008U

The fourth input argument is a numeric value against which we want to a analog output value. 

In the end, we should also configure the corresponding output pin of DAC as an analog output pin. If you don’t know how to use GPIO pins of the STM32F4 discovery board, you can refer to these articles:

STM32F4 DAC Example Code

The following example code generates an analog output signal on the PA5 pin of the STM32F4 discovery board. It will produce a triangle waveform on the PA5 pin. Because we pass the numeric values to HAL_DAC_SetValue() function first in increasing order from low to the maximum value. After that, we pass the values from the highest value to the minimum value using two for loops. 

/*Include header file for STM32F4 HAL libraries and callback functions */
#include "stm32f4xx_hal.h"

/* Function prototype to configure and initialize DAC and GPIO pin */
void configure_DAC(void);
void configure_GPIO(void);
void Delay_ms(volatile int time_ms);
/* create an instance to DAC_HandleTypeDef C struct */
DAC_HandleTypeDef DAC_Config;


/* main function to set DAC numeric value */
int main(void)
{
configure_DAC(); // call DAC initialization and configuration routine
configure_GPIO(); // call GPIO PA5 initialization and configuration routine
	
/* Start DAC conversion with DAC_CHANNEL_2 */
HAL_DAC_Start(&DAC_Config, DAC_CHANNEL_2);
while(1)
{
	int D_Value = 1;
 while(D_Value < 4000) {
 HAL_DAC_SetValue(&DAC_Config, DAC_CHANNEL_2, DAC_ALIGN_12B_R, D_Value);
 Delay_ms(1);
 D_Value+=100;
 }

 while(D_Value > 1) {
 HAL_DAC_SetValue(&DAC_Config, DAC_CHANNEL_2, DAC_ALIGN_12B_R, D_Value);
 Delay_ms(1);
 D_Value-=100;
 }
 }
}

/* Initialize and configure DAC_Channel_2 */
void configure_DAC(void)
{
 DAC_ChannelConfTypeDef Channel_Config;
 __HAL_RCC_DAC_CLK_ENABLE();
 /* DAC Initialization */
 DAC_Config.Instance = DAC;
 HAL_DAC_Init(&DAC_Config);

 /* DAC channel_OUT2 config */
 Channel_Config.DAC_Trigger = DAC_TRIGGER_NONE;
 Channel_Config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
 HAL_DAC_ConfigChannel(&DAC_Config, &Channel_Config, DAC_CHANNEL_2);
}
/* Initialize and configure GPIO PA4 as a analog pin */
void configure_GPIO(void)
{

/* DAC GPIO ---> PA5 */
 GPIO_InitTypeDef GPIO_InitStruct;
 GPIO_InitStruct.Pin = GPIO_PIN_5;
 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/*Generate ms */
void Delay_ms(volatile int time_ms)
{
	      int j;
        for(j = 0; j < time_ms*4000; j++)
            {}  /* excute NOP for 1ms */
}

DAC Example Code Output

DAC STM32F4 disovery Board example code

Now to see the output of the above code, connect one terminal of an oscilloscope with the PA5 pin of the STM32F4 discovery board and the ground terminal with the ground pin of the discovery board. You will get a sawtooth trianglular on the oscilloscope screen.

generate triangular wave with DAC of STM32F4 discovery Board

How Code Works?

To generate a triangular waveform, we provide numeric values to the DAC module in increasing and decreasing order, respectively. First while loop, set numeric values in increasing order from 1 to 4000 with each increment of 100. But as soon as D_value reaches 4000 first while loop terminates and the second while loop starts to execute.

int D_Value = 1;
 while(D_Value < 4000) {
 HAL_DAC_SetValue(&DAC_Config, DAC_CHANNEL_2, DAC_ALIGN_12B_R, D_Value);
 Delay_ms(1);
 D_Value+=100;
 }

In the second while loop, we provide numeric value to the DAC module in decreasing order starting from 3900 with each decrement of 100. But as soon as D_value reaches 100, the second while loop terminates and the start execution from while(1) again. This pattern keeps executing.

 while(D_Value > 1) {
 HAL_DAC_SetValue(&DAC_Config, DAC_CHANNEL_2, DAC_ALIGN_12B_R, D_Value);
 Delay_ms(1);
 D_Value-=100;
 }

In the coming tutorials, we will see how to use DAC module of STM32F4 with DMA and timers as DAC trigger source.

Related Tutorials:

1 thought on “DAC STM32F4 Discovery Board – Generate Waveforms with Digital to Analog Converter”

Leave a Comment