STM32 DAC DMA Underrun problem - Page 1
Forum section: electronics / general

author: syntax333
author_rank: Regular Contributor
timestamp: July 16, 2020, 05:17:11 am
content: Hi, I am currently working on small project which I need to generate some arbitrary signal using DAC inside STM32G431KB.Code is shown below.Code:#include "main.h"#include "SysClockConfig.h"uint16_t Output_Buffer = {2048,2048,2048,2048,2048,2048,2048,2048,2048,2048};void Initialization(){//----------Clock Config----------//RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; //Enable Timer 2 ClockRCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN; //Enable DMARCC->AHB2ENR |= RCC_AHB2ENR_DAC3EN | RCC_AHB2ENR_GPIOAEN; //Enable DAC3 and GPIOARCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Enable SysCfg for OPAMP//----------GPIO Config----------//GPIOA->MODER |= GPIO_MODER_MODE2; //Analog ModeGPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD2); //No PU-PD//----------OPAMP Config----------//OPAMP1->CSR &= ~(OPAMP_CSR_OPAMPxEN); //Disable OPAMPOPAMP1->CSR |= OPAMP_CSR_VPSEL; //Connect DAC3_CH1 to VINPOPAMP1->CSR |= OPAMP_CSR_VMSEL; // Follower ModeOPAMP1->CSR |= OPAMP_CSR_HIGHSPEEDEN; // Enable High-Speed ModeOPAMP1->CSR &= ~(OPAMP_CSR_OPAMPINTEN); //Output connected to pinOPAMP1->CSR |= OPAMP_CSR_OPAMPxEN; //Enable OPAMP//----------Timer Config----------//TIM2->CR1 &= ~(TIM_CR1_CEN); //Counter disableTIM2->PSC = 149; //TIM2->ARR = 2000000; //TIM2->CR1 |= TIM_CR1_URS; //Only counter overflow/underflow generates an updateTIM2->CR2 |= (0b010<<TIM_CR2_MMS_Pos); //The update event is selected as a trigger outputTIM2->EGR |= TIM_EGR_UG; //Update generation//----------DAC Config-----------//DAC3->CR &= ~(DAC_CR_EN1); //Disable DACDAC3->CR |= DAC_CR_DMAEN1; //Enable DMA on DAC Ch1DAC3->CR &= ~(DAC_CR_WAVE1); //Wave Generation DisabledDAC3->CR |= (4 << DAC_CR_TSEL1_Pos); //Trigger Channel 4 = Timer TRGO 2DAC3->MCR |= (0b011 << DAC_MCR_MODE1_Pos);//Normal Mode DAC Channel 1 is connected to on chip peripherals with Buffer disabled.DAC3->MCR |= (0b01 << DAC_MCR_HFSEL_Pos); //High frequency interface mode compatible to AHB>80 MHz enabledDAC3->CR |= DAC_CR_TEN1; //Channel Trigger EnableDAC3->CR |= DAC_CR_EN1; //Enable DACwhile(!(DAC3->SR & DAC_SR_DAC1RDY)); //Wait Until DAC is ready//----------DMA1 Channel1 Configuration----------//DMA1_Channel1->CNDTR = 10; //Set number of data to be sentDMA1_Channel1->CPAR = (uint32_t) &DAC3->DHR12R1; //Peripheral addressDMA1_Channel1->CMAR = (uint32_t) Output_Buffer; //Data Buffer address//VHigh Priority16bit Data size16bit Data sizeMemory Inc. Circular Mode Memory2PeripDMA1_Channel1->CCR |= (0b11 << DMA_CCR_PL_Pos) | (0b01 << DMA_CCR_MSIZE_Pos) | (0b01 << DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_DIR ;DMAMUX1_Channel0->CCR |= (102 << DMAMUX_CxCR_DMAREQ_ID_Pos); //Connect DAC3_CH1 to DMA channel 1DMA1_Channel1->CCR |= DMA_CCR_EN; //Enable Channel}int main(void){HAL_Init();SystemClock_Config();Initialization();//DAC3->DHR12R1 = 2048;TIM2->CR1 |= TIM_CR1_CEN; //Counter enablewhile (1){}}void Error_Handler(void){}void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the CPU, AHB and APB busses clocks*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4;RCC_OscInitStruct.PLL.PLLN = 75;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB busses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK){Error_Handler();}SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);}The code works if I don't use DMA. I can see the expected Output on PA2. However, when I tried to use DMA the output stays at 0V and I get DMA underrun bit set in DAC_SR register.My timer is really slow 150MHz/150 = 1MHz/2000000= 2seconds. I can't see why this happens since underrun caused when DMA can't keep up with the DAC trigger speed.I suspect that DMA do not work at start and thus giving underrun error at beginning.Has anyone encountered something like this?Thank you.

author: syntax333
author_rank: Regular Contributor
timestamp: July 16, 2020, 06:57:11 am
content: Also I tried using timer interrupt to send data into "DAC3->DHR12R1" register. I set timer to 2.5MHz and used internal OPAMP(follower) to route DAC3 output to external pin. Code works fine but it seems like output is limited to 1Msps even though in datasheet it says that DAC3 can output at 15Msps. Is this normal?

author: Jeroen3
author_rank: Super Contributor
timestamp: July 16, 2020, 07:08:59 am
content: QuoteQuoteThat's 16 bit.Don't you get a busfault?edit: I just though that the DMA may not be able to cause an exception in the arm cortex... the transaction will just.. fail.. and that's it. Maybe this is wrong though.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 16, 2020, 07:10:16 am
content: You have configured DAC as the DMA request source. Isn't your intention to use the timer as the DMA request source?

author: mikerj
author_rank: Super Contributor
timestamp: July 16, 2020, 08:49:09 am
content: Quote from: syntax333 on July 16, 2020, 06:57:11 amThat'll most likely be a code speed limitation, not the DAC itself.Quote from: Siwastaja on July 16, 2020, 07:10:16 amWell there's your problem lady!

author: newbrain
author_rank: Super Contributor
timestamp: July 16, 2020, 09:22:44 am
content: Quote from: Siwastaja on July 16, 2020, 07:10:16 amQuote from: mikerj on July 16, 2020, 08:49:09 amNope, the idea is correct.The DAC will issue a DMA request when it receives an HW trigger, when DMA_ENx is set.See thereference manualchapter 22.4.8I think Jeroen3 might be correct.

author: syntax333
author_rank: Regular Contributor
timestamp: July 16, 2020, 11:01:36 am
content: Quote from: Jeroen3 on July 16, 2020, 07:08:59 amYes the problem was transfer size. After setting "0b10 <<DMA_CCR_PSIZE_Pos" which corresponds to 32 bit code now works as expected. Also now DAC can go beyond 1Msps. Thank you!