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

 

1 ACCEPTED SOLUTION

Accepted Solutions

       Hallo TDK,

You are my hero, making me feel ashamed not recognizing the shape of the overlayed signal is a Timer pulse in my project.

Your hint "Looks like something else is trying to control that line" awoke me and pointed me to the solution. When I added the DAC1_OUT1 signal to my project, I needed to solder another wire from the one of the pin headers to an external connector. I could have noticed that pin PA4 (the DAC) is beside PA6 (the timer signal) and check if I didn't make a short when soldering. Well, I did. After correction, the DAC and the application both work as expected.

I really thank you for your patience and willingness to think with me, which finally directed me in the right direction and so the solution.

I don't see a way to grant you Kudos, as this is not the last message in the list, but you certainly earn them The button "Accept as solution" is also not available (only at the last message, which is not the solution).

I wish you a nice day and send many greetings from the Netherlands,

    a very thankful Fred Schimmel

View solution in original post

9 REPLIES 9
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

Once more hallo TDK,

To come closer to the cause of my problem, I stripped a lot of code related to the DAC control, but now I still have the strange behavior. I added a short video of the scope signal that should be a level that changes every 500ms.

The only code that affects the DAC (at least I think) is the following:

  ...
  ... 

 /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  DACsetting = 1000L;

  while (1)
  {
//  FindPeak_Pos();	
//  DetermineCorr();
//  Update_DAC();
	Return_DAC1_WriteSett = HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DACsetting);
	DACsetting += 1000L;
	if (DACsetting > 4095) {
	  DACsetting -= 3900L;
	}
	Count++;
	HAL_Delay(500);
  }
    /* USER CODE END WHILE */

The 'Count' variable is added to have an impression of the repeat rate of the while-loop.

 

The only thing I can think of is some unknown interference of the remaining code with the piece I copied above. It really blows my mind.

 

Maybe someone else, more clever or experienced, has a suggestion???

Have a nice day anyway,

    FRed Schimmel

 

As you said before in your simple test:

> It shows the DAC functions normal,

So issue must be somewhere else. How does this code or setup differ from the code which worked?

 

How is the DAC output connected? Looks like something else is trying to control that line. Don't think you answered how much current is being drawn.

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

Hello @FredS 

Please refer to the example below to generate your dac signal:

STM32CubeH7/Projects/NUCLEO-H723ZG/Examples/DAC/DAC_SignalsGeneration at master · STMicroelectronics/STM32CubeH7 · GitHub

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.
Saket_Om

Hallo Saket_Om,

Thank you for your attempt to help. In fact I already did something as you suggest, but much more simple. That showed me the DAC on its own is OK, so I went on searching.


Have a nice day,

Fred

       Hallo TDK,

You are my hero, making me feel ashamed not recognizing the shape of the overlayed signal is a Timer pulse in my project.

Your hint "Looks like something else is trying to control that line" awoke me and pointed me to the solution. When I added the DAC1_OUT1 signal to my project, I needed to solder another wire from the one of the pin headers to an external connector. I could have noticed that pin PA4 (the DAC) is beside PA6 (the timer signal) and check if I didn't make a short when soldering. Well, I did. After correction, the DAC and the application both work as expected.

I really thank you for your patience and willingness to think with me, which finally directed me in the right direction and so the solution.

I don't see a way to grant you Kudos, as this is not the last message in the list, but you certainly earn them The button "Accept as solution" is also not available (only at the last message, which is not the solution).

I wish you a nice day and send many greetings from the Netherlands,

    a very thankful Fred Schimmel