[Solved] Help with Timer InputCapture

I am trying to set up a timer for input capture, and have been pouring over the STM reference manual and examples, following the steps listed in stm32f2xx_tim.c, but still can’t get it to work.

I have stripped my example down to just trying to count input captures on a pin (A4) connected to a 1kHz source. I suspect the issue is in how I am setting the pin for input. I log values in loop() after a delay(1000). Reading the pin, I get random values as I would expect with a 1kHz input. I have verified the input with a scope.

There is a commented out TIM_GenerateEvent(TIM3, TIM_EventSource_CC1); line inside loop. If I uncomment that, I do receive capture interrupts on channel 1. I also get capture interrupts for the unconfigured channels 2-4, and for the update event, so I expect the interrupts are all configured properly.

I have gone through the capture diagrams Figure 125 & 126 of the reference manual, and everything seems to be configured properly. The only things I can think of is the input pin is not configured properly (just a normal input, correct?), or there is TIM3 alternate function remapping, but I can not figure out how to read AFIO_MAPR to rule this out.

Thanks,

-Michael

captureCount: 0 captureIncreasedCount: 0 updateCount: 62 discardCount: 183 pin: low timer value: 0xE322 TIM3_SR: 0x0 TIM3_CCMR1: 0x1 TIM3_CCER: 0x3
captureCount: 0 captureIncreasedCount: 0 updateCount: 78 discardCount: 231 pin: high timer value: 0x60EF TIM3_SR: 0x0 TIM3_CCMR1: 0x1 TIM3_CCER: 0x3
captureCount: 0 captureIncreasedCount: 0 updateCount: 93 discardCount: 276 pin: low timer value: 0xE148 TIM3_SR: 0x0 TIM3_CCMR1: 0x1 TIM3_CCER: 0x3
captureCount: 0 captureIncreasedCount: 0 updateCount: 109 discardCount: 324 pin: high timer value: 0x62D6 TIM3_SR: 0x0 TIM3_CCMR1: 0x1 TIM3_CCER: 0x3
captureCount: 0 captureIncreasedCount: 0 updateCount: 124 discardCount: 369 pin: low timer value: 0xE32F TIM3_SR: 0x0 TIM3_CCMR1: 0x1 TIM3_CCER: 0x3
captureCount: 0 captureIncreasedCount: 0 updateCount: 140 discardCount: 417 pin: low timer value: 0x63F4 TIM3_SR: 0x0 TIM3_CCMR1: 0x1 TIM3_CCER: 0x3
#define SYSCORECLOCK	60000000UL		// Timer clock tree uses core clock / 2

volatile uint16_t   updateCount = 0;
volatile uint16_t   discardCount = 0;
volatile uint16_t   captureCount = 0;
volatile uint16_t   captureIncreasedCount = 0;
volatile uint32_t   lastCapture = 0;

void TIM3_Update_Interrupt_Handler(void);

void TIM3_Capture_Interrupt_Handler(void);
void TIM3_Capture_Discard_2_Interrupt_Handler(void);
void TIM3_Capture_Discard_3_Interrupt_Handler(void);
void TIM3_Capture_Discard_4_Interrupt_Handler(void);

void setup() {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    
/*
       1. Enable TIM clock using RCC_APBxPeriphClockCmd(RCC_APBxPeriph_TIMx, ENABLE) function
*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    
/*

       2. Configure the TIM pins by configuring the corresponding GPIO pins
*/
    GPIO_InitTypeDef gpioInitStructure;
    gpioInitStructure.GPIO_Pin = GPIO_Pin_6;
    gpioInitStructure.GPIO_Mode = GPIO_Mode_IN;
    gpioInitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

    GPIO_Init(GPIOA, &gpioInitStructure);
    
/*
       2. Configure the Time base unit as described in the first part of this driver,
          if needed, else the Timer will run with the default configuration:
          - Autoreload value = 0xFFFF
          - Prescaler value = 0x0000
          - Counter mode = Up counting
          - Clock Division = TIM_CKD_DIV1
*/
    TIM_TimeBaseInitTypeDef timerInitStructure;

    TIM_TimeBaseStructInit(&timerInitStructure);
    timerInitStructure.TIM_Prescaler = (uint16_t)(SYSCORECLOCK / 1000000UL) - 1;
    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &timerInitStructure);

/*
       3. Fill the TIM_ICInitStruct with the desired parameters including:
          - TIM Channel: TIM_Channel
          - TIM Input Capture polarity: TIM_ICPolarity
          - TIM Input Capture selection: TIM_ICSelection
          - TIM Input Capture Prescaler: TIM_ICPrescaler
          - TIM Input CApture filter value: TIM_ICFilter
*/
    TIM_ICInitTypeDef inputCaptureInitStructure;
    
    TIM_ICStructInit(&inputCaptureInitStructure);
    inputCaptureInitStructure.TIM_Channel = TIM_Channel_1;
    inputCaptureInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    inputCaptureInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
    inputCaptureInitStructure.TIM_ICFilter = 0;
    
/*
       4. Call TIM_ICInit(TIMx, &TIM_ICInitStruct) to configure the desired channel with the
          corresponding configuration and to measure only frequency or duty cycle of the input signal,
          or,
          Call TIM_PWMIConfig(TIMx, &TIM_ICInitStruct) to configure the desired channels with the
          corresponding configuration and to measure the frequency and the duty cycle of the input signal
*/
    TIM_ICInit(TIM3, &inputCaptureInitStructure);

/*
       5. Enable the NVIC or the DMA to read the measured frequency.
*/
    attachSystemInterrupt(SysInterrupt_TIM3_Compare1, TIM3_Capture_Interrupt_Handler);
    attachSystemInterrupt(SysInterrupt_TIM3_Compare2, TIM3_Capture_Discard_2_Interrupt_Handler);
    attachSystemInterrupt(SysInterrupt_TIM3_Compare3, TIM3_Capture_Discard_3_Interrupt_Handler);
    attachSystemInterrupt(SysInterrupt_TIM3_Compare4, TIM3_Capture_Discard_4_Interrupt_Handler);

    attachSystemInterrupt(SysInterrupt_TIM3_Update, TIM3_Update_Interrupt_Handler);
    
    NVIC_InitTypeDef nvicStructure;

    nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
    nvicStructure.NVIC_IRQChannelPreemptionPriority = 14;
    nvicStructure.NVIC_IRQChannelSubPriority = 1;
    nvicStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvicStructure);

/*
       6. Enable the corresponding interrupt (or DMA request) to read the Captured value,
          using the function TIM_ITConfig(TIMx, TIM_IT_CCx) (or TIM_DMA_Cmd(TIMx, TIM_DMA_CCx))
*/
    TIM_ITConfig(TIM3, TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);

/*
       7. Call the TIM_Cmd(ENABLE) function to enable the TIM counter.
*/
    TIM_Cmd(TIM3, ENABLE);

/*
       8. Use TIM_GetCapturex(TIMx); to read the captured value.
*/
    
}

void loop() {
    delay(1000);
    Serial.print("captureCount: ");
    Serial.print(captureCount);
    Serial.print(" captureIncreasedCount: ");
    Serial.print(captureIncreasedCount);
    Serial.print(" updateCount: ");
    Serial.print(updateCount);
    Serial.print(" discardCount: ");
    Serial.print(discardCount);
    Serial.print(" pin: ");
    if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)) {
        Serial.print("high");
    } else {
        Serial.print("low");
    }
    Serial.print(" timer value: 0x");
    //Serial.print(TIM_GetCapture1(TIM3));
    Serial.print(TIM3->CNT, HEX);
    Serial.print(" TIM3_SR: 0x");
    Serial.print(TIM3->SR, HEX);
    Serial.print(" TIM3_CCMR1: 0x");
    Serial.print(TIM3->CCMR1, HEX);
    Serial.print(" TIM3_CCER: 0x");
    Serial.println(TIM3->CCER, HEX);

    // TIM_GenerateEvent(TIM3, TIM_EventSource_CC1);
}

void TIM3_Capture_Interrupt_Handler(void) {
    captureCount++;
    uint32_t capture = TIM_GetCapture1(TIM3);
    if ((capture - lastCapture) > 0) captureIncreasedCount++;
    lastCapture = capture;
}

void TIM3_Update_Interrupt_Handler(void) {
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
		updateCount++;
	}
}

void TIM3_Capture_Discard_2_Interrupt_Handler(void) {
    TIM_GetCapture2(TIM3);
    discardCount++;
}
void TIM3_Capture_Discard_3_Interrupt_Handler(void) {
    TIM_GetCapture3(TIM3);
    discardCount++;
}

void TIM3_Capture_Discard_4_Interrupt_Handler(void) {
    TIM_GetCapture4(TIM3);
    discardCount++;
}

Are you referring to this one?

http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/CD00225773.pdf

Do you know what TIM1_BKIN does? And how to use it as an interrupt? I read that tim1 and another one is different to all the other timers (64 bit instead of 32 I guess…). Might this suit too.?

Thanks!

on any other MCU I had to work on you need to enable the interrupt of each pin separately with a corresponding ISR… That’s something I’m searching right now. I’m fairly new to the STM32… maybe things are handled differently here?

Might be interesting too:

6.3.8 External interrupt/wakeup lines
All ports have external interrupt capability. To use external interrupt lines, the port must be configured in input mode, refer to Section 8.2: External interrupt/event controller (EXTI) and Section 8.2.3: Wakeup event management.

I do not. Each timer is tied to certain IO pins, and TIM1 was not available. Also, I started out by looking at the SparkIntervalTimer code base to figure out what files had the correct APIs. It has a list of available timers it will let you use. It was my understanding that TIM1 was already used by the firmware. I have not looked into what it is used for, and what happens if you took it over. I started out using TIM2 on the Photon before I really dug in, and “Bad Things Happened”.

That is a similar document, but not identical to the one I was looking at. There was another topic about input capture, and Particle linked to the document I am looking at (CD00171190.pdf, RM0008 vs RM0033). Your link is for the F2 parts, which I think may be correct. I’ll have to look and see if the timers are different, but I am using the library calls, so I would hope any differences are taken care of there.

I have found a few examples of using timer input capture, each seems to be using a different level of access to the hardware, but none of them enabled the pin interrupt. They all enabled the timer capture interrupt for the channel associated with the pin, and all appeared to be setting the same values I am.

Good luck with TIM1!

1 Like

Ah… I’m hacking around with the electron… That might be a thing here too… :wink:
some pin of the GSM modul is wired to one pin that has as alternate name a TIM1-name…

But I think I found what I was looking for… Now I need to find a way to set up the ISR and manipulate the specific registry … :sailboat:

Wish you good luck too!
But I’m pretty satisfied that you just need to manipulate the register with the 4 timers (might be an F for 1111 to an 8 -> 1000 for example)

Looks like I’ve got it working. The examples I had previously seen were using GPIO_Mode_IN, but I also read that I should use GPIO_Mode_AF. However whenever I used AF, I could see the output driver fighting my input on the scope. I finally found another example, and it had the magic I needed to configure the pin:

    GPIO_InitTypeDef gpioInitStructure;
    gpioInitStructure.GPIO_Pin = GPIO_Pin_6;
    gpioInitStructure.GPIO_Mode = GPIO_Mode_AF;
    gpioInitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    gpioInitStructure.GPIO_OType = GPIO_OType_PP;
    gpioInitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

    GPIO_Init(GPIOA, &gpioInitStructure);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM3);

I think previously when I tried AF, I had left off GPIO_Speed_100MHz and GPIO_OType_PP, assuming I wasn’t doing output