cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G474, HRTIM as external ADC trigger source

Raggio
Associate III

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

1 ACCEPTED SOLUTION

Accepted Solutions
Francois VILMAIN
Associate II

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

View solution in original post

5 REPLIES 5
Raggio
Associate III

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
Associate II

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.

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
Associate II

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

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