cancel
Showing results for 
Search instead for 
Did you mean: 

Simple DAC control appears not simple

FredS
Senior

    Dear supporters,

In a project I want to create a feedback mechanism to keep the top of a pulse-shaped signal between boundaries. Most of this seems working, except the control of the DAC channel, that should output a voltage for the diode-laser which produces the pulse to be controlled. Whatever I try, I get a more or less constant block shaped signal of 3.3V during ~7us and 0V for 5us. I checked the Reference Manual for the STM32H723VG processor chapter 31 but did find any clue.

In pseudo code I do the following:

    - declare a DAC variable (DAC_HandleTypeDef hdac1; , auto-generated)

    - initialize the DAC within 'main.c'  ( MX_DAC1_Init(); , auto-generated)

/**
  * @brief DAC1 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_DAC1_Init(void)
{

  /* USER CODE BEGIN DAC1_Init 0 */

  /* USER CODE END DAC1_Init 0 */

  DAC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN DAC1_Init 1 */

  /* USER CODE END DAC1_Init 1 */

  /** DAC Initialization
  */
  hdac1.Instance = DAC1;
  if (HAL_DAC_Init(&hdac1) != HAL_OK)
  {
    Error_Handler();
  }

  /** DAC channel OUT1 config
  */
  sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
  sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
  sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
  if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DAC1_Init 2 */

  /* USER CODE END DAC1_Init 2 */

}

    - start the DAC within 'main.c' (   Return_Start_DAC1_OUT1 = HAL_DAC_Start(&hdac1, DAC_CHANNEL_1); ) which returns 'HAL_OK'

- assign a calculated setting to the DAC (

Return_DAC1_WriteSett = HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DACsetting); ) within a repeatedly called function which returns 'HAL_OK'

 

What am I doing wrong? In my simple understanding a DAC will output a voltage according to its setting, until a new setting is written to it. Reality shows me something different, which puzzles me.  Can anyone shed some light??

 

A lot of thanks in advance,

    Fred Schimmel

 

4 REPLIES 4
TDK
Super User

Can you show your code, what you expect and what you instead see? No problems with the code you've presented so far.

> output a voltage for the diode-laser which produces

How much current are you requiring it to output?

If you feel a post has answered your question, please click "Accept as Solution".

    Dear TDK,
Once more it is you who responds first on my question for help, thanks !

 

I will try to show the essential parts of the whole application, with a short description of the rest. It concerns a relative displacement measurement where a laser beam (related to one side) creates a pulse on a linear CCD array of 1x3648 pixels sized 8x200um. The center of the pulse shape defines the actual precision, with a three sigma precision of <150nm. To ensure the performance I want to keep the pulseheight within the upper region of the ADC acceptance, by controlling the voltage of the diode laser supply.

The SPI readout of the ADC is done by DMA, with interrupts on the 'half size' and the 'whole size'. I modified the generated interrupt handling function; the next code is relevant.

The DMA transfers the ADC data to the array 'g_CCD_Buff', the values are corrected and stored in 'g_CCD_Buff2'.

As attempt to decouple the peak-handling even more, I copy the 'g_CCD_Buff2' array to 'g_Peak_Buff'.

The function of the global 'Counter' is to execute a voltage control cycle only once per N readout cycles.

First I show the related declarations:

HAL_StatusTypeDef	Return_Start_DAC1_OUT1		=	0xff;
HAL_StatusTypeDef	Return_DAC1_WriteSett		=	0xff;
uint16_t		PkAbsMax			=	64000U;
//uint16_t		UpLim				=	UPLIM;
//uint16_t		LoLim				=	LOLIM;
uint16_t		UpLim				=	62000;
uint16_t		LoLim				=	54000;
uint16_t		AvLim				=	0;
uint16_t		RefAmpl				=	REFAMPL;
uint16_t		PeakMax				=	0U;
uint16_t		IpkMax				=	0U;
uint16_t		Ista				=	0U;
uint16_t		Iend				=	0U;
uint16_t		PeakLen				=	0U;
int16_t			ReqChange			=	0;
int32_t			ReqChange1000			=	0;
int32_t			DACchange			=	0;
uint32_t		DACsetting			=	DACNOMINAL;
uint32_t		DACabsMax			=	3040U;
uint32_t		DACread				=	0U;
int32_t			DACsensitivity			=	135;	// 135.391 as float
uint32_t		Counter 			= 	0U;

Here a part of the Interrupt routine where the CCD data are handled:

  /* Transfer completed actions */

  if (HT_Interr)
  { // HalfCmpl: correct pixel-sensitivity, copy Marker & export first half g_CCD_Buff:
  for (i=0; i<(CCD_BUFF_LEN)/2; i++) {
    g_CCD_Buff2[i] = (uint16_t)(((float)g_CCD_Buff[i]) * g_PixFactor[i]) ;
    g_Peak_Buff[i] = g_CCD_Buff2[i];
  }
  
  for (i=0; i<MARK_ARRAY_LEN; i++) {    // marks begin new dataset
  g_CCD_Buff2[i] = g_Marker[i];
  }
  Return_CDC_Transmit = CDC_Transmit_HS((uint8_t *)p_CCD_Buff_FirstHalf, CCD_BUFF_LEN);
  result = 1;
  }
else if (TC_Interr)
  { // XferCmpl: correct pixel-sensitivity and export second half g_CCD_Buff:
  for (i=CCD_BUFF_LEN/2; i<(CCD_BUFF_LEN); i++) {
    g_CCD_Buff2[i] = (uint16_t)(((float)g_CCD_Buff[i]) * g_PixFactor[i]) ;
    g_Peak_Buff[i] = g_CCD_Buff2[i];
    }
  Counter++;
  DACread = HAL_DAC_GetValue(&hdac1, DAC_CHANNEL_1);
  g_CCD_Buff2[4] = (uint16_t)(DACread & 0xfffL); // export DACsett as pixel-value[4]
 
  Return_CDC_Transmit = CDC_Transmit_HS((uint8_t *)p_CCD_Buff_SecondHalf, CCD_BUFF_LEN);
  result = 1;
  }
} // end of 'if (HT_Interr || TC_Interr)'

 

'Counter' is checked in the main 'while loop', see:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (Counter > 42) {		// time to check/correct peakheight
      Counter=30;		// reset Counter for next check	
      FindPeak_Pos();           // determine Peak position & height
      DetermineCorr();		// determine ampl out of range
      Update_DAC();		// apply correction if needed
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

The functions 'FindPeak_Pos()', 'DetermineCorr()' and 'Update_DAC()' are shown below:

// Procedure to detect the peak position:
void	FindPeak_Pos() {
	uint16_t	i;

	// Determine Ista and Iend of pulse:
	for (i = 10; i < CCD_BUFF_LEN; i++) {		// skip first pixel-values
		if (g_Peak_Buff[i] > RefAmpl) {
			Ista = i;
			break;
		}
	}
	for (j = (CCD_BUFF_LEN-1); j > Ista; j--) {
		if (g_Peak_Buff[j] > RefAmpl) {
			Iend = j;
			break;
		}
	}
	PeakLen = Iend - Ista;
	return;
}	// end of 'void	FindPeak()'


//	Procedure to check need for amplitude correction:
void	DetermineCorr() {
	uint16_t	i;

	PeakMax = 0;
	for (i = Ista; i < Iend; i++) {
		if (g_Peak_Buff[i] > PeakMax) {
			PeakMax = g_Peak_Buff[i];
			IpkMax = i;
		}
		if (g_Peak_Buff[i] < RefAmpl) {
			// Vulcano shape: = sign of saturation
			ReqChange = PkAbsMax - AvLim;
			return;
		}
	}	// end of 'for (i = Ista; i < PeakLen; i++)'

	// for-loop finished:
	if (PeakMax > UpLim) {
		ReqChange = UpLim - PeakMax;
		return;
		}

	if (PeakMax < LoLim) {
		ReqChange = LoLim - PeakMax;
		return;
		}

	return;
}	// end of 'void DetermineCorr()'


// Procedure to modify the DAC for diode-laser voltage:
void	Update_DAC() {
	if (ReqChange == 0)		// peak ampl OK
		return;

	ReqChange1000 = ((int32_t)ReqChange) * 1000;
	DACchange = ReqChange1000 / DACsensitivity;
	DACsetting += DACchange;
	if (DACsetting > DACabsMax)
		DACsetting = DACabsMax;
	Return_DAC1_WriteSett = HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DACsetting);
}	// end of 'Update_DAC()'

I hope you still can follow the line of execution, the essentials are:

a) the copy of CCD-data in the interrupt routine,

b) the search for the peak and check of the amplitude,

c) the correction of the amplitude if needed.

 

Thanks for your attention,

 

Fred

TDK
Super User

Oh man, that's quite a bit of logic.

Perhaps make a simpler program where you set a DAC value, wait 500 ms, set another, etc, to verify the DAC functionality is correct. Once you get that up and running, you can debug the logic in your script. 

Set variables modified in both the main loop--like "Counter"--as volatile.

 

Careful of calling CDC_Transmit_HS in an interrupt and without checking the return value or state of USB. If the last transfer isn't complete, it will fail.

If you feel a post has answered your question, please click "Accept as Solution".

    Hallo TDK,

Thank you for your smart suggestion. In fact I already did a similar check, but with a high update rate. Triggered by you, I repeated that test but on a much slower rate. It shows the DAC functions normal, so I must verify where I make a mistake in my logic. I think you have put me on the right track, thanks a lot.

 Have a nice day,  Fred Schimmel