Skip to main content
Explorer II
July 22, 2024
Question

Timer Input Capture duty cycle calculation is sometimes complementary

  • July 22, 2024
  • 3 replies
  • 2154 views

Hi!

I have two PWM signals coming into two MCU pins, that are two TIM4 channels. Channel 1 and Channel 2.

 

I initialized both Channel 1 and Channel 2 to input capture mode on Both Edges and enabled the Capture/Compare Interrupt (Not in slave master mode but free running)

 

I capture the value for 3 times and save it into an array and then proceed to calculate the full period as 3rd value minus the first one and the high period as 2nd value minus the first one.

 

The duty calculation is correct but sometimes instead of getting the correct duty cycle ( ex.70%) I get the complementary: 100% - 70% = 30%

 

Channel 1 Configuration:

RCC_ClocksTypeDef __RCC_CLOCK;

	myPWMInputCtor(me);

	RCC_GetClocksFreq(&__RCC_CLOCK);
	me->clock = __RCC_CLOCK.PCLK1_Frequency * 2; //APB1

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

	me->GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	me->GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	me->GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	me->GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	me->GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

	GPIO_Init(GPIOD, &me->GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);

	TIM4_IRQHandler_ptf = TIM4_IRQ_PWM;
	me->NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	me->NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	me->NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	me->NVIC_InitStructure.NVIC_IRQChannelCmd = 1;
	NVIC_Init(&me->NVIC_InitStructure);

	me->TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
	me->TIM_TimeBaseStructure.TIM_Prescaler = 840 - 1;
	me->TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	me->TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1;
	me->TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

	TIM_TimeBaseInit(TIM4, &me->TIM_TimeBaseStructure);

	me->TIM_CH1_ICInitStructure.TIM_Channel 		= TIM_Channel_1;
	me->TIM_CH1_ICInitStructure.TIM_ICPolarity 		= TIM_ICPolarity_BothEdge;
	me->TIM_CH1_ICInitStructure.TIM_ICSelection 	= TIM_ICSelection_DirectTI;
	me->TIM_CH1_ICInitStructure.TIM_ICPrescaler 	= TIM_ICPSC_DIV1;
	me->TIM_CH1_ICInitStructure.TIM_ICFilter 		= 0x0;

	TIM_ICInit(TIM4, &me->TIM_CH1_ICInitStructure);
	TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1);
 // Select the slave Mode: Reset Mod

 // Enable the CC1 and the update Interrupt Request
	TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);

	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
	TIM_Cmd(TIM4, ENABLE);

 

Channel 2 Configuration:

RCC_ClocksTypeDef __RCC_CLOCK;

	myPWMInputCtor(me);

	RCC_GetClocksFreq(&__RCC_CLOCK);
	me->clock = __RCC_CLOCK.PCLK1_Frequency * 2; //APB1

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

	me->GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	me->GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	me->GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	me->GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	me->GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

	GPIO_Init(GPIOD, &me->GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);

	TIM4_IRQHandler_ptf = TIM4_IRQ_PWM;
	me->NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	me->NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	me->NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	me->NVIC_InitStructure.NVIC_IRQChannelCmd = 1;
	NVIC_Init(&me->NVIC_InitStructure);

	me->TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
	me->TIM_TimeBaseStructure.TIM_Prescaler = 840 - 1;
	me->TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	me->TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1;
	me->TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

	TIM_TimeBaseInit(TIM4, &me->TIM_TimeBaseStructure);

	me->TIM_CH2_ICInitStructure.TIM_Channel 		= TIM_Channel_2;
	me->TIM_CH2_ICInitStructure.TIM_ICPolarity 		= TIM_ICPolarity_BothEdge;
	me->TIM_CH2_ICInitStructure.TIM_ICSelection 	= TIM_ICSelection_DirectTI;
	me->TIM_CH2_ICInitStructure.TIM_ICPrescaler 	= TIM_ICPSC_DIV1;
	me->TIM_CH2_ICInitStructure.TIM_ICFilter 		= 0x0;

	TIM_ICInit(TIM4, &me->TIM_CH2_ICInitStructure);

	 // Select the TIM8 Input Trigger: TI1FP1
	TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);
 // Select the slave Mode: Reset Mode
	//TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);


	TIM_ITConfig(TIM4, TIM_IT_CC2, ENABLE);

	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
	TIM_Cmd(TIM4, ENABLE);

 

duty cycle calculation:

if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET) {
			tmyPWMInput *pPWM4_1 = &myCTRL.myHW.myPortDCfg.myPWM4_1; //PIN12
			 // Clear TIM8 Capture Compare1 interrupt pending bit
			TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
			if ( i != 3 ) ch1[i++] = TIM4->CCR1;
			else if (i == 3) {
				uint8_t j;

				uint32_t full_period = ch1[2] - ch1[0];
				uint32_t high_period = (ch1[1] - ch1[0]);
				pPWM4_1->HighPeriod = high_period;
				pPWM4_1->FullPeriod = full_period;

				myPWMCalc(pPWM4_1);
				i = 0;
			}



	}
	if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET) {
			tmyPWMInput *pPWM4_2 = &myCTRL.myHW.myPortDCfg.myPWM4_2; //PIN13
			 // Clear TIM8 Capture Compare1 interrupt pending bit
			TIM_ClearITPendingBit(TIM4, TIM_IT_CC2);
			if ( z != 3 ) ch2[z++] = TIM4->CCR2;
			else if ( z == 3 ) {

				uint32_t full_period = ch2[2] - ch2[0];
				uint32_t high_period = (ch2[1] - ch2[0]);
				pPWM4_2->HighPeriod = high_period;
				pPWM4_2->FullPeriod = full_period;

				myPWMCalc(pPWM4_2);
				z = 0;
			}

	}

 

Any idea on how to workaround this? or why it does this?

    This topic has been closed for replies.

    3 replies

    Super User
    July 22, 2024

    If you capture on both edges, the timer may hit a rising or a falling edge first what explains your observations. If timing is not too fast, you may query the signal level in the interrupt handler and adjust your interpretation of the time stamps accordingly.

    Are the two PWMs coherent with one edge in common or independent? In the first case, combined input PWM capture may help, in the second case, I would use anohter pair of combined channels.

    hth

    KnarfB

     

     

    Explorer II
    July 22, 2024

    I'm sorry, could you explain further what can I do to workaround it? 

    "you may query the signal level in the interrupt handler and adjust your interpretation of the time stamps accordingly."

     

    Unfortunately I can only use these two channels.

    Super User
    July 22, 2024

    The GPIO IDR register of a PWM pin should hold the current pin level, even if the pin is configured to alternate function mode for the timer. So, if your handler is fast enough, you can query IDR whether the first capture hit a rising or a falling edge.

    hth

    KnarfB 

    Super User
    July 22, 2024

    If your signals are slow, say up to maybe 10 kHz, you can do what you propose. If they're very fast, you will miss edges and it will be difficult to know which edge is rising and which is falling, which is ultimately the cause of the error here.

    ST Employee
    July 24, 2024

    It seems sometimes you missed an edge during the duty calculation, maybe due to the interrupt preemption? You can check other interrupt priority