cancel
Showing results for 
Search instead for 
Did you mean: 

Unusual signal pattern at the output of STM32G474 DAC3

imalamoud
Associate II

Hello,

I am implementing a sine wave generator based on the high-speed DAC3 of the G474.

The DAC is in double-DMA mode and its conversions are triggered periodically  by a timer.
Sample and Hold is disabled.

The DAC is connected to an external pin by an OPAMP in the follower mode.

What could be the reason for the downward slope shown on the picture? It is consistent even at low conversion speeds.

Thank you for your help.

 

11 REPLIES 11
KnarfB
Super User

Why not using sample&hold?

From the reference manual: " In sample and hold mode, the DAC core converts data on a triggered conversion, and then holds the converted voltage on a capacitor. When not converting, the DAC cores and buffer
are completely turned off between samples and the DAC output is tri-stated, therefore
reducing the overall power consumption."

hth

KnarfB

MasterT
Lead

I'd check opamp first, configuring as standalone and applying external signal.

Btw, have you done dac & opamp calibration?

Thank you for your suggestion.

I tried to enable SAH before posting but did not see any changes in the signal pattern.

Can you please point to a sample HAL configuration of SAH feature?

Additionally is SAH compatible with DDMA mode and the highest possible conversion speed?
I would like to get 1MHz sine wave thus 16M samples conversion.

Thanks,

Ilia

Thank you for this idea.
However, the signal pattern does not depend on a G474 chip sample. So a defective opamp is unlikely.

Post your code, schematics and pictures of hardware set-up, how to reproduce this abnormality

Hello,

The replication is done on the bare Nucleo G474RE board. The output is taken from the PB1 which is (according to the schematic) is not connected to any other board components.

The DAC is running at 160MHz clock.

For the code example below I have made a loop sample code where the DAC generates a sine wave for 10ms, then stops for 10ms.

The resulting pattern is attached.

Configurations:

/* TIM2 init function */
void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4.294967295E9;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

/* OPAMP3 init function */
void MX_OPAMP3_Init(void)
{

  hopamp3.Instance = OPAMP3;
  hopamp3.Init.PowerMode = OPAMP_POWERMODE_NORMAL;
  hopamp3.Init.Mode = OPAMP_FOLLOWER_MODE;
  hopamp3.Init.NonInvertingInput = OPAMP_NONINVERTINGINPUT_DAC;
  hopamp3.Init.InternalOutput = DISABLE;
  hopamp3.Init.TimerControlledMuxmode = OPAMP_TIMERCONTROLLEDMUXMODE_DISABLE;
  hopamp3.Init.UserTrimming = OPAMP_TRIMMING_FACTORY;
  if (HAL_OPAMP_Init(&hopamp3) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_OPAMP_MspInit(OPAMP_HandleTypeDef* opampHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(opampHandle->Instance==OPAMP3)
  {
  /* USER CODE BEGIN OPAMP3_MspInit 0 */

  /* USER CODE END OPAMP3_MspInit 0 */

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**OPAMP3 GPIO Configuration
    PB1     ------> OPAMP3_VOUT
    */
    GPIO_InitStruct.Pin = DAC_SIG_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(DAC_SIG_GPIO_Port, &GPIO_InitStruct);

  /* USER CODE BEGIN OPAMP3_MspInit 1 */

  /* USER CODE END OPAMP3_MspInit 1 */
  }
}

void MX_DAC3_Init(void)
{
  DAC_ChannelConfTypeDef sConfig = {0};

  /** DAC Initialization
  */
  hdac3.Instance = DAC3;
  if (HAL_DAC_Init(&hdac3) != HAL_OK)
  {
    Error_Handler();
  }
  /** DAC channel OUT2 config
  */
  sConfig.DAC_HighFrequency = DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
  sConfig.DAC_DMADoubleDataMode = ENABLE;
  sConfig.DAC_SignedFormat = DISABLE;
  sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
  sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
  sConfig.DAC_Trigger2 = DAC_TRIGGER_NONE;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
  sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_INTERNAL;
  sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;

  
  if (HAL_DAC_ConfigChannel(&hdac3, &sConfig, DAC_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }

    if (HAL_DACEx_SelfCalibrate(&hdac3, &sConfig, DAC_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }


}

void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(dacHandle->Instance==DAC3)
  {
  /* USER CODE BEGIN DAC3_MspInit 0 */
    DAC_SINE_sconfig.DAC_HighFrequency = DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
    DAC_SINE_sconfig.DAC_DMADoubleDataMode = DISABLE;
    DAC_SINE_sconfig.DAC_SignedFormat = DISABLE;
    DAC_SINE_sconfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
    DAC_SINE_sconfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
    DAC_SINE_sconfig.DAC_Trigger2 = DAC_TRIGGER_NONE;
    DAC_SINE_sconfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
    DAC_SINE_sconfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
    DAC_SINE_sconfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
  
  

  /* USER CODE END DAC3_MspInit 0 */
    /* DAC3 clock enable */
    __HAL_RCC_DAC3_CLK_ENABLE();

        if (HAL_DACEx_SelfCalibrate(&hdac3, &DAC_SINE_sconfig, DAC_CHANNEL_2) != HAL_OK)
		{
			Error_Handler();
		}

    
    /* DAC3 DMA Init */
    /* DAC3_CH2 Init */
    hdma_dac3_ch2.Instance = DMA1_Channel1;
    hdma_dac3_ch2.Init.Request = DMA_REQUEST_DAC3_CHANNEL2;
    hdma_dac3_ch2.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_dac3_ch2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_dac3_ch2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_dac3_ch2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_dac3_ch2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_dac3_ch2.Init.Mode = DMA_CIRCULAR;
    hdma_dac3_ch2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_dac3_ch2) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(dacHandle,DMA_Handle2,hdma_dac3_ch2);

  }
}

Loop code:

//packs samples for doubleDMA consumption

void ddma_buffer_convert(uint32_t *dma_s_buffer, int dma_s_size, uint32_t *dma_d_dbuffer )
{
	uint32_t buffer_low16=0;
	uint32_t buffer=0;

	for (int i=0, j=0; i < dma_s_size;)
	{
		buffer_low16 = dma_s_buffer[i++];
		buffer = dma_s_buffer[i++];
		buffer <<= 16;
		buffer += buffer_low16;
		dma_d_dbuffer[j++]=buffer;
	}
}

int offset_prev = 0;
int sin_amplitude_prev = 0;
int dac_sample_size_prev = 0;

void fill_sine_array(int offset,int sin_amplitude,int dac_sample_size)
{

  if(offset_prev == offset && 
     sin_amplitude_prev == sin_amplitude &&  
     dac_sample_size_prev == dac_sample_size  &&  
     sineArrayFillLength == dac_sample_size)
                                        return;
    

  offset += 0x800;

  for(int i=0; i < dac_sample_size; i++)  {
      sine_wave_array1600_generated[i]= (int)round(offset + (int)(((float)sin_amplitude)*sin(2*M_PI*(i)/(float)dac_sample_size)));
  }
  
  ddma_buffer_convert((uint32_t *)sine_wave_array1600_generated, dac_sample_size, (uint32_t *)sinewave_dmad_buffer );

  offset_prev = offset_prev;
  sin_amplitude_prev = sin_amplitude;
  dac_sample_size_prev = dac_sample_size;

  sineArrayFillLength = dac_sample_size;
}



void start_sine_DAC_loop( int arr_len, int timer_period, int signal_amplitude)
{
 
	while (1)
	{
	  
		fill_sine_array(DAC_OFFSET, signal_amplitude, arr_len);
	  __HAL_TIM_SET_AUTORELOAD(&htim2, timer_period);
	  

	  
		DAC_SINE_sconfig.DAC_DMADoubleDataMode = ENABLE;
		DAC_SINE_sconfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
		DAC_SINE_sconfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;

		if (HAL_DAC_ConfigChannel(&hdac3, &DAC_SINE_sconfig, DAC_CHANNEL_2) != HAL_OK)
		{
		  Error_Handler();
		}

		if (HAL_DAC_Start_DMA(&hdac3, DAC_CHANNEL_2, (uint32_t *)sinewave_dmad_buffer, arr_len/2, DAC_ALIGN_12B_R) != HAL_OK)
		{
		  Error_Handler();
		}

		/* Start Timer trigger */
		HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE);
		if (HAL_TIM_Base_Start(&htim2) != HAL_OK)
		{
		  /* Counter enable error */
		  Error_Handler();
		}
		
		HAL_Delay(10);

		
		if (HAL_TIM_Base_Stop(&htim2) != HAL_OK){
		  Error_Handler();
		}

		if (HAL_DAC_Stop_DMA(&hdac3, DAC_CHANNEL_2) != HAL_OK){
		  Error_Handler();
		}

		
		HAL_Delay(10);

	 } //while
		

}



int main(void)
{

  HAL_Init();

  SystemClock_Config();


  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC3_Init();
  MX_OPAMP3_Init();
  MX_TIM2_Init();
  
  if (HAL_OPAMP_Start(&hopamp3) != HAL_OK) {
 	/* OPAMP enable error */
  	   Error_Handler();
  }
  
  start_sine_DAC_loop( 1600, 99, 835);
} //

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV6;
  RCC_OscInitStruct.PLL.PLLN = 80;
  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 buses 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_4) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the peripherals clocks
  */
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
  PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}


I think that problem is in unnecessary de-initialization of the dac inside while{} loop.

This line 

		if (HAL_DAC_Stop_DMA(&hdac3, DAC_CHANNEL_2) != HAL_OK){

does  __HAL_DAC_DISABLE(hdac, Channel);

so voltage may drift in any direction after DAC was switch off.

  There is an example how to use dacs, 

initialization should be done once, stop timer is enough to interrupt output

 

Yes, that was the problem. Thank you so much!

However, this DMA need to be parametrized by the varying buffer array contents and the array length from time to time.

Is this the correct way to to stop DMA for reconfiguring?

    if (hdac3.DMA_Handle2->State == HAL_DMA_STATE_BUSY)
    {
        if(HAL_DAC_Stop_DMA(&hdac3, DAC_CHANNEL_2))
        {
           Error_Handler();
        }
    }

    
    if (HAL_DAC_Start_DMA(&hdac3, DAC_CHANNEL_2, (uint32_t *)sinewave_dmad_buffer, arr_len/2, DAC_ALIGN_12B_R) != HAL_OK)
    {
      Error_Handler();
    }

Correct way to re-init dac-dma system is provided in the example I linked above.

Study: static void DAC_ChangeWave(t_wavetype wave)