2020-04-08 05:54 PM
I've spent literally dozens of hours just trying to understand and get a timer to work. This is not due to a lack of effort.
I'm creating a setup to use TIM1 in input capture mode to capture the rising and falling edges of a pin coming from an IR receiver, using the same strategy STM shows in AN4834. Full code is below. I understand everything about the basic timer setup, and setting up the two input channels to capture both edges. All good.
I thought I fully understood the point of the slave mode configuration: The slave aspect is used so that on the specified edge (the falling edge) of the specified timer input (TI2FP2), the clock is reset. So on the falling edge of IC2, the count is captured (in CC2), and then the clock resets to 0. However...
However, what I'm seeing is that even though the falling edge and capture occurs, the clock is NOT being reset to 0. It keeps counting up until the period elapses and an update event is generated. Why?
GPIO_InitTypeDef gpio_init_struct = {0};
TIM_IC_InitTypeDef tim_ic_init = {0};
TIM_SlaveConfigTypeDef tim_slave_conf = {0};
TIM_MasterConfigTypeDef tim_master_conf = {0};
gpio_init_struct.Pin = irPin;
gpio_init_struct.Mode = GPIO_MODE_AF_OD;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
gpio_init_struct.Alternate = GPIO_AF2_TIM1;
HAL_GPIO_Init(irPort, &gpio_init_struct);
timer->Instance = TIM1;
timer->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
timer->Init.CounterMode = TIM_COUNTERMODE_UP;
timer->Init.Period = clockHz / 1000 * BEACON_TIMEOUT_MS - 1;
timer->Init.Prescaler = TIMER_PRESCALER - 1;
timer->Init.RepetitionCounter = 0;
timer->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_IC_Init(timer);
tim_ic_init.ICPolarity = TIM_ICPOLARITY_RISING;
tim_ic_init.ICSelection = TIM_ICSELECTION_DIRECTTI;
tim_ic_init.ICPrescaler = TIM_ICPSC_DIV1;
tim_ic_init.ICFilter = 0x0;
HAL_TIM_IC_ConfigChannel(timer, &tim_ic_init, TIM_CHANNEL_1);
tim_ic_init.ICPolarity = TIM_ICPOLARITY_FALLING;
tim_ic_init.ICSelection = TIM_ICSELECTION_INDIRECTTI; // same pin as Channel 1
tim_ic_init.ICPrescaler = TIM_ICPSC_DIV1;
tim_ic_init.ICFilter = 0x0;
HAL_TIM_IC_ConfigChannel(timer, &tim_ic_init, TIM_CHANNEL_2);
tim_slave_conf.SlaveMode = TIM_SLAVEMODE_RESET;
tim_slave_conf.InputTrigger = TIM_TS_TI2FP2;
tim_slave_conf.TriggerPolarity = TIM_TRIGGERPOLARITY_FALLING;
tim_slave_conf.TriggerFilter = 0;
HAL_TIM_SlaveConfigSynchro(timer, &tim_slave_conf);
tim_master_conf.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
tim_master_conf.MasterOutputTrigger = TIM_TRGO_RESET;
HAL_TIMEx_MasterConfigSynchronization(timer, &tim_master_conf);
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
__HAL_TIM_URS_ENABLE(timer);
__HAL_TIM_CLEAR_FLAG(timer, TIM_FLAG_UPDATE | TIM_FLAG_CC1 | TIM_FLAG_CC2);
__HAL_TIM_ENABLE_IT(timer, TIM_FLAG_UPDATE | TIM_IT_CC1 | TIM_IT_CC2);
__HAL_TIM_ENABLE(timer);
if (HAL_TIM_IC_Start_IT(timer, TIM_CHANNEL_1) != HAL_OK) {
while (1)
{}
}
if (HAL_TIM_IC_Start_IT(timer, TIM_CHANNEL_2) != HAL_OK) {
while (1)
{}
}
Bonus questions:
1) What purpose is the "master" side of this doing? Why does the TRGO of the master matter at all? As I understand it, the only thing the master's configuration as shown above means is that if an update event is generated, then TRGO goes high also. But what *uses* that TRGO in this arrangement?
2) Since both input channels are connected to the same pin due to the use of indirect mode on channel 2, doesn't that mean that for the slave configuration the input trigger could be either TIM_TS_TI1FP1 *or* TIM_TS_TI2FP2? Aren't they doing the same exact thing?
Solved! Go to Solution.
2020-04-09 02:25 AM
I'm breaking the register values up for easier cross-referencing with the reference manual
TIM1_CR1 0x00000005 = TIM_CR1_URS | TIM_CR1_CEN;
TIM1_SMCR 0x00000064 =
TIM_SMCR_TS_2|TIM_SMCR_TS_1| // 110: Filtered Timer Input 2 (TI2FP2)
TIM_SMCR_SMS_2; // 0100: Reset Mode
TIM1_CCMR1 0x00000201 =
TIM_CCMR1_CC2S_1 | // 10: 10: CC2 channel is configured as input, IC2 is mapped on TI1
TIM_CCMR1_CC1S_0; // 01: CC1 channel is configured as input, IC1 is mapped on TI1
TIM1_CCER 0x00000031 =
TIM_CCER_CC1E | // channel 1 capture enabled
TIM_CCER_CC2E | // channel 2 capture enabled
TIM_CCER_CC2P; // falling edge
Now the problem is clear: both channels are capturing TI1, but the trigger is coming from TI2FP2. Check the Advanced-control timer block diagram in the reference manual to see what these signal names mean. TI2FP2 is connected to the CH2 pin, not to the 2nd capture/compare unit (CC2).
Now I can as well fix it, getting rid of a couple of barely documented library calls in the process.
TIM1->PSC = prescaler - 1;
TIM1->EGR = TIM_EGR_UG;
TIM1->ARR = period - 1;
TIM1->SMCR =
TIM_SMCR_TS_2|TIM_SMCR_TS_0 | // ***** 101: Filtered Timer Input 1 (TI1FP1) *****
TIM_SMCR_SMS_2; // 0100: Reset Mode
TIM1->CCMR1 =
TIM_CCMR1_CC2S_1 | // 10: 10: CC2 channel is configured as input, IC2 is mapped on TI1
TIM_CCMR1_CC1S_0; // 01: CC1 channel is configured as input, IC1 is mapped on TI1
TIM1->CCER =
TIM_CCER_CC1E | // channel 1 capture enabled
TIM_CCER_CC2E | // channel 2 capture enabled
TIM_CCER_CC2P; // falling edge
TIM1->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_UIE; // do you really need UIE?
TIM1->CR1 = TIM_CR1_CEN;
Note that this resets the counter on the rising edge (see the description of the SMS bitfield in the reference manual). To reset on falling edge, you must connect the signal to the ETR pin as well (if it is not the same as CH1), then you can invert the polarity through TIM_SMCR_ETP.
Now you can simplify the interrupt handler as well.
void TIM1_CC_IRQHandler(void) {
uint32_t sr = TIM1->SR;
TIM1->SR = ~sr;
if(sr & TIM_SR_CC1IF) {
// capture 1 event
}
if(sr & TIM_SR_CC2IF) {
// capture 2 event
}
}
2020-04-08 07:27 PM
It's always good to include your chip number, or at least the family.
2020-04-08 08:54 PM
F303RE
2020-04-08 10:47 PM
>even though the falling edge and capture occurs,
On both channels? How do you know?
>What purpose is the "master" side of this doing?
Nothing. It's a CubeMX artefact.
> Aren't they doing the same exact thing?
They era supposed to be set to act on mutually opposite edges.
Read out and post content of TIM and relevant GPIO registers.
JW
2020-04-08 11:38 PM
> On both channels? How do you know?
Yes on both channels. The interrupt handler is called. I'm then getting the capture values from CCR1 and CCR2. Some samples over time, captured in two ring buffers, for input channel 1 and channel 2 respectively:
The relative values between channel 2 and channel 1 look correct, but as you can see, the timer count keeps increasing rather than resetting like I'm expecting due to the slave reset.
>> What purpose is the "master" side of this doing?
> Nothing. It's a CubeMX artefact.
Hmm. That would explain why it doesn't seem to actually affect anything. It's slightly strange to me that STM's own example code in that application note has it, since I would imagine it'd have been the minimal required code.
I'm not sure if there's a better way to print this, but this is the state after starting the timer:
TIM1_CR1 0x00000005
TIM1_CR2 0x00000000
TIM1_SMCR 0x00000064
TIM1_SR 0x00000000
TIM1_CCMR1 0x00000201
TIM1_CCMR2 0x00000000
TIM1_CCMR3 0x00000000
TIM1_CCER 0x00000031
TIM1_PSC 0x0000018f
TIM1_ARR 0x0000fde8
TIM1_OR 0x00000000
Thanks.
2020-04-09 01:05 AM
> TIM1_CR1 0x00000005
Try only CEN to be set, leave URS cleared.
JW
2020-04-09 02:25 AM
I'm breaking the register values up for easier cross-referencing with the reference manual
TIM1_CR1 0x00000005 = TIM_CR1_URS | TIM_CR1_CEN;
TIM1_SMCR 0x00000064 =
TIM_SMCR_TS_2|TIM_SMCR_TS_1| // 110: Filtered Timer Input 2 (TI2FP2)
TIM_SMCR_SMS_2; // 0100: Reset Mode
TIM1_CCMR1 0x00000201 =
TIM_CCMR1_CC2S_1 | // 10: 10: CC2 channel is configured as input, IC2 is mapped on TI1
TIM_CCMR1_CC1S_0; // 01: CC1 channel is configured as input, IC1 is mapped on TI1
TIM1_CCER 0x00000031 =
TIM_CCER_CC1E | // channel 1 capture enabled
TIM_CCER_CC2E | // channel 2 capture enabled
TIM_CCER_CC2P; // falling edge
Now the problem is clear: both channels are capturing TI1, but the trigger is coming from TI2FP2. Check the Advanced-control timer block diagram in the reference manual to see what these signal names mean. TI2FP2 is connected to the CH2 pin, not to the 2nd capture/compare unit (CC2).
Now I can as well fix it, getting rid of a couple of barely documented library calls in the process.
TIM1->PSC = prescaler - 1;
TIM1->EGR = TIM_EGR_UG;
TIM1->ARR = period - 1;
TIM1->SMCR =
TIM_SMCR_TS_2|TIM_SMCR_TS_0 | // ***** 101: Filtered Timer Input 1 (TI1FP1) *****
TIM_SMCR_SMS_2; // 0100: Reset Mode
TIM1->CCMR1 =
TIM_CCMR1_CC2S_1 | // 10: 10: CC2 channel is configured as input, IC2 is mapped on TI1
TIM_CCMR1_CC1S_0; // 01: CC1 channel is configured as input, IC1 is mapped on TI1
TIM1->CCER =
TIM_CCER_CC1E | // channel 1 capture enabled
TIM_CCER_CC2E | // channel 2 capture enabled
TIM_CCER_CC2P; // falling edge
TIM1->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_UIE; // do you really need UIE?
TIM1->CR1 = TIM_CR1_CEN;
Note that this resets the counter on the rising edge (see the description of the SMS bitfield in the reference manual). To reset on falling edge, you must connect the signal to the ETR pin as well (if it is not the same as CH1), then you can invert the polarity through TIM_SMCR_ETP.
Now you can simplify the interrupt handler as well.
void TIM1_CC_IRQHandler(void) {
uint32_t sr = TIM1->SR;
TIM1->SR = ~sr;
if(sr & TIM_SR_CC1IF) {
// capture 1 event
}
if(sr & TIM_SR_CC2IF) {
// capture 2 event
}
}
2020-04-09 02:30 PM
IT'S WOOORRKKKIIIIIINGGG!!!!
> Now the problem is clear: both channels are capturing TI1, but the trigger is coming from TI2FP2...
Ok, so that answers the bonus question then. They really do mean the pins, not the capture inputs. Thanks.
After merely switching to using TI1FP1, I did see that both CC1 and CC2 are capturing on the same edge, because their capture values are identical. BUT now I've figured out that's because I misunderstood what the HAL function's "tim_slave_conf.TriggerPolarity = TIM_TRIGGERPOLARITY_FALLING" value meant. I thought that was configuring which edge triggers the slave reset; It actually just ends up setting CC1's polarity again. That was misleading and the behavior is not clear to me. The HAL is both a blessing and a curse. Over time I'm sure I will experience this more often.
Once I discovered a better way to see register contents in the debugger in the SFRs tab, I could actually see that happening really easily, changed the rest of my code to handle the different edge triggering, and voila.
Thanks a ton.
I spent so long on this reading every bit of the documentation, I just was misinterpreting it. Thanks!
2020-04-10 05:01 AM
That URS was just a blind shot, I did not try it - and berendi obviosly spotted the problem.
> To reset on falling edge, you must connect the signal to the ETR pin as well
Wouldn't it be simpler just to set CH1 to falling edge and CH2 to rising?
> The HAL is both a blessing and a curse.
It's no blessing. If you talk about the "self-documenting" nature of the symbols it creates - well, then what's really needed is better documentation, not a "library".
For TIM channel inputs I often refer to this picture:
You also may want to consider the delay between the input signal and the actual reset of counter. For IR decoding this probably does not make much difference, but you may want to bee aware of it. See final posts of https://community.st.com/s/question/0D53W000003zGZBSA2/stm32f0-input-capture-interrupt-quirk.
JW