2017-12-12 11:37 PM
On an STM32F407 device I'm configuring a timer TIM4 in input capture mode to measure the input duration of a PWM signal on the Channel 3 input. The signal comes for a US Digital absolute encoder where the duty cycle of the 4ms PWM signal indicates the encoder position.
The problem I'm having is that when I swap the capture/compare 3 polarity (CC3P) bit in the timer capture/compare enable register (CCER) register, the timer continues to only detect the rising edge of the PWM signal. Code similar to that shown below works for advanced control timers TIM1 and TIM8 on all four channels, but it won't work for general purpose TIM4 on channel 3. Is the ability to detect a falling edge a known issue with general purpose timers?
My interrupt handler where I swap the polarity bit is shown below.
volatile uint16_t rise_time = 0;
void TIM4_IRQHandler(void)
{
uint16_t ccer;
uint16_t capture;
uint16_t duration;
// TMR4 Channel 3 steering position capture processing.
if ((TIM4->SR & TIM_IT_CC3) && (TIM4->DIER & TIM_IT_CC3))
{
// Get the input capture value. This clears the interrupt.
capture = (uint16_t) TIM4->CCR3;
// Get the timer capture compare enable register (CCER).
ccer = TIM4->CCER;
// Did we detect the rising or falling edge?
if (ccer & 0x20)
{
// Falling edge so determine the pulse duration.
duration = capture - rise_time;
// Process the duration.
pwm_process_duration(PWM_CHANNEL_STEER, duration);
}
else
{
// Rising edge so we save the capture value.
rise_time = capture;
}
// Invert the capture polarity bit.
ccer ^= 0x20;
// Make sure capture is enabled.
ccer |= 0x10;
// Write back the CCER register.
TIM4->CCER = ccer;
}
}
My GPIO and timer initialization code is here:
GPIO_InitTypeDef gpio_init;
TIM_ICInitTypeDef tim_ic_init;
TIM_TimeBaseInitTypeDef tim_timebase_init;
// Alternate functions for TIM4 channel 3 and 4 pins.
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_TIM4);
// Configure GPIOB pins for alternate functions.
GPIO_StructInit(&gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_8;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &gpio_init);
// Timer configuration.
// APB1 clock is 84MHz so a prescaler of 84000000 / 1000000 - 1 = 83
tim_timebase_init.TIM_Period = 0xffff;
tim_timebase_init.TIM_Prescaler = 83;
tim_timebase_init.TIM_ClockDivision = TIM_CKD_DIV1;
tim_timebase_init.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &tim_timebase_init);
// TIM4 channel 3 configuration.
TIM_ICStructInit(&tim_ic_init);
tim_ic_init.TIM_Channel = TIM_Channel_3;
tim_ic_init.TIM_ICPolarity = TIM_ICPolarity_Rising;
tim_ic_init.TIM_ICSelection = TIM_ICSelection_DirectTI;
tim_ic_init.TIM_ICPrescaler = TIM_ICPSC_DIV1;
tim_ic_init.TIM_ICFilter = 0x0;
TIM_ICInit(TIM4, &tim_ic_init);
// Clear the interrupt pending bits.
TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
// Enable the TIM4 CC4 interrupt request.
TIM_ITConfig(TIM4, TIM_IT_CC4, ENABLE);
// Enable TIM4.
TIM_Cmd(TIM4, ENABLE);
Thanks,
Mike
Solved! Go to Solution.
2017-12-12 11:58 PM
- you enable channel 4 and then check for channel 3
- you don't clear the flag in SR which caused the interrupt
JW
2017-12-12 11:58 PM
- you enable channel 4 and then check for channel 3
- you don't clear the flag in SR which caused the interrupt
JW
2017-12-13 10:20 PM
Figured out my problem in the interrupt handler. The constant I was using to check and clear the capture/compare 3 polarity (CC3P) bit in the capture/compare enable register (CCER) was 0x20, but this is incorrect. It should have been 0x200 for the CC3P bit, rather than 0x20 which is the CC2P bit and was the wrong channel.
2017-12-13 11:35 PM
Ah, thank you. Using the GPIO_AF_TIM4 rather than GPIO_AF_TIM3 is a problem which I fixed.
However, the interrupt is cleared by reading the TIM4->CCR register so I don't seem to need to touch the TIM4-SR register.
I had another problem of using the wrong constant (0x20 rather than 0x200) to check and toggle the CC3P bit in the CCER.
With these issues fixed the code now functions as expected and correctly determines the duration of the PWM signal in clock counts.
I'm including the correct code below with the fixes from the bad code originally posted.
volatile uint16_t rise_time = 0;
void TIM4_IRQHandler(void)
{
uint16_t ccer;
uint16_t capture;
uint16_t duration;
// TMR4 Channel 3 steering position capture processing.
if ((TIM4->SR & TIM_IT_CC3) && (TIM4->DIER & TIM_IT_CC3))
{
// Get the input capture value. This clears the interrupt.
capture = (uint16_t) TIM4->CCR3;
// Get the timer capture compare enable register (CCER).
ccer = TIM4->CCER;
// Did we detect the rising or falling edge?
if (ccer & 0x200)
{
// Falling edge so determine the pulse duration.
duration = capture - rise_time;
// Process the duration.
pwm_process_duration(PWM_CHANNEL_STEER, duration);
}
else
{
// Rising edge so we save the capture value.
rise_time = capture;
}
// Invert the capture polarity bit.
ccer ^= 0x200;
// Make sure capture is enabled.
ccer |= 0x10;
// Write back the CCER register.
TIM4->CCER = ccer;
}
}
GPIO_InitTypeDef gpio_init;
TIM_ICInitTypeDef tim_ic_init;
TIM_TimeBaseInitTypeDef tim_timebase_init;
// Alternate functions for TIM4 channel 3 and 4 pins.
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_TIM3);
// Configure GPIOB pins for alternate functions.
GPIO_StructInit(&gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_8;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &gpio_init);
// Timer configuration.
// APB1 clock is 84MHz so a prescaler of 84000000 / 1000000 - 1 = 83
tim_timebase_init.TIM_Period = 0xffff;
tim_timebase_init.TIM_Prescaler = 83;
tim_timebase_init.TIM_ClockDivision = TIM_CKD_DIV1;
tim_timebase_init.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &tim_timebase_init);
// TIM4 channel 3 configuration.
TIM_ICStructInit(&tim_ic_init);
tim_ic_init.TIM_Channel = TIM_Channel_3;
tim_ic_init.TIM_ICPolarity = TIM_ICPolarity_Rising;
tim_ic_init.TIM_ICSelection = TIM_ICSelection_DirectTI;
tim_ic_init.TIM_ICPrescaler = TIM_ICPSC_DIV1;
tim_ic_init.TIM_ICFilter = 0x0;
TIM_ICInit(TIM4, &tim_ic_init);
// Clear the interrupt pending bits.
TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
// Enable the TIM4 CC4 interrupt request.
TIM_ITConfig(TIM4, TIM_IT_CC4, ENABLE);
// Enable TIM4.
TIM_Cmd(TIM4, ENABLE);
Thanks,
Mike
2017-12-14 02:10 AM
However, the interrupt is cleared by reading the TIM4->CCR register so I don't seem to need to touch the TIM4-SR register.
Indeed. Sorry for the noise.
had another problem of using the wrong constant (0x20 rather than 0x200) to check and toggle the CC3P bit in the CCER.
This is why I preach using the CMSIS-mandated-header constants rather than 'mägic numbers':
// Invert the capture polarity bit.
TIM_CCER_CC3P;
// Make sure capture is enabled.
0x10;
This is redundant and you know it. Redundant things sometimes bite.
JW