cancel
Showing results for 
Search instead for 
Did you mean: 

How to trigger ADC conversion via timer correctly?

CBrom.1
Associate II

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

1 ACCEPTED SOLUTION

Accepted Solutions
CBrom.1
Associate II

With the change concerning the function LL_ADC_REG_StartConversionExtTrig mentioned yesterday the program seems to work.

Thanks for your time and help!

View solution in original post

4 REPLIES 4

See TIMx_CR1.CMS description, and PWM center-aligned mode

subchapter in TIM chapter in RM.

JW

CBrom.1
Associate II

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?

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

CBrom.1
Associate II

With the change concerning the function LL_ADC_REG_StartConversionExtTrig mentioned yesterday the program seems to work.

Thanks for your time and help!