2018-04-10 05:55 AM
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
++;
}
}
Solved! Go to Solution.
2018-04-11 03:18 AM
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++;}
}2018-04-10 06:35 AM
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.
2018-04-10 08:42 AM
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.
2018-04-10 02:35 PM
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
2018-04-10 06:07 PM
I'm with JW on this. If you are not comfortable with LL, don't use it until you do get comfortable.
2018-04-11 03:18 AM
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++;}
}