Skip to main content
Raggio
Associate III
January 24, 2022
Solved

STM32G474, HRTIM as external ADC trigger source

Dear all,

I've been trying to generate "LL_ADC_REG_TRIG_EXT_HRTIM_TRG1" with HRTIM to trigger a dual simultaneous conversion of the ADC of my STM32G474 based project.

I use LL drivers so, to my understanding the code to enable the trigger on the HRTIM side should be:

// HRTIM Peripheral clock enable
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_HRTIM1);
 
// HRTIM DLL (Delay-Locked-Loop) Calibration
LL_HRTIM_ConfigDLLCalibration(HRTIM1, LL_HRTIM_DLLCALIBRATION_MODE_CONTINUOUS, LL_HRTIM_DLLCALIBRATION_RATE_3);
while(LL_HRTIM_IsActiveFlag_DLLRDY(HRTIM1) == RESET){};
 
// > HRTIM Master Configuration
LL_HRTIM_TIM_SetPrescaler(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_PRESCALERRATIO_MUL4);
LL_HRTIM_TIM_SetPeriod(HRTIM1, LL_HRTIM_TIMER_MASTER, iInverter_PWM_Level);
LL_HRTIM_TIM_SetRepetition(HRTIM1, LL_HRTIM_TIMER_MASTER, 0x00);
// Time Base Settings:
LL_HRTIM_TIM_SetCounterMode(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_MODE_CONTINUOUS);
// Timing Unit:
LL_HRTIM_TIM_SetInterleavedMode(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_INTERLEAVED_MODE_DISABLED);
LL_HRTIM_TIM_DisableHalfMode(HRTIM1, LL_HRTIM_TIMER_MASTER);
LL_HRTIM_TIM_DisableStartOnSync(HRTIM1, LL_HRTIM_TIMER_MASTER);
LL_HRTIM_TIM_DisableResetOnSync(HRTIM1, LL_HRTIM_TIMER_MASTER);
LL_HRTIM_TIM_SetDACTrig(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_DACTRIG_NONE);
LL_HRTIM_TIM_EnablePreload(HRTIM1, LL_HRTIM_TIMER_MASTER);
LL_HRTIM_TIM_SetUpdateGating(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_UPDATEGATING_INDEPENDENT);
LL_HRTIM_TIM_SetBurstModeOption(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_BURSTMODE_MAINTAINCLOCK);
LL_HRTIM_TIM_SetUpdateTrig(HRTIM1, LL_HRTIM_TIMER_MASTER, LL_HRTIM_UPDATETRIG_NONE);
// Wait for configuration applied
while(LL_HRTIM_IsActiveFlag_DLLRDY(HRTIM1) == RESET){};
 
// > HRTIM Trigger to START ADC Conversion
LL_HRTIM_ConfigADCTrig(HRTIM1, LL_HRTIM_ADCTRIG_1, LL_HRTIM_ADCTRIG_UPDATE_MASTER, LL_HRTIM_ADCTRIG_SRC13_MPER);
LL_HRTIM_SetADCPostScaler(HRTIM1, LL_HRTIM_ADCTRIG_1, 20);

While, on the ADC side, it should be:

// Setup ADC1 as the master ADC:
// - Receive the start of conversion from the HRTIM MASTER TRG1
// - Setup the Scan sequence length, default is disabled.
// - Disable sequencer discontinuity.
// - Setup the Regular Conversion Mode in Single Conversion per Trigger
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_EXT_HRTIM_TRG1;
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;					
ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;							
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
LL_ADC_SetGainCompensation(ADC1, 0);
LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);

Right now the code is working, generating Center-Aligned PWM signals on Channels A,C,E,F and also acquiring ADC in dual synchronous mode with successful DMA transfer (when used with SOFTWARE trigger).... I really cannot understand what's missing from my configuration.

I've also analysed the B-G474E-DPOW1 Application Demo code (that use HRTIM ADC Trigger) and, apart from using HAL, it really seem the same. Maybe I miss an enable somewhere?

Thank you in advance for any help,

Davide

This topic has been closed for replies.
Francois VILMAIN
Francois VILMAINBest answer
ST Employee
February 3, 2022

Hello Davide,

I developed a short test code to check your use case. Initially I got the same behavior because I forgot to call LL_ADC_REG_StartConversion(ADC1) after the HRTIM1/ADC1 initialization step.

Now I get something working as expected. You'll find attached a zip file containing the source code I used to investigate this point (+ IAR project config.). I ran this code on a MB1428 board.

Le me know whether or not it helps.

regards,

François

5 replies

Raggio
RaggioAuthor
Associate III
January 25, 2022

I've still haven't found a solution to the HRTIM ADC Trigger, but I've managed a workaround in calling the LL_ADC_REG_StartConversion inside TIMER B HRTIM_TIMB_IRQn_IRQHandler.

TIMER B is a dedicated UP-COUNTING timer, with CMP1 configured at 50% DutyCycle.

The workaround is functional, but not optimal, even if the TIMER B is "statically configured" the IRQ does not latch with a constant period, sometime pulses are skipped (TIMER B CMP1 is the only and highest IRQ).

Here follows the TIMER B init code:

// > HRTIM Channel B Configuration Block (ADC SW Trigger)
// Time Base Settings:
LL_HRTIM_TIM_SetPrescaler(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_PRESCALERRATIO_MUL8);
LL_HRTIM_TIM_SetPeriod(HRTIM1, LL_HRTIM_TIMER_B, iInverter_PWM_Level);
LL_HRTIM_TIM_SetRepetition(HRTIM1, LL_HRTIM_TIMER_B, 0x00);
// Time Base Settings:
// The Timer is set in Up-Down Center-Aligned Mode. It's event generation IP is
// configured in "crest" mode (events are generated when counter reach period value).
// By now only the master timer PER event is used, slave event are just configured.
LL_HRTIM_TIM_SetCountingMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_COUNTING_MODE_UP);
LL_HRTIM_TIM_SetRollOverMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_ROLLOVER_MODE_PER);
LL_HRTIM_TIM_SetFaultEventRollOverMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_ROLLOVER_MODE_PER);
LL_HRTIM_TIM_SetBMRollOverMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_ROLLOVER_MODE_PER);
LL_HRTIM_TIM_SetADCRollOverMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_ROLLOVER_MODE_PER);
LL_HRTIM_TIM_SetOutputRollOverMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_ROLLOVER_MODE_PER);
// Time Base Settings:
// Counter in Free-Run (Continuous Mode)
LL_HRTIM_TIM_SetCounterMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_MODE_CONTINUOUS);
// Timing Unit:
LL_HRTIM_TIM_SetInterleavedMode(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_INTERLEAVED_MODE_DISABLED);
LL_HRTIM_TIM_DisableHalfMode(HRTIM1, LL_HRTIM_TIMER_B);
LL_HRTIM_TIM_DisableStartOnSync(HRTIM1, LL_HRTIM_TIMER_B);
LL_HRTIM_TIM_DisableResetOnSync(HRTIM1, LL_HRTIM_TIMER_B);
LL_HRTIM_TIM_SetDACTrig(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_DACTRIG_NONE);
LL_HRTIM_TIM_DisablePreload(HRTIM1, LL_HRTIM_TIMER_B);	// Must be disabled as CNT1 is used.
LL_HRTIM_TIM_SetUpdateGating(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_UPDATEGATING_INDEPENDENT);
LL_HRTIM_TIM_SetBurstModeOption(HRTIM1, LL_HRTIM_TIMER_B, LL_HRTIM_BURSTMODE_MAINTAINCLOCK);
LL_HRTIM_TIM_DisablePushPullMode(HRTIM1, LL_HRTIM_TIMER_B);
LL_HRTIM_TIM_SetUpdateTrig(HRTIM1, LL_HRTIM_TIMER_B, u32_HRTIM_UpdateTrig);
LL_HRTIM_TIM_DisableResyncUpdate(HRTIM1, LL_HRTIM_TIMER_B);
LL_HRTIM_TIM_SetResetTrig(HRTIM1, LL_HRTIM_TIMER_B, u32_HRTIM_ResetTrig);
//	Compare Unit 1:
// Duty is set to 50% FIXED, as this is for ADC Trigger Generation
LL_HRTIM_TIM_SetComp1Mode(HRTIM1, LL_HRTIM_TIMER_B, u32_HRTIM_SetComp1Mode);
LL_HRTIM_TIM_SetCompare1(HRTIM1, LL_HRTIM_TIMER_B, (iInverter_PWM_Level>>1));
 
// Wait for configuration applied
while(LL_HRTIM_IsActiveFlag_DLLRDY(HRTIM1) == RESET){};
 
// Enable TIMERB CMP1 interrupt */
LL_HRTIM_EnableIT_CMP1(HRTIM1, LL_HRTIM_TIMER_B );
NVIC_EnableIRQ(	HRTIM1_TIMB_IRQn );
NVIC_SetPriority( HRTIM1_TIMB_IRQn, 0 );

The ISR code to manage TIMERB CMP1, toggle of a pin (simulating ADC Triggering):

void HRTIM_TIMB_IRQn_IRQHandler (void) { //HRTIM_Master_IRQn_IRQHandler( void ){
 // Code must check for a specific enabled flag
 if( LL_HRTIM_IsActiveFlag_CMP1( HRTIM1, LL_HRTIM_TIMER_B ) == 1 ){ 
 LL_HRTIM_ClearFlag_CMP1( HRTIM1, LL_HRTIM_TIMER_B );
 vGpioAPI_Toggle(xGpioAPI_XMI_GPIO2);	
 }
}

Here follows the scope acquisitions of the issue, highlighted in red:

  • in yellow is the CENTER ALIGNED PWM output of TIMER A, with a varying duty cycle
  • in blue the vGpioAPI_Toggle(xGpioAPI_XMI_GPIO2) function, called inside the TIMERB CMP1 IRQ, with fixed 50% duty.

0693W00000JNJ6jQAH.png0693W00000JNJ6oQAH.png

Francois VILMAIN
ST Employee
January 28, 2022

Hello Davide,

Attached is a snapshot of the HRTIM master, HRTIM common and ADC1 registers taken with a working configuration. It might help you to identify what is missing in your software. Note that as far as HRTIM is concerned, the configuration is pretty simple.

Raggio
RaggioAuthor
Associate III
February 3, 2022

Dear @Francois VILMAIN​ ,

Thank you for your answer.

I've tried to replicate the configuration you've suggested but I still get an erroneous behaviour of the HRTIM peripheral.

That's my minima init of HRTIM, that results in the same Common & Master register configuration as indicated in your register snapshot.

HRTIM1->sMasterRegs.MCR		|= 0x2001000B;		
HRTIM1->sMasterRegs.MDIER	|= 0x00000010;
HRTIM1->sMasterRegs.MCNTR	= 0x00000000;
HRTIM1->sMasterRegs.MPER	= 2*iInverter_PWM_Level;
HRTIM1->sMasterRegs.MCMP1R	= iInverter_PWM_Level;
 
// a) CMP1 Tentative
LL_HRTIM_ConfigADCTrig(HRTIM1, LL_HRTIM_ADCTRIG_1, LL_HRTIM_ADCTRIG_UPDATE_MASTER, LL_HRTIM_ADCTRIG_SRC13_MCMP1);
// b) MPER Tentative
//LL_HRTIM_ConfigADCTrig(HRTIM1, LL_HRTIM_ADCTRIG_1, LL_HRTIM_ADCTRIG_UPDATE_MASTER, LL_HRTIM_ADCTRIG_SRC13_MPER);
// Postascaler
LL_HRTIM_SetADCPostScaler(HRTIM1, LL_HRTIM_ADCTRIG_1, 20);

On the ADC Side, EXTSE bits have the right pattern [10101], EXTEN have rising edge [01].

That is done by the following instructions:

// Enable the ADC external trigger
LL_ADC_REG_SetTriggerSource( ADC1, LL_ADC_REG_TRIG_EXT_HRTIM_TRG1 );
LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING);

If, instead of External, I configure the ADC to use the Software Trigger with "LL_ADC_REG_SetTriggerSource( ADC1, LL_ADC_REG_TRIG_SOFTWARE )", the acquisition pattern starts and complete without issue.

I believe my problem is that the HRTIM is not generating the TRG1 but I really cannot understand the reason. Attached you can find my registers snapshot: maybe you can spot the elephant in the room.

Thank you,

Davide

0693W00000JONR9QAP.png

Francois VILMAIN
Francois VILMAINBest answer
ST Employee
February 3, 2022

Hello Davide,

I developed a short test code to check your use case. Initially I got the same behavior because I forgot to call LL_ADC_REG_StartConversion(ADC1) after the HRTIM1/ADC1 initialization step.

Now I get something working as expected. You'll find attached a zip file containing the source code I used to investigate this point (+ IAR project config.). I ran this code on a MB1428 board.

Le me know whether or not it helps.

regards,

François

Raggio
RaggioAuthor
Associate III
February 4, 2022

Dear @Francois VILMAIN​ ,

And that's the missing piece!

Coming from F4s I've always used LL_ADC_REG_StartConversionExtTrig( ADC1, ExternalTrigger). Not seeing the function inside the library I've thought that it was not necessary anymore, but of course it is.

Regards,

Davide