Skip to main content
RShre.2
Associate III
March 10, 2023
Question

How to write your own DAC driver in STM32?

  • March 10, 2023
  • 22 replies
  • 4455 views

I was using HAL Driver for DAC, however, it turned out not to be suitable for my project because at high sampling frequency, using HAL_DAC_Setvalue() inside a timer interrupt caused problem. So i need to write my own driver for DAC. But I have no idea how to get started. Is there any material someone could link?

This topic has been closed for replies.

22 replies

waclawek.jan
Super User
March 10, 2023

Which STM32?

> using HAL_DAC_Setvalue() inside a timer interrupt caused problem.

What problems?

What do you mean by "high sampling frequency"?

> Is there any material someone could link?

The reference manual to your STM32. Using DAC - after having configured it - is only one write to its data register.

JW

RShre.2
RShre.2Author
Associate III
March 10, 2023

STM32 nucleo G071RB

i am trying to generate a sine waveform using DDS and i was using timer interrupt. The trig_frequency(sampling rate) when increased from 100KHz to higher, the output frequency had shift(decreased in value). The HAL_DAC_SetValue() requires more than 2us to execute while for sampling rate, for instance, 500 KHz = 2us and the code that I had written to assign LUT into a variable runtime > 2us so I need to not use HAL Driver for the DAC Setvalue. Something that doesn't take too much time to execute.

Peter BENSCH
ST Technical Moderator
March 10, 2023

However, with 500kHz, i.e. 2µs, you come quite close to the maximum frequency of the DAC. As you can see from the data sheet, the DAC needs about 1.6-1.7µs settling time with the buffer switched on - OK, for a full 12bit jump. Anyway, that's a challenge, especially using an interrupt routine as @Community member​ noted.

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Peter BENSCH
ST Technical Moderator
March 10, 2023

Correct, you can also use LL instead of the HAL or control the DAC in bare metal. With the LL library you come quite close to bare metal, so that you only need to activate it: in STM32CubeMX > "project" > Project Manager > Advanced Settings --> click on HAL to the right of the peripheral DAC and change to LL.

Regards

/Peter

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
RShre.2
RShre.2Author
Associate III
March 10, 2023

0693W00000aIfgoQAC.jpg

RShre.2
RShre.2Author
Associate III
March 10, 2023

if you meant this, i couldn't find that option "Project Manager"

I assume it's not for G0 series.

waclawek.jan
Super User
March 10, 2023

Or just switch on compiler optimization.

Although, generally, high-frequency interrupts are not a good idea.

Also, you start hitting DAC's electrical limitations, see DAC chapter in datasheet (tSETTLING, tW-to-W). Note, that if buffer is ON, DAC is not rail-to-rail.

JW

RShre.2
RShre.2Author
Associate III
March 10, 2023

Yes, but that is what my project requires. Unfortunately.

waclawek.jan
Super User
March 10, 2023

> Yes, but that is what my project requires. Unfortunately.

What, using Cube/HAL, unoptimized code, or interrupts? Or fast DAC output?

You can replace interrupts by DMA.

As for the electrical limitations, there's no way to avoid them. If you are okay with a certain degree of distortion, you can use the DAC carefully, after having observed the limitations in DS. Otherwise, you need to find another solution - external faster DAC, a dedicated DDS chip.

JW

LCE
Principal II
March 10, 2023

Always check the requirements before making the choice...

One more vote for DMA - if that STM supports that for the DAC.

concerning sampling rate stability:

  • too much going on in your ISR for that sampling rate -> use DMA
  • with interrupts, there might be higher priority interrupts that kill your timing, -> use DMA (circular mode)
  • HSI as clock source is quite jittery, but not that bad

And when calculating your sine, make some level checks.

Better stay a few hundred LSBs below the minimum / maximum.

S.Ma
Principal
March 10, 2023

I also recommend to use DMA with DAC trigger signal from Timer (you can start using Timer output pin and wire jump it to DAC trigger pin for easy debug and scope trigger). The DMA can cycle through a big buffer and get interrupt when one half is ready to be updated if needed be for dynamic waveform.

Keep the DMA channels for things with high interrupt rate or critical latency, so definitely not for I2C or USART for example. The other benefit is it relaxes the core frequency needs and opens ways to lower power.

rclar.6
Associate III
August 1, 2023

Here is my main file that sets up DMA for the DAC (my DAC is routed internally to an OPAMP because I have a G474 device).

 

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();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

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

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_DAC3_Init();
 MX_OPAMP3_Init();
 MX_TIM3_Init();
 MX_TIM6_Init();
 /* USER CODE BEGIN 2 */
 // 1 10 12
 int16_t pData[]={100,200,300,400,500,600,700,800,900,800,700,600,500,400,300,200,0};
 int i;
 i = HAL_TIM_Base_Start(&htim3);
 i = HAL_TIM_Base_Start(&htim6);
 i = HAL_DAC_Start(&hdac3, DAC_CHANNEL_2);
 // really required ??? //
 i = HAL_DMA_Init(&hdma_dac3_ch2);

 i = HAL_OPAMP_Start(&hopamp3);

 i = HAL_DAC_SetValue(&hdac3, DAC_CHANNEL_2, DAC_ALIGN_12B_R, 0); // see this on scope output

 //i = HAL_DAC_SetValue(&hdac3, DAC_CHANNEL_2, DAC_ALIGN_12B_R, 4095); // see this on scope output


 //HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t *pData, uint32_t Length,
 // uint32_t Alignment)
 i = HAL_DAC_Start_DMA(&hdac3, DAC_CHANNEL_2, (uint32_t *)pData, 16, DAC_ALIGN_12B_R); // gives HAL_OK does nothing

 // causes error unsuprisinigly : i = HAL_DAC_Start_DMA(&hdma_dac3_ch2, DAC_CHANNEL_2, (uint32_t *)pData, 10, DAC_ALIGN_12B_R); // gives HAL_OK does nothing


 if ( i != HAL_OK ) {
	 Error_Handler();
 }

 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	 pData[5]++; // make the 6th in sequence wobbly so as to distingish it from the triangle auto wave generator
	 if ( pData[5] > 650)
		 pData[5] = 550; 
 asm (" nop");

 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}
LCE
Principal II
August 1, 2023

It's hard to read with the comments - so is it working now?

Do you have it working below the limits, let's say with a sampling rate of 100 kHz?