Question
Strange readings using multiple timer captures to read PWM duty cycle
Posted on January 10, 2015 at 23:40
Hi there,
I'm using an STM32F4 Discovery to build my own quadcopter flight controller.
I have wired up a RC receiver's throttle channel to pin B6 on the discovery. My receiver sends a PWM signal over that wire which I am attempting to read the duty cycle and frequency of using multiple channels of a timer.
I have set all of this up, and it works most of the time. Unfortunately I'm getting a problem where the duty cycle value I calculate skyrockets to Further inspection shows that the calculation is 100 because the TIM_GetCapture1(TIM4) and TIM_GetCapture2(TIM4) function calls are returning the same value.
I haven't been able to understand why this might be happening. I am measuring a different PWM signal on another timer without issues. The other measurement uses the same NVIC_IRQChannelPreemptionPriority and NVIC_IRQChannelSubPriority. I don't know whether this is a problem or not.
Is anyone able to help me understand what might be going on? Code is below.
Cheers
Cam
RCC_ClocksTypeDef RCC_Clocks;
void
InitialisePWMInputThrottle() {
/* Note that this timer is on AHB1, which means it runs at 80,000,000 Hz. */
/* AHB1 Peripherals run at half the HCLK Speed */
pwmInputTimer4.hclckDivisor = 2.0f;
/* Enable the timer clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/* Initialise duty cycle and freq to zero */
pwmInputTimer4.dutyCycle = 0.0;
pwmInputTimer4.frequency = 0.0;
pwmInputTimer4.ic1value = 0;
pwmInputTimer4.ic2value = 0;
/* Work out the system / bus / timer clock speed */
RCC_GetClocksFreq(&RCC_Clocks);
/* Enable the clock to the GPIO Port */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* Turn on PB06, it will be connected to Timer 4, Channel 1.
* Timer Channel 2 will also be used, I believe this renders pin PB7 unusable.
*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect TIM pin to alternate function */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);
/* init the timer:
* It doesn't really matter what prescaler we use, because the duty cycle is calculated as a percentage.
* (as long as the prescalar ensures that the counter will not overflow)
* The maximum (16bit) period should never be reached, as we will reset the counter before we get there.
*/
TIM_TimeBaseInitTypeDef timerInitStructure;
timerInitStructure.TIM_Prescaler = 1000;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = 65535;
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
timerInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM4, &timerInitStructure);
/* Enable the Timer counter */
TIM_Cmd(TIM4, ENABLE);
/* We're attempting to not have a prescalar, that is, not divide the incoming signal.
* I wonder this prescalar differs from the above prescalar.
* Channel 1 is configured to capture on the rising edge.
* */
TIM_ICInitTypeDef TIM_ICInitStructure1;
TIM_ICInitStructure1.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure1.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure1.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure1.TIM_ICPrescaler = 0;
TIM_ICInitStructure1.TIM_ICFilter = 0;
TIM_ICInit(TIM4, &TIM_ICInitStructure1);
/* Channel 2 is configured to capture on the falling edge. */
TIM_ICInitTypeDef TIM_ICInitStructure2;
TIM_ICInitStructure2.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure2.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure2.TIM_ICSelection = TIM_ICSelection_IndirectTI;
TIM_ICInitStructure2.TIM_ICPrescaler = 0;
TIM_ICInitStructure2.TIM_ICFilter = 0;
TIM_ICInit(TIM4, &TIM_ICInitStructure2);
/* Ensure that Channel two is set up as a slave, and that it resets the counters on a falling edge */
TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);
/* Enable the interrupt that gets fired when the timer counter hits the period */
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
/* Enable the Timer interrupts */
NVIC_InitTypeDef nvicStructure;
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
nvicStructure.NVIC_IRQChannelSubPriority = 1;
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicStructure);
}
void
TIM4_IRQHandler()
{
/* makes sure the interrupt status is not reset (and therefore SET?) */
if
(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) {
/* ensure that the interrupt doesn't get triggered again */
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
uint32_t IC1Value = TIM_GetCapture1(TIM4);
uint32_t IC2Value = TIM_GetCapture2(TIM4);
/* As a percentage */
float
updatedDutyCycle = (IC2Value * 0f) / IC1Value;
/* HCLK is the Advanced High Speed Bus (AHB) Clock Speed, which is a
factor of the System Clock (one, at the moment, hence is the same) */
float
updatedFrequency = (RCC_Clocks.HCLK_Frequency) / pwmInputTimer4.hclckDivisor / (IC1Value * 1000);
pwmInputTimer4.ic1value = IC1Value;
pwmInputTimer4.ic2value = IC2Value;
pwmInputTimer4.dutyCycle = updatedDutyCycle;
pwmInputTimer4.frequency = updatedFrequency;
}
}
#stm32f4 #input #discovery #pwm