cancel
Showing results for 
Search instead for 
Did you mean: 

Chain two timers

Pilous Droip
Senior

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?

6 REPLIES 6

> //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

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);

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

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
Chief II

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

Because I wouldn't learn anything. :grinning_face: