STM32F407 Problem With Input Capture on Four Channels - Page 1
Forum section: electronics / general

author: techy101
author_rank: Unknown
timestamp: October 22, 2016, 02:21:51 am
content: I’m having some trouble with four channel timers on the STM32F407. I am trying to use all four channels of Timer 2 for input capture and am not quite able to get it to work. Channels 1 and 4 work just fine, but 2 and 3 don’t seem to be doing anything at all. Looking at the block diagram it seems that there’s an option of using an XOR for channels 2 and 3 muxed with channel 1 (Circled in red in block diagram below). I did find a bit related to this in the reference manual, TIM2_CR2.TI1S, but setting it either way does not change the behavior. The reference manual makes it seem like all four channels are available for input capture, and they are each mapped to TI1..TI4 respectively in the TIM2_CCMR1 and TIM2_CCMR2 registers.I’ve attached code for both my initialization function and the interrupt handler. I’m using MikroC, so the syntax is a bit weird. The program is designed to use a structure to create six instances and pass the struct into functions, which makes the handler a bit messy. There is more going on in the handler than I’d like, but for now I don’t know a better way to deal with the delta calculations, and that’s another topic anyway.Any help would be appreciated. It took a while to get these working and I simply don’t understand why 2 and 3 wouldn’t work when 1 and 4 do. My best guess is that it has something to do with the XOR and mux between channel 1, 2, and 3.InitializationCode:// Initialize Input Capture on Timer 2 Channel 1void init_input_capture() {// Configure timer 2RCC_APB1ENR.TIM2EN = 1; // Enable clock gating for timer module 2TIM2_CR1.CEN = 0; // Disable timer/counterTIM2_CR2.TI1S = 0; // TIM2_CH1 connected to TI1 Input (1 would be Ch1, 2, 3 XOR to TI1)TIM2_PSC = ENCODER_TIM_PSC; // Set timer 2 prescalerTIM2_ARR = ENCODER_TIM_RELOAD; // Set timer 2 Auto Reload valueTIM2_CR1 |= 0x10; // Set counter direction as upcounting (DIR bit)// Configure motor 1 (Pin A0, Channel 1) input captureGPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM2_CH1_PA0); // Configure alternate function for A0 as Timer 2 Channel 1TIM2_CCMR1_Input |= 0x01; // Set capture channel 1 as input on TI1 (CC1S = 01)TIM2_CCER.CC1P = 0; // Set capture on rising edge eventTIM2_CCER.CC1NP = 0;TIM2_CCER.CC1E = 1; // Enable capture on channel 1TIM2_DIER.CC1IE = 1; // Enable interrupt on capture channel 1// Configure motor 2 (Pin A1, Channel 2) input captureGPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM2_CH2_PA1); // Configure alternate function for pin A1 as Timer 2 Channel 2TIM2_CCMR1_Input |= 0x100; // Set capture channel 2 as input on TI2 (CC2S = 01)TIM2_CCER.CC2P = 0; // Set capture on rising edge eventTIM2_CCER.CC2NP = 0;TIM2_CCER.CC2E = 1; // Enable capture on channel 2TIM2_DIER.CC2IE = 1; // Enable interrupt on capture channel 2// Configure motor 3 (Pin A2, Channel 3) input captureGPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM2_CH3_PA2); // Configure alternate function for pin A2 as Timer 2 Channel 3TIM2_CCMR2_Input |= 0x01; // Set capture channel 3 as input on TI3 (CC3S = 01)TIM2_CCER.CC3P = 0; // Set capture on rising edge eventTIM2_CCER.CC3NP = 0;TIM2_CCER.CC3E = 1; // Enable capture on channel 2TIM2_DIER.CC3IE = 1; // Enable interrupt on capture channel 3// Configure motor 4 (Pin A3, Channel 4) input captureGPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM2_CH4_PA3); // Configure alternate function for pin A3 as Timer 2 Channel 4TIM2_CCMR2_Input |= 0x100; // Set capture channel 4 as input on TI4 (CC4S = 01)TIM2_CCER.CC3P = 0; // Set capture on rising edge eventTIM2_CCER.CC3NP = 0;TIM2_CCER.CC4E = 1; // Enable capture on channel 2TIM2_DIER.CC4IE = 1; // Enable interrupt on capture channel 4// Configure timer interruptsTIM2_DIER.UIE = 1; // Enable overflow interruptNVIC_IntEnable(IVT_INT_TIM2); // Enable timer 2 interruptTIM2_CR1.CEN = 1; // Enable timer/counter// Calculate period of TIM2_CLK in mstimer2_period_ms = (long double) 1000.0 / (MCU_FREQUENCY / (ENCODER_TIM_PSC + 1));}Interrupt handlerCode:// Interrupt handler for Timer 2 (Overflows and Capture events)void timer2_ISR() iv IVT_INT_TIM2 {// Timer 2 Overflowif(TIM2_SR.UIF == 1) {TIM2_SR.UIF = 0; // Clear timer 2 interrupt bitoverflow_count++; // Increment overflow counter}// Channel 1 (Motor 1) input capture eventif (TIM2_SR.CC1IF == 1) {fngr_pointer.enc_start_time = fngr_pointer.enc_end_time; // Store previous captured value for next calculationfngr_pointer.enc_end_time = TIM2_CCR1; // Read stored input capture timefngr_pointer.enc_overflow_start = fngr_pointer.enc_overflow_end; // Store previous overflow value for next calculationfngr_pointer.enc_overflow_end = overflow_count; // Store number of timer 2 overflows for this motorfngr_pointer.enc_chan_b = FNGR_POINTER_ENC_B; // Sample the second encoder channel state (For direction)fngr_pointer.position_temp++; // Increment total input capture event counter}// Channel 2 (Motor 2) input capture eventif (TIM2_SR.CC2IF == 1) {fngr_middle.enc_start_time = fngr_middle.enc_end_time; // Store previous captured value for next calculationfngr_middle.enc_end_time = TIM2_CCR2; // Read stored input capture timefngr_middle.enc_overflow_start = fngr_middle.enc_overflow_end; // Store previous overflow value for next calculationfngr_middle.enc_overflow_end = overflow_count; // Store number of timer 2 overflows for this motorfngr_pointer.enc_chan_b = FNGR_MIDDLE_ENC_B; // Sample the second encoder channel state (For direction)fngr_middle.position_actual++; // Increment total input capture event counter}// Channel 3 (Motor 3) input capture eventif (TIM2_SR.CC3IF == 1) {fngr_ring.enc_start_time = fngr_ring.enc_end_time; // Store previous captured value for next calculationfngr_ring.enc_end_time = TIM2_CCR3; // Read stored input capture timefngr_ring.enc_overflow_start = fngr_ring.enc_overflow_end; // Store previous overflow value for next calculationfngr_ring.enc_overflow_end = overflow_count; // Store number of timer 2 overflows for this motorfngr_pointer.enc_chan_b = FNGR_RING_ENC_B; // Sample the second encoder channel state (For direction)fngr_ring.position_actual++; // Increment total input capture event counter}// Channel 4 (Motor 4) input capture eventif (TIM2_SR.CC4IF == 1) {fngr_pinky.enc_start_time = fngr_pinky.enc_end_time; // Store previous captured value for next calculationfngr_pinky.enc_end_time = TIM2_CCR4; // Read stored input capture timefngr_pinky.enc_overflow_start = fngr_pinky.enc_overflow_end; // Store previous overflow value for next calculationfngr_pinky.enc_overflow_end = overflow_count; // Store number of timer 2 overflows for this motorfngr_pointer.enc_chan_b = FNGR_PINKY_ENC_B; // Sample the second encoder channel state (For direction)fngr_pinky.position_actual++; // Increment total input capture event counter}}

author: newbrain
author_rank: Super Contributor
timestamp: October 22, 2016, 12:24:33 pm
content: Just some minor things:You got the direction wrong. 0 is upcount and 1 is downcount. But this is immaterial, as this bit is ignored in this configuration.You are relying on the fact that all the registers hold their reset value when you call the init function. This depends on the rest of your code, so it might be a safe assumption.In setting up CCR4, you are setting again CC3P and CC3NP bits. But see the previous comment.The setting of TI1S is correct.But, apart from the above, I can't see anything obvious.Have you tried some basic debugging such as breakpoint in the relevant ISR section, swapping the physical inputs, mapping CCR2 and 3 inputs respectively to TI1 and TI4?Maybe your code is too well behaved to give you the middle finger...

author: techy101
author_rank: Unknown
timestamp: October 23, 2016, 04:32:37 pm
content: Thank you for catching the couple of silly errorsI've gone through with a debugger and verified that the registers I'm trying to set are in fact set correctly. I can also verify that the ISR's for channels 2 and 3 are not being entered as it's coded right now. However I was able to map CC2 to TI1 and it fired right up, so there's something going on with TI2 and TI3. Still stumped thoughOh, and that joke was absolutely terrible and amazing at the same time. It made my wife groan

author: newbrain
author_rank: Super Contributor
timestamp: October 23, 2016, 06:49:00 pm
content: Quote from: techy101 on October 23, 2016, 04:32:37 pmVery strange.I'd make sure of all the HW connections, but you've probably already done that.Next step would be checking the code for those MikroC library calls (is that even available?), or, better yet, set up the GPIO pins yourself with registers.QuoteI'll take that as a compliment. My wife has lost all hopes since longtime. The offspring still complains, though...

author: techy101
author_rank: Unknown
timestamp: October 24, 2016, 04:17:11 pm
content: I've been able to verify the CCMR1, CCMR2, CCER, and DIER registers are configured as expected by looking at the register descriptions in the reference manual. I've also been able to verify that the TIM2_SR CC2IF and CC3IF bits aren't being set when an event happens.Here is a capture of the relevant registers from the debugger.As far as I can tell all channels are configured identically. The only thing that's giving me pause is the register map in the datasheet shows PA0 AF1 listed as "TIM2_CH1_ETR" not "TIM2_CH1" like the other channels. I don't really understand the external trigger in relation to an input capture, but I thought it wasn't used. I'm not sure if this plays any role or not.Thank you for the help.

author: newbrain
author_rank: Super Contributor
timestamp: October 24, 2016, 07:31:28 pm
content: Quote from: techy101 on October 24, 2016, 04:17:11 pmThe PA0 pin acts as both input capture and ETR for Timer 2, depending on the configuration (other timers have separate pins), so no, it does not matter.Unfortunately, the image is not showing; did it include also GPIO registers?Since the capture triggers when connected to TI1, it could be a GPIO misconfiguration, or an HW issue.Can you use any of the other TI2 and TI3 pins, resp. PB3 and PB10, or check that the used pins are actually working (e.g. using an EXTI interrupt to capture the edge, though it might not be definitive proof - pins circuitry can also be "half-cooked")?BTW: I also checked the errata for the F407, no mention of timer input capture problems.

author: newbrain
author_rank: Super Contributor
timestamp: October 25, 2016, 10:32:37 pm
content: So,in the end I wrote some code to test all the Timer 2 input capture registers, with the relevant interrupt.I jumpered PA0-PA3 to PD12-PD15 (the LEDs on the STM32F407 Discovery) and toggled them every 100ms, either in a Gray Code pattern to have at most a single IRQ pending/active, or downcounting 15-0 to have a nice 0000->1111 transition.The code is attached below, just cobbled up together this evening (no style prize), one needs the F4 Cube (HAL+CMSIS) to compile it and "-std=c99".I did not add UART or SWO output, lazy me, so I ended up using a debugger (eh, some say it's useless).The TimerInit() and the ISR (TIM2_IRQHandler) are strictly modeled on the ones included in the OP (but I used CMSIS register and bit names).This 0000->1111 event is shown in the screenshot.Again check your hardware, and whether the pins are OK!

author: dannyf
author_rank: Super Contributor
timestamp: October 26, 2016, 11:28:26 am
content: You nay want to check your pin remapping.As to the code itself, I posted a piece of code that does direct register access on timer 1 (tested also on 2 through 5) for compare match on f1. Input capture should be similar.