KL25支持9种低功耗模式,最常用的是LLS模式。在这种模式下,MCU停止运作,大部分外设都不能工作,只有某些外设通过设置还可以继续工作。比如:TSI按键和低功耗定时器(LPTMR)。要想唤醒,只有一个办法:利用低功耗唤醒模块(LLWU)。这个模块唯一作用就是将KL25从低功耗模式唤醒。
本来想写的是让MCU进入LLS模式,然后用低功耗定时器(LPTMR)每1秒触发TSI硬件扫描一次,这样当人手按下TSI按键的时候,就可以产生中断,中断传递到低功耗唤醒模块(LLWU)来唤醒MCU。但是这种办法需要经历三级才能唤醒MCU:先要LPTMR每一秒产生一次硬件触发,TSI模块接收到触发信号之后,就会开始扫描,扫描完之后产生中断,该中断被传递到LLWU模块,LLWU模块就会唤醒MCU,执行LLWU的中断处理程序,然后返回到正常的代码,也就是进入到LLS模式这句话的下一句代码。
这种设定没有调试成功。搞了很长时间都不正确。最后不得不用一种简单的唤醒:直接用LPTMR唤醒,不使用TSI模块。过程是:让LPTMR每一秒钟产生一次中断,该中断传递到LLWU,用来唤醒MCU进入LLWU中断处理代码。果然简单多了,最后调试成功。
LLWU模块可以接受16种外部引脚信号来制造中断,也可以接收内部模块的中断拿来自己产生中断。当它的输入源是内部模块的时候,比如LPTMR或者TSI按键时,感觉更像一个中断传递模块。它本身接收其他模块的中断,然后自己用来唤醒MCU。CPU唤醒之后也不是像正常运行那样,运行该模块的中断处理代码,而是运行LLWU的中断处理代码。这可能是低功耗的中断和正常运行的中断不太一样的地方吧。
CPU处于正常模式的时候,某模块产生中断的时候,直接运行该模块的中断处理代码;但是在低功耗唤醒情况下,必须通过LLWU传递内部模块的中断,中断的处理代码也是LLWU的中断处理代码。不过话说回来,因为LLWU本身并不是什么产生中断的模块,更像一个传递模块,所以它的中断处理程序跟传递中断给他的那个模块的中断处理几乎没什么两样。
我们看代码:
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);
}
}
}
贴上几段关键的代码:
//低功耗定时器的初始化,主要要注意开启它的中断功能。但是虽然开启了它的中断,MCU并不会执行它的中断代码,而是执行LLWU的中断代码。
//我曾经以为如果它的中断也开启,LLWU的也有中断,那么就会同时有两个中断,所以关掉了LPTMR中断,结果不成功。
//其实LLWU只不过对LPTMR产生中断之后的过程进行了包装。
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
}
//下面是LLWU的中断处理代码。跟LPTMR本身的中断处理代码大同小异。(下面的哦if语句大部分都没用)
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)
**********************************************************************/
//唤醒源是LPTMR,这里的代码是关键,可以看到它对LPTMR的时钟进行了重新使能,控制寄存器也重新进行了填值。虽然我不知道这是为什么,因为用户手册说过LPTMR
//模块可以在低功耗模式下运行。而且,我在主函数之中已经做过初始化。
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);
}
我们看一下LPTMR本身的中断处理代码:
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 );
}
可以发现两者大同小异,只是没有重新使能时钟的代码。
代码成功运行后,LED不停闪烁,MCU就实现了从低功耗模式唤醒。感觉有以下要点:
1,内部模块要允许中断
2,LLWU的中断处理代码要对中断源再次做一些初始化工作,并清除中断标志
|