2020-09-22 03:41 AM
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
Solved! Go to Solution.
2020-09-23 01:44 AM
With the change concerning the function LL_ADC_REG_StartConversionExtTrig mentioned yesterday the program seems to work.
Thanks for your time and help!
2020-09-22 04:04 AM
See TIMx_CR1.CMS description, and PWM center-aligned mode
subchapter in TIM chapter in RM.
JW
2020-09-22 04:18 AM
Hello Jan,
thank you for your fast and precise reply. I checked this register. It is set by the function LL_TIM_Init
depending on the value in TIM_InitStruct.CounterMode (line 27 in timer setup). I used an custom macro for that, sorry for the inconvenience. But essentially the CMS is set correctly (as I wrote, the capture compare ISR works as expected).
I think, that i actually found the reason for unwanted ADC behaviour in another place. Line 47 is wrongly configured. It is supposed to be:
LL_ADC_REG_StartConversionExtTrig(ADC2, LL_ADC_REG_TRIG_EXT_RISING);
Am I on the right track, or is something else missing or fundamentally flawed?
2020-09-22 11:27 AM
I don't know, I don't Cube.
Read out the TIM and ADC registers content and check them against the manual; if in doubt, post them.
JW
2020-09-23 01:44 AM
With the change concerning the function LL_ADC_REG_StartConversionExtTrig mentioned yesterday the program seems to work.
Thanks for your time and help!