Showing results for 
Search instead for 
Did you mean: 

STM32L0 how to calibrate HSI using LSE

Federico Calia
Associate II
Posted on March 30, 2018 at 10:24

Hi! I have an STM32L0 nucleo board and I want to calibrate HSI using LSE; I have read the application note: AN4631 that explain a solution: at page 12 is possible to see the Hardware connection with TIM21 CH1 in capture/compare configuration;

But... I don't knew how to translate this info into CUBE-MX configurator for a correct result 


Could you please tell me how to set it? please help me, thank you very very much.

Bye bye

Posted on March 30, 2018 at 13:34

I don't use CubeMX so I don't know how to click it in there, but it's only about setting the TIM21_OR register.


Looking into Cube's sources,  this should be the incantation to use:



Federico Calia
Associate II
Posted on March 30, 2018 at 17:22

Thank you very much

Bye bye

Federico Calia
Associate II
Posted on March 30, 2018 at 22:43

Well, I was able to associate the LSE clock source to channel 1 of the timer,


It was enabled by the HAL function:


and I have verified that it works fine.

Now: I associated to ''Trigger Event Selection'' : Update Event.

And for each tick of the LSE I get an interrupt, managed in the routine:


Here all the possible configurations:


And I don't knew what is the best solution to obtainting a correct HSI calibration from the LSE.

In my mind is sufficient to get a fixed number of ticks from one and from the other oscillator,

than is necessary to compare expected values with obtained values and is possible to obtain the error.

For example: I get 32768 ticks from my LSE crystal and 4000000 ticks from my sys clock (4Mhz) in one second.

If at the 32768th tick I have the HSI counter only with 39999990 ticks, in one second there was an

error of 4000000-39999990 = 10 ticks on the HSI RC clock and I have to update the HSI16CAL register.

But I don't knew if this solution is good and fine.

What do you think about that? are there better strategies to get the result?  I thank you very much


Posted on March 30, 2018 at 23:31

You don't need that second step, setting TRGO  - that would be used if you'd want to link internally some other timer to TIM21.

The method is roughly correct - in the interrupt, you count up to 32768 and then read out the capture register (TIM21_CCR1) to find out the number of HSI ticks. This all is assuming there is no PLL used and there is no divider on the APB buses.

As there will be some setup time, it's better to read out the CCR1 in the first and then in the 32769th interrupt and subtract them.

Note that the dependency of HSI16 frequency on the HSI16CAL is nonlinear, 'jumping' down by typ. 1.5% every 16 values; and even between the 'jumps' the 'tuning step' of HSI16 is rather large, typ. 0.4%, so out of 4000000 it's more 16000 than 10.


Senior II
Posted on March 31, 2018 at 00:10


I don't knew how to translate this info into CUBE-MX configurator for a correct result '

forget about Cube for now. here is the gist of the task: you have two clocks, one presumed to be accurate and another not. how do you use the accurate clock to measure the not-so-accurate clock?

1) set up the accurate clock to increment counter 1, and the inaccurate to increment counter 2; zero both counters

2) run counter 1 for a known period of time (= known number of cycles), while counter 2 is incrementing.

3) at the end of the run of counter 1, read the count in counter 2.

there should be a mathematical relationship between the two frequencies and the two counts. that forms the basis of your calibration.

now, all you need is to implement it in your favorite CubeMX, or any other mcu you may encounter in the future, ST or not.

Federico Calia
Associate II
Posted on March 31, 2018 at 00:30

Thank you very much!

Now I knew what to do, and how to do it...

I hope (:

I'll try tomorrow, bye bye

Federico Calia
Associate II
Posted on March 31, 2018 at 18:48

I have created a new timer based on internal clock (4Mhz), and I set

 the following configuration parameters:

htim2.Init.Prescaler = 399;

htim2.Init.Period = 1;

htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

And everytime the timer connected to LSE reaches one second (32768 pulses) I read the other timer and then I set it to '0'.

According to the settings I have: 1/4'000'000 = 0.00000025 as clock pulse period, and then an interrupt every (0.00000025*400 = 0.0001) 0.0001 seconds... And

 I expect to read in one second: 10000 pulses. Is It correct? 

Because This is not my real result; the number of interrupts connected to my counter are, for every second, a number from 6784 to 6865... And I don't understand why


 is this a possible error introduced by debug?

What do you think is wrong?

Thank you very much

Posted on March 31, 2018 at 21:59


What do you think is wrong?'

no code, no tell.

conceptually, at a 1x prescaler, your timer connected to the 4Mhz oscillator would have accumulated 4M ticks. Assuming that it is a 16-bit timer, it would have overflown 4M/64K = 61x, and has a residual reading of 2304.

So that's what you should expect. I would increment a MSB counter in the ISR and read it + the counter value in the other timer's ISR and then reset them. the combination of the two should yield a number that's very close to 4M if both clocks are accurate.

Posted on April 01, 2018 at 10:36

What do you think is wrong?

As dhenry said, without code it's just a guess, but let me guess: several things.


htim2.Init.Prescaler = 399;

htim2.Init.Period = 1;

means that that timer will overflow once in 800 cycles not once in 400 as you've assumed. But that should result in a number close to 5000.

I also guess that you've counting the LSE pulses by interrupting at every pulse, that's 32768 interrupts per second, and at system clock 4MHz it's around 122 cycles per interrupt. As you appear to be using Cube/HAL, where the 'abstraction' in interrupt processing involves quite a lot of overhead, so I can imagine that that ISR's duration is close to those 122 cycles (especially if compiler optimizations are switched off). In other words, if the 2nd timer's interrupt occurs, it may well be you are missing one of the TIM21 interrupts, which in turn means that it takes more than 32768 LSE ticks - i.e. more than one second - until you count up to 32768. That would explain why you see a number higher than 5000.

My idea was different: Let TIM21 count the system clocks, and use the LSE input into  CH1 as an input capture (i.e. at a LSE pulse the current value of TIM21_CNT is copied by hardware into TIM21_CCR1). By subtracting the captured values in capture interrupts 32768 apart you'll get the number of system ticks. And switch on compiler optimizations if you insist on using Cube/HAL to avoid capture misses. You can find out such miss checking the overcapture flag.

You still can use the prescaler to make the result fit into 16 bits, but that also results in lower resolution of the measured value. Given the relatively rough trimming step this may or may not be adequate.