cancel
Showing results for 
Search instead for 
Did you mean: 

How to write your own DAC driver in STM32?

RShre.2
Associate III

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?

22 REPLIES 22

thanks. Is it possible to use both LL and HAL in a single project? For example, use HAL to toggle pin and LL for DAC or in similar way other stuffs?

> 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

Yes, you can mix HAL and LL and bare metal as you like. You just have to keep track of what you have done where. ;)

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.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

 // Check which version of the timer triggered this callback and toggle LED

 if (htim == &htim16 )

 {

//  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

 phase += tuning;

 idx = (phase >> 20) & 0xFFF;

 output_val = LUT1[idx];

 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

 LL_DAC_ConvertData12LeftAligned(DAC, LL_DAC_CHANNEL_1, output_val);

 LL_DAC_TrigSWConversion(DAC, LL_DAC_CHANNEL_1);

 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

//  output_val = LUT1[idx];

////  start_time = __HAL_TIM_GET_COUNTER(&htim2);

//

////  end_time = __HAL_TIM_GET_COUNTER(&htim2);

////  duration = start_time - end_time;

//

//  HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, output_val);

//  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

//  if(duration>50*){

//  HAL

//  }

 }

}

This is my timer interrupt handler, based on my trigger time which is 5us (200KHz), i tried to check if i was getting the correct toggling. I tried to see how much time does LL take to run by veiwing the toggling in oscilloscope. Which is not different from when I used HAL Driver.

0693W00000aIg3bQAC.png 

Also the sine output is not stable, where frequency is oscillating between 5-9KHz when the expected output is 10KHz. I don't know if I am doing anything wrong here.

Except for the timer interrupt handler, the modification i made was to enable the LL,

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

 MX_TIM16_Init();

 MX_DAC1_Init();

 /* USER CODE BEGIN 2 */

 sine_val(LUT1, 1);

// sine_val(LUT2, -1);

// TIM16->PSC = 0;

// TIM16->ARR = 640-1;

 PSC = htim16.Init.Prescaler;

 ARR = htim16.Init.Period;

 samplingrate();

 phase_increment(f_exp, Fs, 32);

// HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);

 LL_DAC_Enable(DAC1, LL_DAC_CHANNEL_1);

 HAL_TIM_Base_Start_IT(&htim16);

 /* USER CODE END 2 */

 /* Infinite loop */

 /* USER CODE BEGIN WHILE */

 while (1)

 {

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

 }

 /* USER CODE END 3 */

}

0693W00000aIg5SQAS.png

LCE
Principal

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

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.

If you link the DAC internally to an OPAMP and the use DMA  ( as on the STMG devices) you can get 15 DAC outputs per micro second so you can make nice accurate 1MHz signals from the DAC

> ... so you can make nice accurate 1MHz signals from the DAC

I wouldn't bet on that with the G071.

I have had this on STM32G474 but this has a DAC to internal and then an OPAMP that acts as a DAC follower. So not only fast (think it started to go wrong when we tried to push it below 66nS per DAC output) but also the low driving impedance of an opamp (like 20mA? dunno, been reading and rereading the datasheet MUCH TOO MUCH these past few days to check right now)