How to trigger ADC conversion via timer correctly?
Dear Community,
for some time I've tried to set up the hardware of a STM32F429ZI to periodically trigger a discontinuous ADC conversion with 2 channels.
I'm using TIM1 in PWM mode to generate CC interrupts with TIM CH3. The timer is running in center mode, counting up and down sequentially. The TIM1 CC3 interrupt is supposed to trigger the ADC conversion once per PWM period (when the counter is counting up).
Testing the CC3 interrupt generation using the NVIC ISR and Osci shows expected behavior.
Testing the ADC conversion timing using the ADC_IRQHandler shows 2 calls of this ISR in one PWM period. When I change the Capture Compare value of CH3 the two ADC_IRQHanlder calls shift symmetrically w.r.t. the center of the PWM period. Therefore I think that the ADC is triggered at both capture compare matches of CH3. I'd like it to be triggered only on the first capture compare event.
Did I somewhere wrongly configured the timer or adc trigger setup?
The timer configuration is as follows:
/**
******************************************************************************
* Configure project specific GPIO Pins to be used as output with the TIM1 PWM.
******************************************************************************
* TIM1_CH1 -> PE9
* TIM1_CH1N -> PE8
* TIM1_CH2 -> PE11
* TIM1_CH2N -> PE10
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_8 | LL_GPIO_PIN_9
| LL_GPIO_PIN_10 | LL_GPIO_PIN_11
| LL_GPIO_PIN_12 | LL_GPIO_PIN_13;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOE, &GPIO_InitStruct);
LL_TIM_DisableCounter(TIM1);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
/* Timer initialization */
TIM_InitStruct.Prescaler = TIM_CLOCKPRESCALER_DIV1;
TIM_InitStruct.CounterMode = PWM1_COUNTERMODE;
TIM_InitStruct.Autoreload = PWM_ARR;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = CTRL_LOOP_PRESCALER - 1;
LL_TIM_Init(TIM1, &TIM_InitStruct);
/*
******************************************************************************
* Channel Configuration
******************************************************************************
*/
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE;
TIM_OC_InitStruct.CompareValue = TIM1->ARR*3/4; // initial duty cycle
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_LOW;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
TIM_OC_InitStruct.CompareValue = (TIM1->ARR*3/4 - PWM_CCR_MIN); // initial duty cycle
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH3);
/* Break and Dead Time options */
TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
TIM_BDTRInitStruct.DeadTime = PWM_DEAD_TIME;
TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;
TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;
TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
TIM1->DIER |= TIM_DIER_CC3IE;
NVIC_EnableIRQ(TIM1_CC_IRQn);
NVIC_SetPriority(TIM1_CC_IRQn,
NVIC_EncodePriority(NVIC_GetPriorityGrouping(),
NVIC_TIM1CC_PRE_PRIO,
NVIC_TIM1CC_SUB_PRIO));
LL_TIM_EnableAllOutputs(TIM1);
LL_TIM_EnableCounter(TIM1);The ADC is configured as:
/**
******************************************************************************
* Configure the GPIO Pin used as analog input for ADC3 on Channel 10.
******************************************************************************
* ADC2_IN10 -> PC0 (Channel 10)
* ADC2_IN11 -> PC1 (Channel 11)
*/
GPIO_AnalogInput_InitStruct.Pin = LL_GPIO_PIN_0;
LL_GPIO_Init(GPIOC, &GPIO_AnalogInput_InitStruct);
GPIO_AnalogInput_InitStruct.Pin = LL_GPIO_PIN_1;
LL_GPIO_Init(GPIOC, &GPIO_AnalogInput_InitStruct);
ADC_InitStruct.Resolution = ADC2_RESOLUTION;
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_DISABLE;
LL_ADC_Init(ADC2, &ADC_InitStruct);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC2);
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
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_NONE; // None to use ADC without DMA
LL_ADC_REG_Init(ADC2, &ADC_REG_InitStruct);
LL_ADC_REG_SetFlagEndOfConversion(ADC2, LL_ADC_REG_FLAG_EOC_SEQUENCE_CONV);
ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV2;
ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT;
LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC2), &ADC_CommonInitStruct);
LL_ADC_SetChannelSamplingTime(ADC2, LL_ADC_CHANNEL_10, CONF_ADC_SYMPLING_CYCLES);
LL_ADC_SetChannelSamplingTime(ADC2, LL_ADC_CHANNEL_11, CONF_ADC_SYMPLING_CYCLES);
ADC2->CR1 |= (ADC2->CR1 & ~ADC_CR1_DISCNUM_Msk) | ( 1UL << ADC_CR1_DISCNUM_Pos);
ADC2->CR1 |= ADC_CR1_DISCEN;
LL_ADC_SetSequencersScanMode(ADC2, LL_ADC_SEQ_SCAN_ENABLE);
// Set channel conversion number and sequence
LL_ADC_REG_SetSequencerLength(ADC2, LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS);
LL_ADC_REG_SetSequencerRanks(ADC2, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_10);
LL_ADC_REG_SetSequencerRanks(ADC2, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_11);
// Enable external trigger
LL_ADC_REG_StartConversionExtTrig(ADC2, ADC_CR2_EXTEN);
// Select external trigger (RM0090 p. 418)
LL_ADC_REG_SetTriggerSource(ADC2, LL_ADC_REG_TRIG_EXT_TIM1_CH3);
LL_ADC_EnableIT_EOCS(ADC2); // set EOCIE in CR1
NVIC_EnableIRQ(ADC_IRQn);
NVIC_SetPriority(ADC_IRQn,
NVIC_EncodePriority(NVIC_GetPriorityGrouping(),
NVIC_ADC2_PRE_PRIO,
NVIC_ADC2_SUB_PRIO+1));
LL_ADC_Enable(ADC2);The ISRs are only toggeling GPIO pins for debugging purposes.
I appreciate your help.
Cheers,
Clemens