Skip to main content
Pilous Droip
Senior
September 28, 2021
Question

Chain two timers

  • September 28, 2021
  • 5 replies
  • 1777 views

hi friends,

I try chain of 2 timers on STM32L053. From 2 16 bit timers I would like to create one 32bit timer.

My init is here.

void CLOCK__TIM21_Init(uint16_t Prescaler) {
 
 //initialize tim21 as master
 //enable clock to tim21
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21);
 
 //stop the timer to configure it
 LL_TIM_DisableCounter(TIM21); //clear cen. 0=disable the timer, 1=enable the timer
 LL_TIM_SetClockDivision(TIM21, LL_TIM_CLOCKDIVISION_DIV1); //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved
 LL_TIM_SetCounterMode(TIM21, LL_TIM_COUNTERMODE_UP); //clear DIR bit. 0=upcounter, 1=downcounter
 LL_TIM_SetOnePulseMode(TIM21, LL_TIM_ONEPULSEMODE_REPETITIVE); //clear opm bit. 0=periodic timer, 1=one-shot timer
 
 //or to simply zero the register
 LL_TIM_SetTriggerOutput(TIM21, LL_TIM_TRGO_UPDATE); //MMS = 0b010->tim2 as prescaler
 //source from internal clock -> disable slave mode
 LL_TIM_SetSlaveMode(TIM21, LL_TIM_SLAVEMODE_DISABLED); //clear sms->master mode and use internal clock
 
 //clear the status register bits for capture / compare flags
 LL_TIM_ClearFlag_UPDATE(TIM21);
 LL_TIM_ClearFlag_CC4(TIM21);
 LL_TIM_ClearFlag_CC3(TIM21);
 LL_TIM_ClearFlag_CC2(TIM21);
 LL_TIM_ClearFlag_CC1(TIM21);
 //disable the interrupt by clearing the enable bits
 LL_TIM_DisableIT_CC1(TIM21);
 LL_TIM_DisableIT_CC2(TIM21);
 LL_TIM_DisableIT_CC3(TIM21);
 LL_TIM_DisableIT_CC4(TIM21);
 LL_TIM_DisableIT_UPDATE(TIM21);
 
 //set the prescaler
 LL_TIM_SetPrescaler(TIM21, Prescaler - 1); //set the prescaler to Prescaler
 //user can specify a prescaler here. otherwise use 0xffff
 LL_TIM_SetAutoReload(TIM21, 0xffff); //auto reload register / period = 0; - need to change for downcounters
 
 LL_TIM_SetCounter(TIM21, 0); //reset the counter
 
 //enable the timer.
 // go
 LL_TIM_EnableCounter(TIM21);
}
 
void CLOCK__TIM22_Init(void) {
 
 //initialize tim22 as slave
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM22);
 
 LL_TIM_DisableCounter(TIM22); //clear cen. 0=disable the timer, 1=enable the timer
 LL_TIM_SetClockDivision(TIM22, LL_TIM_CLOCKDIVISION_DIV1); //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved
 LL_TIM_SetCounterMode(TIM22, LL_TIM_COUNTERMODE_UP); //clear DIR bit. 0=upcounter, 1=downcounter
 LL_TIM_SetOnePulseMode(TIM22, LL_TIM_ONEPULSEMODE_REPETITIVE); //clear opm bit. 0=periodic timer, 1=one-shot timer
 
 //source from internal clock -> disable slave mode
 LL_TIM_SetSlaveMode(TIM22, LL_TIM_SLAVEMODE_DISABLED); //clear sms->master mode and use internal clock
 
 //clear the status register bits for capture / compare flags
 LL_TIM_ClearFlag_UPDATE(TIM22);
 LL_TIM_ClearFlag_CC4(TIM22);
 LL_TIM_ClearFlag_CC3(TIM22);
 LL_TIM_ClearFlag_CC2(TIM22);
 LL_TIM_ClearFlag_CC1(TIM22);
 //disable the interrupt by clearing the enable bits
 LL_TIM_DisableIT_CC1(TIM22);
 LL_TIM_DisableIT_CC2(TIM22);
 LL_TIM_DisableIT_CC3(TIM22);
 LL_TIM_DisableIT_CC4(TIM22);
 LL_TIM_DisableIT_UPDATE(TIM22);
 
 //set the prescaler
 LL_TIM_SetPrescaler(TIM22, 0); //set the prescaler to 1:1 - master timer acts as prescaler
 LL_TIM_SetAutoReload(TIM22, 0xffff); //auto reload register / period = 0; - need to change for downcounters
 LL_TIM_SetCounter(TIM22, 0); //reset the counter
 
 //enable the timer.
 LL_TIM_EnableCounter(TIM22); //enable the timer
 
 //source from trgo -> enable slave mode and trigger on trgo
 LL_TIM_SetTriggerInput(TIM22, LL_TIM_TS_ITR0);
 LL_TIM_SetClockSource(TIM22, LL_TIM_CLOCKSOURCE_EXT_MODE1);
}

And My init function is:

 CLOCK__TIM21_Init(32000);
 CLOCK__TIM22_Init();

And I want to use it, for read uptime my board.

uint32_t Timer_time(void) {
 uint16_t msw, lsw; //timer's high/low words
 do {
 msw = LL_TIM_GetCounter(TIM22);
 lsw = LL_TIM_GetCounter(TIM21);
 } while (msw != LL_TIM_GetCounter(TIM22)); //see if overflow has taken place
 return (msw << 16) | lsw; //return 32-bit time
}
 

For uptime I call this function:

void SerialConsole_timeUP(void) {
 print("Time : %u sec", (uint32_t )Timer_Time() / 1000u);
}

And my problem is:

  1. Start time is set about: 1384 sec
  2. increment is only in range: cca 1384 to 1441

For settings I use this document: https://www.st.com/resource/en/application_note/dm00042534-stm32-crossseries-timer-overview-stmicroelectronics.pdf

Does anybody see any problems?

This topic has been closed for replies.

5 replies

waclawek.jan
Super User
September 28, 2021

> //source from internal clock -> disable slave mode

> LL_TIM_SetSlaveMode(TIM21, LL_TIM_SLAVEMODE_DISABLED); //clear sms->master mode and use internal clock

>

> //source from internal clock -> disable slave mode

> LL_TIM_SetSlaveMode(TIM22, LL_TIM_SLAVEMODE_DISABLED); //clear sms->master mode and use internal clock

OK so how are they chained?

JW

Pilous Droip
Senior
September 28, 2021

In CLOCK__TIM22_Init I have the last two lines. Where I enable slave timer. Or not? :grinning_face:

 //source from trgo -> enable slave mode and trigger on trgo
 LL_TIM_SetTriggerInput(TIM22, LL_TIM_TS_ITR0);
 LL_TIM_SetClockSource(TIM22, LL_TIM_CLOCKSOURCE_EXT_MODE1);

waclawek.jan
Super User
September 28, 2021

I don't use Cube/LL so don't understand the code. At the end of the day, the mcu works out of the registers, so read them out and check/post.

[EDIT] Also, what exactly do you mean by "increment" in your initial post?

JW

Pilous Droip
Senior
September 28, 2021

On page 588 in Reference manual is specific table: https://www.st.com/resource/en/reference_manual/dm00095744-ultra-low-power-stm32l0x3-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf#page=588 where is shown, how is connected slave and master TIM21 and TIM22. And it was my mistake.

Here is init functions for TIM21 and TIM22 in master/mode.

/*------------------------------------------------------------------------------
 -- CLOCK__TIM21_Init
 --
 -- Spojeni Master timeru 21 a slave timeru 22 do jednoho 32bitoveho
 --
 -- Init master timer 21
 ------------------------------------------------------------------------------*/
void CLOCK__TIM21_Init(uint16_t Prescaler) {
 
 LL_TIM_InitTypeDef TIM_InitStruct = { 0 };
 
 /* Peripheral clock enable */
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21);
 
 TIM_InitStruct.Prescaler = Prescaler - LL_TIM_IC_FILTER_FDIV1_N2;
 TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
 TIM_InitStruct.Autoreload = 65535;
 TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
 LL_TIM_Init(TIM21, &TIM_InitStruct);
 LL_TIM_DisableARRPreload(TIM21);
 LL_TIM_SetClockSource(TIM21, LL_TIM_CLOCKSOURCE_INTERNAL);
 LL_TIM_SetTriggerOutput(TIM21, LL_TIM_TRGO_UPDATE);
 LL_TIM_DisableMasterSlaveMode(TIM21);
 
 LL_TIM_EnableCounter(TIM21);
}
 
/*------------------------------------------------------------------------------
 -- CLOCK__TIM22_Init
 --
 -- Spojeni Master timeru 21 a slave timeru 22 do jednoho 32bitoveho
 --
 -- Init slave timer 22
 ------------------------------------------------------------------------------*/
void CLOCK__TIM22_Init(void) {
 
 LL_TIM_InitTypeDef TIM_InitStruct = { 0 };
 
 /* Peripheral clock enable */
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM22);
 
 TIM_InitStruct.Prescaler = 0;
 TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
 TIM_InitStruct.Autoreload = 65535;
 TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
 LL_TIM_Init(TIM22, &TIM_InitStruct);
 LL_TIM_DisableARRPreload(TIM22);
 LL_TIM_SetTriggerInput(TIM22, LL_TIM_TS_ITR0);
 LL_TIM_SetClockSource(TIM22, LL_TIM_CLOCKSOURCE_EXT_MODE1);
 LL_TIM_DisableIT_TRIG(TIM22);
 LL_TIM_DisableDMAReq_TRIG(TIM22);
 LL_TIM_SetTriggerOutput(TIM22, LL_TIM_TRGO_RESET);
 LL_TIM_DisableMasterSlaveMode(TIM22);
 
 LL_TIM_EnableCounter(TIM22);
}

Here is function for reading:

uint32_t GetTickTimer(void) {
 uint16_t msw, lsw; //timer's high/low words
 do {
 msw = LL_TIM_GetCounter(TIM22);
 lsw = LL_TIM_GetCounter(TIM21);
 } while (msw != LL_TIM_GetCounter(TIM22)); //see if overflow has taken place
 return (msw << 16) | lsw; //return 32-bit time
}

Test delay function:

void WaitMsec(uint32_t msec) {
 uint32_t new;
 
 uint16_t msw, lsw; //timer's high/low words
 do {
 msw = LL_TIM_GetCounter(TIM22);
 lsw = LL_TIM_GetCounter(TIM21);
 } while (msw != LL_TIM_GetCounter(TIM22)); //see if overflow has taken place
 
 new = ((msw << 16) | lsw) + msec;
 
 while (((LL_TIM_GetCounter(TIM22) << 16) | LL_TIM_GetCounter(TIM21)) < new) {
 __NOP();
 };
}

Piranha
Principal III
September 29, 2021

Why don't you use the popular and very handy approach - global 32-bit software tick counter, which is incremented in a SysTick interrupt?

Pilous Droip
Senior
October 1, 2021

Because I wouldn't learn anything. :grinning_face: