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
Solved! Go to Solution.
2025-08-12 3:47 AM
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
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
2025-08-11 9:18 AM
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.
2025-08-11 9:30 AM
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
2025-08-11 2:04 PM
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
2025-08-11 3:44 PM
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.
2025-08-12 1:41 AM
Hello @FredS
Please refer to the example below to generate your dac signal:
2025-08-12 3:13 AM
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
2025-08-12 3:47 AM
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