cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 DAC DMA Underrun problem

LLOLO.1
Associate II

Hi, I am currently working on small project which I need to generate some arbitrary signal using DAC inside STM32G431KB.

Code is shown below.

#include "main.h"
#include "SysClockConfig.h"
 
 
uint16_t Output_Buffer[10] = {2048,2048,2048,2048,2048,2048,2048,2048,2048,2048};
 
 
void Initialization()
{
 
	//----------Clock Config----------//
	RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; //Enable Timer 2 Clock
	RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN; //Enable DMA
	RCC->AHB2ENR |= RCC_AHB2ENR_DAC3EN | RCC_AHB2ENR_GPIOAEN; //Enable DAC3 and GPIOA
	RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Enable SysCfg for OPAMP
 
 
	//----------GPIO Config----------//
	GPIOA->MODER |= GPIO_MODER_MODE2; //Analog Mode
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD2); //No PU-PD
 
 
	//----------OPAMP Config----------//
	OPAMP1->CSR &= ~(OPAMP_CSR_OPAMPxEN); //Disable OPAMP
	OPAMP1->CSR |= OPAMP_CSR_VPSEL; //Connect DAC3_CH1 to VINP
	OPAMP1->CSR |= OPAMP_CSR_VMSEL; // Follower Mode
	OPAMP1->CSR |= OPAMP_CSR_HIGHSPEEDEN; // Enable High-Speed Mode
	OPAMP1->CSR &= ~(OPAMP_CSR_OPAMPINTEN); //Output connected to pin
	OPAMP1->CSR |= OPAMP_CSR_OPAMPxEN; //Enable OPAMP
 
 
	//----------Timer Config----------//
	TIM2->CR1 &= ~(TIM_CR1_CEN); //Counter disable
	TIM2->PSC = 149; //
	TIM2->ARR = 2000000; //
	TIM2->CR1 |= TIM_CR1_URS; //Only counter overflow/underflow generates an update
	TIM2->CR2 |= (0b010<<TIM_CR2_MMS_Pos); //The update event is selected as a trigger output
	TIM2->EGR |= TIM_EGR_UG; //Update generation
 
 
	//----------DAC Config-----------//
	DAC3->CR &= ~(DAC_CR_EN1); //Disable DAC
	DAC3->CR |= DAC_CR_DMAEN1; //Enable DMA on DAC Ch1
	DAC3->CR &= ~(DAC_CR_WAVE1); //Wave Generation Disabled
	DAC3->CR |= (4 << DAC_CR_TSEL1_Pos); //Trigger Channel 4 = Timer TRGO 2
	DAC3->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 enabled
 
	DAC3->CR |= DAC_CR_TEN1; //Channel Trigger Enable
	DAC3->CR |= DAC_CR_EN1; //Enable DAC
	while(!(DAC3->SR & DAC_SR_DAC1RDY)); //Wait Until DAC is ready
 
 
	//----------DMA1 Channel1 Configuration----------//
	DMA1_Channel1->CNDTR = 10; //Set number of data to be sent
	DMA1_Channel1->CPAR = (uint32_t) &DAC3->DHR12R1; //Peripheral address
	DMA1_Channel1->CMAR = (uint32_t) Output_Buffer; //Data Buffer address
	//						    VHigh Priority			   16bit Data size 				16bit Data size			Memory Inc.    Circular Mode   Memory2Perip
	DMA1_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 1
 
 
	DMA1_Channel1->CCR |= DMA_CCR_EN; //Enable Channel
 
 
}
 
 
int main(void)
{
 
  HAL_Init();
  SystemClock_Config();
 
  Initialization();
 
  //DAC3->DHR12R1 = 2048;
  TIM2->CR1 |= TIM_CR1_CEN; //Counter enable
 
 
  while (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.

1 ACCEPTED SOLUTION

Accepted Solutions

Check the DMA registers - you'll probably see it's in Transfer Error. You have to set DMA on peripheral side to WORD size transfers, as DAC in 'G4 is directly on AHB and DAC registers allow only 32-bit access.

I'd also set up DMA first, only after then I'd enable DAC's DMAEN.

I can't comment about the analog performance - the performance you mention being given in datasheet, is it given for the DAC-opamp combo? MSPS does not necessarily imply analog bandwidth, too.

JW

View solution in original post

3 REPLIES 3
LLOLO.1
Associate II

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?

Check the DMA registers - you'll probably see it's in Transfer Error. You have to set DMA on peripheral side to WORD size transfers, as DAC in 'G4 is directly on AHB and DAC registers allow only 32-bit access.

I'd also set up DMA first, only after then I'd enable DAC's DMAEN.

I can't comment about the analog performance - the performance you mention being given in datasheet, is it given for the DAC-opamp combo? MSPS does not necessarily imply analog bandwidth, too.

JW

LLOLO.1
Associate II

Thank you! After changing only peripheral transfer size to WORD DMA is working now. Also it can go beyond 1Msps now. Maybe there is bandwidth limitation in interrupt method because I can sample 2.5Msps with DMA.