2025-08-11 6:26 AM
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
2025-08-11 6:46 AM
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?
2025-08-11 7:56 AM
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