Skip to main content
Gergo Santha
Associate II
Apr 10, 2018 12:55 PM UTC
Solved

TIM3 CH2 external counter configuration

Posted on April 10, 2018 at 14:55

Hi!

I want to use an external clock signal for timing. This clock signal is ~130 kHz. I want to count N pulses with a timer and I want an IRQ after N pulses. For this purpose I'd like to use TIM3 in external counter mode 1. The input pin is PC7 which is TIM3_CH2 in alternate function 2. I'm using the LL driver. So far I came up with this but it's not working no matter what value I write in to the ARR register. This code is based on several examples in CUBE_FW_L4 and other web sources. I know that there are lots of code out there but it seems that people are using different drivers like standard, HAL etc. I call timing_init_timing() function to configure the pins, timer etc. The function timing_set_ARR is used from a command terminal and I get zero calling timing_get_counter() function and the timcnt readout is always 1.

I already had my laps on reference manual but I really not get the concept as the driver is using confusing names which I couldn't found in the RM of the MCU. I'm missing some points probably so any help would be great.

void

timing_configure_input_pin

(

void

)

{

 

// Clock

  LL_AHB2_GRP1_EnableClock

(

LL_AHB2_GRP1_PERIPH_GPIOC

)

;

 

// TIM3 configuration on GPIO PC7, alternate function 2

  LL_GPIO_SetPinMode

(

GPIOC

,

LL_GPIO_PIN_7

,

LL_GPIO_MODE_ALTERNATE

)

;

  LL_GPIO_SetPinPull

(

GPIOC

,

LL_GPIO_PIN_7

,

LL_GPIO_PULL_DOWN

)

;

  LL_GPIO_SetPinSpeed

(

GPIOC

,

LL_GPIO_PIN_7

,

LL_GPIO_SPEED_FREQ_HIGH

)

;

  LL_GPIO_SetAFPin_0_7

(

GPIOC

,

LL_GPIO_PIN_7

,

LL_GPIO_AF_2

)

;

}

void

timing_configure_nvic

(

void

)

{

  NVIC_SetPriority

(

TIM3_IRQn

,

0

)

;

  NVIC_EnableIRQ

(

TIM3_IRQn

)

;

}

void

timing_configure_timer

(

void

)

{

 

// Clock

  LL_APB1_GRP1_EnableClock

(

LL_APB1_GRP1_PERIPH_TIM3

)

;

  LL_TIM_IC_SetFilter

(

TIM3

,

LL_TIM_CHANNEL_CH2

,

LL_TIM_IC_FILTER_FDIV1

)

;

  LL_TIM_IC_SetPrescaler

(

TIM3

,

LL_TIM_CHANNEL_CH2

,

LL_TIM_ICPSC_DIV8

)

;

  LL_TIM_IC_SetPolarity

(

TIM3

,

LL_TIM_CHANNEL_CH2

,

LL_TIM_IC_POLARITY_RISING

)

;

  LL_TIM_IC_SetActiveInput

(

TIM3

,

LL_TIM_CHANNEL_CH2

,

LL_TIM_ACTIVEINPUT_DIRECTTI

)

;

  LL_TIM_SetClockSource

(

TIM3

,

LL_TIM_CLOCKSOURCE_EXT_MODE1

)

;

  LL_TIM_CC_EnableChannel

(

TIM3

,

LL_TIM_CHANNEL_CH2

)

;

  LL_TIM_EnableIT_UPDATE

(

TIM3

)

;

  LL_TIM_GenerateEvent_UPDATE

(

TIM3

)

;

}

void

timing_set_ARR

(

uint16_t

val

)

{

  LL_TIM_SetAutoReload

(

TIM3

,

val

)

;

 

}

uint32_t

timing_get_counter

(

void

)

{

 

return

LL_TIM_GetCounter

(

TIM3

)

;

}

void

timing_init_timing

(

void

)

{

  timing_configure_input_pin

(

)

;

  timing_configure_nvic

(

)

;

  timing_configure_timer

(

)

;

}

void

TIM3_IRQHandler

(

void

)

{

 

if

(

LL_TIM_IsActiveFlag_UPDATE

(

TIM3

)

==

1

)

{

   

// Clear the update interrupt flag

    LL_TIM_ClearFlag_UPDATE

(

TIM3

)

;

    board_toggle_NUCLEO_LED

(

)

;

    timcnt

++;

 

}

}

    This topic has been closed for replies.
    Gergo Santha
    Gergo SanthaBest answer
    Associate II
    Apr 11, 2018 10:18 AM UTC
    Posted on April 11, 2018 at 12:18

    Okay, I converted a code based on Std Periph library to direct register read/write and it is working now. I paste the code here. The input signal is ~130209 Hz connected to PC7 of STM32L476RG from SPIRIT1 XO, 25e6 MHz/192.

    void timing_configure_input_pin(void) {

        /* Enable the peripheral clock of GPIOs */

        LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);

      /* GPIO TIM3_CH2 configuration */

      LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_7, LL_GPIO_MODE_ALTERNATE);

      LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_7, LL_GPIO_PULL_DOWN);

      LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_7, LL_GPIO_SPEED_FREQ_HIGH);

      LL_GPIO_SetAFPin_0_7(GPIOC, LL_GPIO_PIN_7, LL_GPIO_AF_2);

    }

    void timing_configure_nvic(void) {

      NVIC_SetPriority(SPIRIT1_XO_OUT_TIMER_CC_IRQn, 0);

      NVIC_EnableIRQ(SPIRIT1_XO_OUT_TIMER_CC_IRQn);

    }

    void timing_ti2_config(void) {

      // Temporary variables

      uint16_t tmpccmr1 = 0, tmpccer = 0, tmp = 0;

      /* Disable the Channel 2: Reset the CC2E Bit */

      TIM3->CCER &= (uint16_t)~TIM_CCER_CC2E;

      tmpccmr1 = TIM3->CCMR1;

      tmpccer = TIM3->CCER;

      tmp = (uint16_t)(0x00 << 4); // TIM_ICPolarity_Rising = ((uint16_t)0x0000)

      /* Select the Input and set the filter */

      tmpccmr1 &= ((uint16_t)~TIM_CCMR1_CC2S) & ((uint16_t)~TIM_CCMR1_IC2F);

      tmpccmr1 |= (uint16_t)(0x00 << 12); // TIM_ICFilter:  must be a value between 0x00 and 0x0F.

      tmpccmr1 |= (uint16_t)(0x0002 << 8); //CC2S[1:0] 10: CC2 channel is configured as input, IC2 is mapped on TI1

      /* Select the Polarity and set the CC2E Bit */

      tmpccer &= (uint16_t)~(TIM_CCER_CC2P | TIM_CCER_CC2NP);

      tmpccer |=  (uint16_t)(tmp | (uint16_t)TIM_CCER_CC2E);

      /* Write to TIMx CCMR1 and CCER registers */

      TIM3->CCMR1 = tmpccmr1 ;

      TIM3->CCER = tmpccer;

    }

    void timing_select_input_trigger(void) {

      uint16_t tmpsmcr = 0;

      /* Get the TIM3 SMCR register value */

      tmpsmcr = TIM3->SMCR;

      /* Reset the TS Bits */

      tmpsmcr &= (uint16_t)~TIM_SMCR_TS;

      /* Set the Input Trigger source */

      tmpsmcr |= (uint16_t)0x0060; // TS[6:4] 110: Filtered Timer Input 2 (TI2FP2)

      /* Write to TIMx SMCR */

      TIM3->SMCR = tmpsmcr;

    }

    void timing_configure_timer(void) {

      SPIRIT1_XO_OUT_TIMER_CC_CLK_ENABLE();

      // CMS = b00 (counter counts up/down depending on DIR bit

      // DIR = b0 counter upcounter

      // CKD = b00

      TIM3->CR1 &=~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD);

      // Set ARR value (period)

      TIM3->ARR = 1;

      // Set prescaler

      TIM3->PSC = 1301; // The counter clock frequency CK_CNT is equal to f CK_PSC / (PSC[15:0] + 1)

      timing_ti2_config();

      timing_select_input_trigger();

      TIM3->SMCR |= (uint16_t)0x0007; // TIM_SlaveMode_External1 = ((uint16_t)0x0007)

      TIM3->DIER |=TIM_DIER_UIE;

      TIM3->CR1 |= TIM_CR1_CEN;

    }

    void timing_init_timing(void) {

        timing_configure_input_pin();

        timing_configure_nvic();

        timing_configure_timer();    

    }

    void SPIRIT1_XO_OUT_TIMER_IRQ_HANDLER(void)

    {

      if(LL_TIM_IsActiveFlag_UPDATE(SPIRIT1_XO_OUT_TIMER) == 1)

      {

        /* Clear the update interrupt flag*/

        LL_TIM_ClearFlag_UPDATE(SPIRIT1_XO_OUT_TIMER);

        board_toggle_NUCLEO_LED();

        timcnt++;

      }

    }

    5 replies

    henry.dick
    Associate II
    Apr 10, 2018 1:35 PM UTC
    Posted on April 10, 2018 at 15:35

    A few ways of doing it.

    1. For a count up counter, load the counter with -N and use the overflow interrupt.

    2. For count down counter, load the counter with initial value of N and overflow interrupt.

    3. Set the compare match at N and use the cc interrupt.

    4. Use a Pressler of 129, and load the counter with -1.

    ....

    The possibility is simply endless.

    Gergo Santha
    Associate II
    Apr 10, 2018 3:42 PM UTC
    Posted on April 10, 2018 at 15:42

    dhenry,

    thank you for your help. At the moment I'm not getting much change in my timcnt variable (and I read out zero when I call LL_TIM_GetCounter function), so I've a feeling that the signal is not routed into the timer correctly. I'm not sure that my code is correct either as I couldn't find a code example with the LL driver.

    henry.dick
    Associate II
    Apr 11, 2018 1:07 AM UTC
    Posted on April 11, 2018 at 01:07

    I'm with JW on this. If you are not comfortable with LL, don't use it until you do get comfortable.

    waclawek.jan
    Super User
    Apr 10, 2018 9:35 PM UTC
    Posted on April 10, 2018 at 23:35

    Does it count at all?

    Read back and check/post the GPIO and TIM registers content.

    I already had my laps on reference manual but I really not get the concept as the driver is using confusing names which I couldn't found in the RM of the MCU.

    Then why do you use the 'driver' at all?

    JW

    Gergo Santha
    Gergo SanthaAuthorBest answer
    Associate II
    Apr 11, 2018 10:18 AM UTC
    Posted on April 11, 2018 at 12:18

    Okay, I converted a code based on Std Periph library to direct register read/write and it is working now. I paste the code here. The input signal is ~130209 Hz connected to PC7 of STM32L476RG from SPIRIT1 XO, 25e6 MHz/192.

    void timing_configure_input_pin(void) {

        /* Enable the peripheral clock of GPIOs */

        LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);

      /* GPIO TIM3_CH2 configuration */

      LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_7, LL_GPIO_MODE_ALTERNATE);

      LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_7, LL_GPIO_PULL_DOWN);

      LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_7, LL_GPIO_SPEED_FREQ_HIGH);

      LL_GPIO_SetAFPin_0_7(GPIOC, LL_GPIO_PIN_7, LL_GPIO_AF_2);

    }

    void timing_configure_nvic(void) {

      NVIC_SetPriority(SPIRIT1_XO_OUT_TIMER_CC_IRQn, 0);

      NVIC_EnableIRQ(SPIRIT1_XO_OUT_TIMER_CC_IRQn);

    }

    void timing_ti2_config(void) {

      // Temporary variables

      uint16_t tmpccmr1 = 0, tmpccer = 0, tmp = 0;

      /* Disable the Channel 2: Reset the CC2E Bit */

      TIM3->CCER &= (uint16_t)~TIM_CCER_CC2E;

      tmpccmr1 = TIM3->CCMR1;

      tmpccer = TIM3->CCER;

      tmp = (uint16_t)(0x00 << 4); // TIM_ICPolarity_Rising = ((uint16_t)0x0000)

      /* Select the Input and set the filter */

      tmpccmr1 &= ((uint16_t)~TIM_CCMR1_CC2S) & ((uint16_t)~TIM_CCMR1_IC2F);

      tmpccmr1 |= (uint16_t)(0x00 << 12); // TIM_ICFilter:  must be a value between 0x00 and 0x0F.

      tmpccmr1 |= (uint16_t)(0x0002 << 8); //CC2S[1:0] 10: CC2 channel is configured as input, IC2 is mapped on TI1

      /* Select the Polarity and set the CC2E Bit */

      tmpccer &= (uint16_t)~(TIM_CCER_CC2P | TIM_CCER_CC2NP);

      tmpccer |=  (uint16_t)(tmp | (uint16_t)TIM_CCER_CC2E);

      /* Write to TIMx CCMR1 and CCER registers */

      TIM3->CCMR1 = tmpccmr1 ;

      TIM3->CCER = tmpccer;

    }

    void timing_select_input_trigger(void) {

      uint16_t tmpsmcr = 0;

      /* Get the TIM3 SMCR register value */

      tmpsmcr = TIM3->SMCR;

      /* Reset the TS Bits */

      tmpsmcr &= (uint16_t)~TIM_SMCR_TS;

      /* Set the Input Trigger source */

      tmpsmcr |= (uint16_t)0x0060; // TS[6:4] 110: Filtered Timer Input 2 (TI2FP2)

      /* Write to TIMx SMCR */

      TIM3->SMCR = tmpsmcr;

    }

    void timing_configure_timer(void) {

      SPIRIT1_XO_OUT_TIMER_CC_CLK_ENABLE();

      // CMS = b00 (counter counts up/down depending on DIR bit

      // DIR = b0 counter upcounter

      // CKD = b00

      TIM3->CR1 &=~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD);

      // Set ARR value (period)

      TIM3->ARR = 1;

      // Set prescaler

      TIM3->PSC = 1301; // The counter clock frequency CK_CNT is equal to f CK_PSC / (PSC[15:0] + 1)

      timing_ti2_config();

      timing_select_input_trigger();

      TIM3->SMCR |= (uint16_t)0x0007; // TIM_SlaveMode_External1 = ((uint16_t)0x0007)

      TIM3->DIER |=TIM_DIER_UIE;

      TIM3->CR1 |= TIM_CR1_CEN;

    }

    void timing_init_timing(void) {

        timing_configure_input_pin();

        timing_configure_nvic();

        timing_configure_timer();    

    }

    void SPIRIT1_XO_OUT_TIMER_IRQ_HANDLER(void)

    {

      if(LL_TIM_IsActiveFlag_UPDATE(SPIRIT1_XO_OUT_TIMER) == 1)

      {

        /* Clear the update interrupt flag*/

        LL_TIM_ClearFlag_UPDATE(SPIRIT1_XO_OUT_TIMER);

        board_toggle_NUCLEO_LED();

        timcnt++;

      }

    }