Skip to main content
Gergo Santha
Associate II
April 10, 2018
Solved

TIM3 CH2 external counter configuration

  • April 10, 2018
  • 3 replies
  • 2068 views
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.
    Best answer by Gergo Santha
    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++;

      }

    }

    3 replies

    henry.dick
    Associate II
    April 10, 2018
    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
    April 10, 2018
    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
    April 11, 2018
    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
    April 10, 2018
    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
    April 11, 2018
    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++;

      }

    }