Skip to main content
Visitor
June 22, 2026
Question

How to trigger ADC with timer CC event using STM32F103

  • June 22, 2026
  • 3 replies
  • 97 views

I’m using stm32f103 timer cc event to trigger adc injected conv. But I failed. I can trigger it with timer trgo. This means my adc configuration is correct. 

LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_CENTER_UP_DOWN;
TIM_InitStruct.Autoreload = 2249;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM2;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE;
TIM_OC_InitStruct.CompareValue = 0;
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH4);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH4, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH4);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_OC4REF);
LL_TIM_EnableMasterSlaveMode(TIM1);
TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
TIM_BDTRInitStruct.DeadTime = 0;
TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_ENABLE;
TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;
TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct);

LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
GPIO_InitStruct.Pin = LL_GPIO_PIN_13 | LL_GPIO_PIN_14 | LL_GPIO_PIN_15;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_8 | LL_GPIO_PIN_9 | LL_GPIO_PIN_10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

LL_TIM_EnableAllOutputs(TIM1);
// LL_TIM_EnableIT_CC4(TIM1);
LL_TIM_EnableCounter(TIM1);
LL_ADC_InitTypeDef ADC_InitStruct = {0};
LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
LL_ADC_INJ_InitTypeDef ADC_INJ_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
GPIO_InitStruct.Pin = LL_GPIO_PIN_4 | LL_GPIO_PIN_6;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_DISABLE;
LL_ADC_Init(ADC1, &ADC_InitStruct);
ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_DUAL_REG_SIM_INJ_SIM;
LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
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;
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
ADC_INJ_InitStruct.TriggerSource = LL_ADC_INJ_TRIG_EXT_TIM1_CH4;
ADC_INJ_InitStruct.SequencerLength = LL_ADC_INJ_SEQ_SCAN_DISABLE;
ADC_INJ_InitStruct.SequencerDiscont = LL_ADC_INJ_SEQ_DISCONT_DISABLE;
ADC_INJ_InitStruct.TrigAuto = LL_ADC_INJ_TRIG_INDEPENDENT;
LL_ADC_INJ_Init(ADC1, &ADC_INJ_InitStruct);

LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_6);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_6,
LL_ADC_SAMPLINGTIME_13CYCLES_5);

LL_ADC_INJ_SetSequencerRanks(ADC1, LL_ADC_INJ_RANK_1, LL_ADC_CHANNEL_4);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4,
LL_ADC_SAMPLINGTIME_1CYCLE_5);
LL_ADC_INJ_SetOffset(ADC1, LL_ADC_INJ_RANK_1, 0);

LL_ADC_Enable(ADC1);
LL_ADC_StartCalibration(ADC1);
while (LL_ADC_IsCalibrationOnGoing(ADC1)) {
}
LL_ADC_EnableIT_EOS(ADC1);
LL_ADC_EnableIT_JEOS(ADC1);

LL_ADC_INJ_StartConversionExtTrig(ADC2, LL_ADC_INJ_TRIG_EXT_RISING);
LL_ADC_INJ_StartConversionExtTrig(ADC1, LL_ADC_INJ_TRIG_EXT_RISING);

 

3 replies

mxmxlwlwAuthor
Visitor
June 22, 2026

disable brake

ST Technical Moderator
June 22, 2026

Hello ​@mxmxlwlw 

The ADC injected trigger configuration appears to be valid, since the conversion is already working with TIM1 TRGO. The likely limitation is that LL_ADC_INJ_TRIG_EXT_TIM1_CH4 depends on the TIM1 channel 4 compare event (CC4), not on the TRGO signal generated from OC4REF. With CCR4 = 0 and CH4 configured in PWM mode, the CC4 event may not be generated in a way that the ADC injected trigger can use reliably. I would recommend setting CCR4 to a valid compare value inside the timer period, and configuring CH4 in output compare / timing mode rather than PWM, so that the CC4 event is produced explicitly.

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Saket_Om
waclawek.jan
Super User
June 22, 2026

LL_ADC_INJ_TRIG_EXT_TIM1_CH4 in RM008 ADC_CR2.JEXTSEL description maps to

> Timer 1 CC4 event

That’s an unfortunately incorrectly named signal. It’s not CC4 (i.e. signal setting TIMx_SR.CCxIF, which would imply Capture can be used), it’s not TRGO (i.e. output signal from the mux controlled by TIMx_CR2.MMS), and it’s not OC4REF (i.e. internal signal from the channel’s output comparator) either. Instead, it’s directly the TIM1’s CH4 output. You may need to set a mode generating a waveform there (normally PWM), and you need also enable given channel by setting respective TIMx_CCER.CCxE bit. If you’d have a pin set to AF to this TIMx_CHx in GPIO/AFIO, you would see a waveform there, and the appropriate edges (rising? I’m not quite sure) are those where the ADC is triggered. But you don’t need to actually set a pin to output that waveform.

JW