2024-07-22 02:04 AM
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?
2024-07-22 02:15 AM
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
2024-07-22 02:16 AM - edited 2024-07-22 02:21 AM
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.
2024-07-22 02:27 AM
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
2024-07-22 03:06 AM
What do you think about about setting channel to rising, reading the captured value, setting the channel to falling, getting a capture and set it again to rising to get the final capture and calculate all the variables?
2024-07-22 06:20 AM
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.
2024-07-22 06:22 AM
This could also work. Double check that you can switch the settings on-the-fly and that the re-config works in time.
hth
KnarfB
2024-07-24 12:30 AM
It seems sometimes you missed an edge during the duty calculation, maybe due to the interrupt preemption? You can check other interrupt priority