AnsweredAssumed Answered

Strange readings using multiple timer captures to read PWM duty cycle

Question asked by swords.cameron on Jan 10, 2015
Latest reply on Mar 3, 2015 by Clive One


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 100. 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

001.RCC_ClocksTypeDef RCC_Clocks;
002. 
003.void InitialisePWMInputThrottle() {
004. 
005.    /* Note that this timer is on AHB1, which means it runs at 80,000,000 Hz. */
006.    /* AHB1 Peripherals run at half the HCLK Speed */
007.    pwmInputTimer4.hclckDivisor = 2.0f;
008. 
009.    /* Enable the timer clock */
010.    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
011. 
012. 
013.    /* Initialise duty cycle and freq to zero */
014.    pwmInputTimer4.dutyCycle = 0.0;
015.    pwmInputTimer4.frequency = 0.0;
016.    pwmInputTimer4.ic1value = 0;
017.    pwmInputTimer4.ic2value = 0;
018. 
019.    /* Work out the system / bus / timer clock speed */
020.    RCC_GetClocksFreq(&RCC_Clocks);
021. 
022.    /* Enable the clock to the GPIO Port */
023.    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
024. 
025.    /* Turn on PB06, it will be connected to Timer 4, Channel 1.
026.     * Timer Channel 2 will also be used, I believe this renders pin PB7 unusable.
027.     */
028.    GPIO_InitTypeDef GPIO_InitStructure;
029.    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
030.    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
031.    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
032.    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
033.    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
034.    GPIO_Init(GPIOB, &GPIO_InitStructure);
035. 
036.    /* Connect TIM pin to alternate function */
037.    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);
038. 
039.    /* init the timer:
040.     * It doesn't really matter what prescaler we use, because the duty cycle is calculated as a percentage.
041.     *    (as long as the prescalar ensures that the counter will not overflow)
042.     * The maximum (16bit) period should never be reached, as we will reset the counter before we get there.
043.     */
044.    TIM_TimeBaseInitTypeDef timerInitStructure;
045.    timerInitStructure.TIM_Prescaler = 1000;
046.    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
047.    timerInitStructure.TIM_Period = 65535;
048.    timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
049.    timerInitStructure.TIM_RepetitionCounter = 0;
050.    TIM_TimeBaseInit(TIM4, &timerInitStructure);
051. 
052.    /* Enable the Timer counter */
053.    TIM_Cmd(TIM4, ENABLE);
054. 
055.    /* We're attempting to not have a prescalar, that is, not divide the incoming signal.
056.     * I wonder this prescalar differs from the above prescalar.
057.     * Channel 1 is configured to capture on the rising edge.
058.     * */
059.    TIM_ICInitTypeDef TIM_ICInitStructure1;
060.    TIM_ICInitStructure1.TIM_Channel = TIM_Channel_1;
061.    TIM_ICInitStructure1.TIM_ICPolarity = TIM_ICPolarity_Rising;
062.    TIM_ICInitStructure1.TIM_ICSelection = TIM_ICSelection_DirectTI;
063.    TIM_ICInitStructure1.TIM_ICPrescaler = 0;
064.    TIM_ICInitStructure1.TIM_ICFilter = 0;
065.    TIM_ICInit(TIM4, &TIM_ICInitStructure1);
066. 
067.    /* Channel 2 is configured to capture on the falling edge. */
068.    TIM_ICInitTypeDef TIM_ICInitStructure2;
069.    TIM_ICInitStructure2.TIM_Channel = TIM_Channel_2;
070.    TIM_ICInitStructure2.TIM_ICPolarity = TIM_ICPolarity_Falling;
071.    TIM_ICInitStructure2.TIM_ICSelection = TIM_ICSelection_IndirectTI;
072.    TIM_ICInitStructure2.TIM_ICPrescaler = 0;
073.    TIM_ICInitStructure2.TIM_ICFilter = 0;
074.    TIM_ICInit(TIM4, &TIM_ICInitStructure2);
075. 
076.    /* Ensure that Channel two is set up as a slave, and that it resets the counters on a falling edge */
077.    TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1);
078.    TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);
079.    TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);
080. 
081.    /* Enable the interrupt that gets fired when the timer counter hits the period */
082.    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
083. 
084.    /* Enable the Timer interrupts */
085.    NVIC_InitTypeDef nvicStructure;
086.    nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
087.    nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
088.    nvicStructure.NVIC_IRQChannelSubPriority = 1;
089.    nvicStructure.NVIC_IRQChannelCmd = ENABLE;
090.    NVIC_Init(&nvicStructure);
091.}
092. 
093. 
094.void TIM4_IRQHandler()
095.{
096.    /* makes sure the interrupt status is not reset (and therefore SET?) */
097.    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) {
098.        /* ensure that the interrupt doesn't get triggered again */
099.        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
100. 
101.        uint32_t IC1Value = TIM_GetCapture1(TIM4);
102.        uint32_t IC2Value = TIM_GetCapture2(TIM4);
103. 
104.        /* As a percentage */
105.        float updatedDutyCycle = (IC2Value * 100.0f) / IC1Value;
106. 
107.        /* HCLK is the Advanced High Speed Bus (AHB) Clock Speed, which is a
108.           factor of the System Clock (one, at the moment, hence is the same) */
109.        float updatedFrequency = (RCC_Clocks.HCLK_Frequency) / pwmInputTimer4.hclckDivisor / (IC1Value * 1000);
110. 
111.        pwmInputTimer4.ic1value = IC1Value;
112.        pwmInputTimer4.ic2value = IC2Value;
113.        pwmInputTimer4.dutyCycle = updatedDutyCycle;
114.        pwmInputTimer4.frequency = updatedFrequency;
115.    }
116.}

Outcomes