cancel
Showing results for 
Search instead for 
Did you mean: 

struggle to use LPTIM with LSE clock, timer frequency different by factor of 10 to 32,768 kHz

JDirk.1
Senior

Hello,

I struggle to use the LSE clock as a source for timer LPTIMER1. My MC is a STM32L051R8Tx and I am using Keil MDK-Lite. ARM Compiler default 5.

For debugging purpose I am using the following settings: PSC = 1; ARR = 3275; Interrupt: Auto-reload match. In the IRQ handler I toggle a LED to measure the frequency by use of a manual stop-watch.

I cant measure the 32,768 kHz. Instead I measure the following: 

with printf() statement in loopwith debuggerLowest drive~3,276kHz
with printf() statement in loopwithout debuggerLowest drive~3,276kHz
    
without printf()with debuggerLowest drive~150Hz
without printf()without debuggerLowest drive~50Hz

 

The frequency is approx. factor 10 less and will dramatically go down when I remove the printf statement inside the while(1) loop. Can someone please give me some hints where I need to have a look on?

bool low_power_timer_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // 1: Power interface clock enabled
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN; // 1: Low-power timer clock enabled

RCC->CCIPR |= RCC_CCIPR_LPTIM1SEL_0; // 11: LSE clock selected as LP Timer cloc
RCC->CCIPR |= RCC_CCIPR_LPTIM1SEL_1; // 11: LSE clock selected as LP Timer cloc

PWR->CR |= PWR_CR_DBP; // 1: Access to RTC, RTC Backup and RCC CSR registers enabled
PWR->CR; // Dummy read, needed due to timings?

LPTIM1->CR = 0; // CFGR must only be modified when the LPTIM is disabled
LPTIM1->CFGR &= ~LPTIM_CFGR_CKSEL; // 0: LPTIM is clocked by internal clock source (APB clock or any of the embedded oscillators)
LPTIM1->CFGR &= ~LPTIM_CFGR_COUNTMODE; // 0: the counter is incremented following each internal clock pulse

RCC->CSR |= RCC_CSR_LSEON; // Enable LPTIM -> LSE (32.768 kHz) clock

// Wait until the LSE oscillator is stable
WaitForConditionToTrue(((RCC->CSR) & RCC_CSR_LSERDY) == RCC_CSR_LSERDY,
I2C_POLL_TIME, return false);

//RCC->CSR |= RCC_CSR_LSEDRV_1; // 11: Highest drive
//RCC->CSR |= RCC_CSR_LSEDRV_0;

RCC->CSR &= ~RCC_CSR_LSEDRV_1; // 00: Lowest drive
RCC->CSR &= ~RCC_CSR_LSEDRV_0;

PWR->CR &= ~PWR_CR_DBP; // Lock Access to CSR register

// If any bit in the LPTIM_IER register (Interrupt Enable Register) is set after that its
// corresponding flag in the LPTIM_ISR register (Status Register) is set, the interrupt is not
// asserted.
LPTIM1->ICR |= LPTIM_ICR_ARRMCF; // Writing 1 to this bit clears the ARRM flag in the LPTIM_ISR register
LPTIM1->IER |= LPTIM_IER_ARRMIE; // 1: ARRM interrupt enabled

//The LPTIM_CFGR register must only be modified when the LPTIM is disabled (ENABLE bit reset to ‘0’).

//LPTIM1->CFGR |= LPTIM_CFGR_PRELOAD; // Registers are updated at the end of the current LPTIM period
LPTIM1->CFGR &= ~LPTIM_CFGR_PRELOAD; // 0: Registers are updated after each APB bus write access


LPTIM1->CFGR &= ~LPTIM_CFGR_TRIGEN_0; // 00: software trigger (counting start is initiated by software)
LPTIM1->CFGR &= ~LPTIM_CFGR_TRIGEN_1; // 00: software trigger (counting start is initiated by software)
LPTIM1->CFGR &= ~LPTIM_CFGR_PRESC_0; // 000: /1
LPTIM1->CFGR &= ~LPTIM_CFGR_PRESC_1;
LPTIM1->CFGR &= ~LPTIM_CFGR_PRESC_2;

return true;
}

 

void low_power_timer_enable(void)
{
LPTIM1->CNT = 0; // Reset counter value
LPTIM1->CR |= LPTIM_CR_ENABLE; // 1:LPTIM is enabled
LPTIM1->CR |= LPTIM_CR_CNTSTRT; // Setting this bit starts the timer in Continuous mode
// This bit can be set only when the LPTIM is enabled.

LPTIM1->CMP = 0; // CMP is the compare value used by the LPTIM.
// This bit can be set only when the LPTIM is enabled.

LPTIM1->ARR = 3276-1; // This value must be strictly greater than the CMP[15:0] value
// This bit can be set only when the LPTIM is enabled.
}

main()

low_power_timer_enable();

while(1)
{

//printf("\r");

}

 

1 ACCEPTED SOLUTION

Accepted Solutions

The wording of RM may be not the perfect, and you are not the first to misunderstand it.

You have two options to use LSE:

- connect externally a 32.768kHz crystal (known sometimes also as "watch crystal"), or

- connect externally a 32.768kHz oscillator, when you set the LSEBYP bit (to avoid using the internal amplifier which would be used otherwise as part of the oscillator together with external crystal)

As TDK said above, there's no crystal built into the chip.

JW

 

View solution in original post

16 REPLIES 16
TDK
Guru

Your program doesn't do anything except start the timer. Your main loop has a single commented-out printf statement. How are you measuring its frequency?

If you feel a post has answered your question, please click "Accept as Solution".

// LPTIM1_IRQHandler
void LPTIM1_IRQHandler(void)
{
LPTIM1->ICR |= LPTIM_ICR_ARRMCF; // Writing 1 to this bit clears the ARRM flag in the LPTIM_ISR register

GPIOA->ODR ^= 0x20;
//g_interrupt_flag = 1;

} // End: void LPTIM1_IRQHandler(void)

TDK
Guru

> I toggle a LED to measure the frequency by use of a manual stop-watch.

By my calculations, your LED should blink at 5 Hz. Is that also your expectation? That's pretty fast to be timing it with a stopwatch. What are you seeing? Can we perhaps get a little more accuracy by using a logic analyzer or scope?

The addition of printf shouldn't be affecting things. To know why it's affecting things, we would need to see how printf is handled. Perhaps interrupts are being disabled during it.

 

> LPTIM1->ICR |= LPTIM_ICR_ARRMCF;

ICR is write-only. What you're doing is working okay for this example, but to only clear the ARRM flag, this is the proper way:

LPTIM1->ICR = LPTIM_ICR_ARRMCF;

 

Within LPTIM1_IRQHandler, you should check to see if ARRM is set before acting on it. What you are doing is probably working fine since it's the only interrupt and you're clearing the flag at the first statement, but note that the IRQ can be triggered without ARRM being set, particularly if it's cleared at the last statement.

If you feel a post has answered your question, please click "Accept as Solution".

> By my calculations, your LED should blink at 5 Hz

From my point of view the LED should blink at 10Hz. 1/32768Hz x 3275 = 0,1 seconds -> 1/0.1 = 10 Hz. Unfortunately, I see the LED ~ 1 second "on" and ~1 second "off" -> 0.5Hz. If I comment out the printf statement the LED is ~65 seconds "off" and afterwards ~ 65 seconds "on".

 

> but note that the IRQ can be triggered without ARRM being set

That would surprise me. In the LPTIM interrupt enable register (LPTIM_IER) I set only Bit 1 ARRM interrupt enabled. Doing so, I assume the Interrupt will only be triggered if the CNT value matches the ARR value. Anyway, I did a try and I observed the ARRM flag in the ISR register: I can confirm its set before I toggle the LED.

 

> To know why it's affecting things, we would need to see how printf is handled.

I am attaching all three files (main.c, usart.c and usart.h), it might be easier to see where the issues are.

sorry I thought about again: LED blinking at 5Hz is what I also assume but I see 0,5Hz

You use printf() in interrupt, and that probably lasts longer than is period of LPTIM.

JW

Even if I remove all printf statements from the IRQ handler, the behavior will not change.

And does the behaviour change if you use higher LSE drive settings?

What hardware is this, your own, or some "known good" such as Nucleo or Disco?

Can you verify LSE frequency in some other way, e.g. through RTC, or outputting it to MCO pin?

JW

I don’t use a DEV board such as Nucleo. The chip is used standalone, together with a few other components (ST25DV, sensors, some analog components, …) on a breadboard. I manufactured I couple of PCBs but the layout and all the components are available on the breadboard as well. My investigation described in this discussion are related to this breadboard.

If I change the driving capability of the LSE, the frequency is changing significantly.

with printf() statement in loop

without debugger

Medium low drive

1,700kHz

with printf() statement in loop

without debugger

Medium high drive

1,200kHz

with printf() statement in loop

without debugger

Highest drive

590Hz

    

with printf() statement in loop

with debugger

Lowest drive

3,276kHz

with printf() statement in loop

without debugger

Lowest drive

3,276kHz

    

with printf() statement in loop

with debugger

Lowest drive

150Hz

with printf() statement in loop

without debugger

Lowest drive

50Hz

 

The highest frequency I can archive is around 3kHz and still of by the factor of 10.  

By the way: Using TIM2 with the MSI clock is working as excepted, therefore I assume my assumptions on timers are generally ok.

> Can you verify LSE frequency in some other way, e.g. through RTC, or outputting it to MCO pin?

That’s probably a good idea but something which needs time. Could it be that the LPTIM is not clocked by LSE although I set the registers accordingly? Which frequency do I observe instead and why is it changing depending on the driving capability settings of the LSE?