2021-09-28 04:47 AM
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:
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?
2021-09-28 04:51 AM
> //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
2021-09-28 05:07 AM
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);
2021-09-28 06:22 AM
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
2021-09-28 08:18 AM
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();
};
}
2021-09-29 09:00 AM
Why don't you use the popular and very handy approach - global 32-bit software tick counter, which is incremented in a SysTick interrupt?
2021-10-01 09:09 AM
Because I wouldn't learn anything. :grinning_face: