KL25 uses low power timer to wake up MCU with low power consumption
[Copy link]
KL25 supports 9 low power modes, the most commonly used is LLS mode. In this mode, the MCU stops working, most peripherals cannot work, and only some peripherals can continue to work through settings. For example: TSI button and low power timer (LPTMR). There is only one way to wake up: use the low power wake-up module (LLWU). The only function of this module is to wake up KL25 from low power mode.
What I originally wanted to write was to put the MCU into LLS mode, and then use the low power timer (LPTMR) to trigger the TSI hardware scan once every 1 second, so that when a person presses the TSI button, an interrupt can be generated, and the interrupt is passed to the low power wake-up module (LLWU) to wake up the MCU. However, this method requires three levels to wake up the MCU: first, the LPTMR generates a hardware trigger every second, and after the TSI module receives the trigger signal, it will start scanning, and after the scan, an interrupt is generated. The interrupt is passed to the LLWU module, and the LLWU module will wake up the MCU, execute the LLWU interrupt handler, and then return to the normal code, which is the next sentence of the sentence "entering LLS mode".
This setting did not work. I tried it for a long time but it was still not correct. Finally I had to use a simple wake-up method: wake up directly with LPTMR without using TSI module. The process is: let LPTMR generate an interrupt every second, and the interrupt is passed to LLWU to wake up the MCU and enter the LLWU interrupt processing code. It was much simpler and finally debugged successfully.
The LLWU module can receive 16 external pin signals to create interrupts, and can also receive interrupts from internal modules to generate interrupts itself. When its input source is an internal module, such as LPTMR or TSI buttons, it feels more like an interrupt transfer module. It receives interrupts from other modules and then uses them to wake up the MCU. After the CPU wakes up, it does not run the interrupt processing code of the module like normal operation, but runs the interrupt processing code of LLWU. This may be the difference between low-power interrupts and normal operation interrupts.
When the CPU is in normal mode, when a module generates an interrupt, the interrupt handling code of the module is directly executed; however, in the case of low-power wake-up, the interrupt of the internal module must be transmitted through LLWU, and the interrupt handling code is also the interrupt handling code of LLWU. However, since LLWU itself is not a module that generates interrupts, but more like a transmission module, its interrupt handling program is almost the same as the interrupt handling of the module that transmits the interrupt to it.
Let's look at the code:
void LLWU_Init(void);
char nmi_wake_up_flag;
char llwu_wake_up_flag;
uint32 converting = 0;
uint32 result0A = 0;
uint32 dummyread;
int main (void)
{
int i;
LLWU_Init();
lptmr_init(100, LPTMR_USE_LPOCLK);
GPIO_Init(PORTC, 16, GPIO_OUTPUT, 0); //绿灯亮
/* Enable LPTMR wakeup interrupt */
enable_irq(LPTMR_irq_no);
EnableInterrupts;
//Set the STOPM field to 0b011 for LLS mode
SMC_PMCTRL |= SMC_PMCTRL_STOPM(0x3) ;
dummyread=SMC_PMCTRL;
// Set the SLEEPDEEP bit to enable deep sleep mode (STOP)
SCB_SCR |= SCB_SCR_SLEEPDEEP_MASK;
while(1)
{
// WFI instruction will start entry into LLS mode
asm("WFI"); //进入LLS模式
for (i=0;i<1000;i++){
GPIO_ToggleBit(PORTC, 16); //绿灯闪烁
Delay1Us(5000);
}
}
}
Here are some key codes:
//When initializing the low power timer, you should mainly turn on its interrupt function. However, even if its interrupt is turned on, the MCU will not execute its interrupt code, but the interrupt code of LLWU.
//I used to think that if its interrupt was also enabled and LLWU also had an interrupt, then there would be two interrupts at the same time, so I turned off the LPTMR interrupt, but it was unsuccessful.
//In fact, LLWU only packages the process after LPTMR generates an interrupt.
void lptmr_init(int count, int clock_source)
{
SIM_SCGC5 |= SIM_SCGC5_LPTMR_MASK;
// LPTMR0_CSR &= !LPTMR_CSR_TEN_MASK;
LPTMR0_PSR = ( LPTMR_PSR_PRESCALE(0) // 0000 is div 2
| LPTMR_PSR_PBYP_MASK // LPO feeds directly to LPT
| LPTMR_PSR_PCS(clock_source)) ; // use the choice of clock
if (clock_source== 0)
printf("\n LPTMR Clock source is the MCGIRCLK \n\r");
if (clock_source== 1)
// printf("\n LPTMR Clock source is the LPOCLK \n\r");
printf("\n LPTMR is OK");
if (clock_source== 2)
printf("\n LPTMR Clock source is the ERCLK32 \n\r");
if (clock_source== 3)
printf("\n LPTMR Clock source is the OSCERCLK \n\r");
LPTMR0_CMR = LPTMR_CMR_COMPARE(count); //Set compare value
LPTMR0_CSR =( LPTMR_CSR_TCF_MASK // Clear any pending interrupt
| LPTMR_CSR_TIE_MASK // LPT interrupt enabled
| LPTMR_CSR_TPS(0) //TMR pin select
|!LPTMR_CSR_TPP_MASK //TMR Pin polarity
|!LPTMR_CSR_TFC_MASK // Timer Free running counter is reset whenever TMR counter equals compare
|!LPTMR_CSR_TMS_MASK //LPTMR0 as Timer
);
LPTMR0_CSR |= LPTMR_CSR_TEN_MASK; //Turn on LPT and start counting
}
// Below is the interrupt handling code of LLWU. It is similar to the interrupt handling code of LPTMR itself. (Most of the if statements below are not used)
void llwu_isr(void){
printf("go to LLWU INT");
if (LLWU_F1 & LLWU_F1_WUF5_MASK) {
// printf("****WUF5 was set *****\r\n");
LLWU_F1 |= LLWU_F1_WUF5_MASK; // write one to clear the flag
}
if (LLWU_F1 & LLWU_F1_WUF6_MASK) {
// printf("****WUF6 was set *****\r\n");
LLWU_F1 |= LLWU_F1_WUF6_MASK; // write one to clear the flag
}
if (LLWU_F1 & LLWU_F1_WUF7_MASK) {
// printf("****WUF7 was set from PTC3 input *****\r\n");
LLWU_F1 |= LLWU_F1_WUF7_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF8_MASK) {
// printf("****WUF8 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF8_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF9_MASK) {
// printf("****WUF9 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF9_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF10_MASK) {
// printf("****WUF10 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF10_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF11_MASK) {
// printf("****WUF11 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF11_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF12_MASK) {
// printf("****WUF12 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF12_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF13_MASK) {
// printf("****WUF13 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF13_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF14_MASK) {
// printf("****WUF14 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF14_MASK; // write one to clear the flag
}
if (LLWU_F2 & LLWU_F2_WUF15_MASK) {
// printf("****WUF15 was set *****\r\n");
LLWU_F2 |= LLWU_F2_WUF15_MASK; // write one to clear the flag
}
/************************************************************************
* Note: This ISR does not write to the LLWU_F3 register because these
* are peripheral module wakeups. The flags contained in the LLWU_F3
* register should be cleared through the associated module interrupt
* and not through the LLWU_F3 per the Kinetis L Family Reference
* Manual (LLWU Chapter)
**********************************************************************/
//The wakeup source is LPTMR. The code here is the key. You can see that it re-enables the clock of LPTMR and re-fills the control register. Although I don't know why this is because the user manual says that LPTMR
//The module can run in low power mode. Moreover, I have already done the initialization in the main function.
if (LLWU_F3 & LLWU_F3_MWUF0_MASK) {
// printf("****WUF3_MWUF0 IF LPTMR *****\r\n");
SIM_SCGC5 |= SIM_SCGC5_LPTMR_MASK;
LPTMR0_CSR |= LPTMR_CSR_TCF_MASK; // write 1 to TCF to clear the LPT timer compare flag,清除中断标志位
LPTMR0_CSR = ( LPTMR_CSR_TEN_MASK | LPTMR_CSR_TIE_MASK | LPTMR_CSR_TCF_MASK );
}
if(LLWU_FILT1 & LLWU_FILT1_FILTF_MASK){
LLWU_FILT1 |= LLWU_FILT1_FILTF_MASK;
}
if(LLWU_FILT2 & LLWU_FILT2_FILTF_MASK){
LLWU_FILT2 |= LLWU_FILT2_FILTF_MASK;
}
NVIC_ICPR |= 1 << (LLWU_irq_no%32);
}
Let's take a look at the interrupt handling code of LPTMR itself:
void lptmr_isr(void)
{
printf("\n****LPT ISR entered*****\r\n");
// enable timer
// enable interrupts
// clear the flag
LPTMR0_CSR |= LPTMR_CSR_TCF_MASK; // write 1 to TCF to clear the LPT timer compare flag
LPTMR0_CSR = ( LPTMR_CSR_TEN_MASK | LPTMR_CSR_TIE_MASK | LPTMR_CSR_TCF_MASK );
}
It can be found that the two are similar, except that there is no code to re-enable the clock.
After the code runs successfully, the LED keeps flashing, and the MCU wakes up from low power mode. I think there are the following points:
1. The internal module must allow interrupts
2. The interrupt handling code of LLWU needs to do some initialization work on the interrupt source again and clear the interrupt flag
|