利用Cortex-M的Send-Event-on-Pend特性实现休眠轮询编程模式,休眠轮询模式在保持程序设计简单性的同时降低了功耗。
mingdu.zheng at gmail dot com
http://blog.csdn.net/zoomdy/article/details/79399478
轮询模式因为不涉及中断处理,因此在程序结构上比较简单,也比较符合人类的同步思维习惯。但通常的轮询模式需要不断地检查外设状态,这会消耗更多的时钟和电能,将轮询模式和Cortex-M的Send-Event-on-Pend特性结合起来,可以实现休眠轮询模式,这种模式会在外设状态没有改变时进入休眠状态,当外设状态改变时再唤醒CPU检查外设状态。休眠轮询模式保持了轮询的简单性又降低了系统功耗。
中断使能和屏蔽
Cortex-M中有三个地方可以屏蔽中断。一是CPU的PRIMASK寄存器,这是全局中断屏蔽开关,可以屏蔽所有的中断,这可以称作CPU级的中断使能;二是NVIC的中断使能寄存器,这可以使能或屏蔽某个外设的全部中断,这可以称作NVIC级的中断使能;三是外设的中断使能寄存器,可以控制外设更细节的中断使能,这可以称作外设级的中断使能。
要实现休眠轮询,需要设置外设的中断使能寄存器启用外设中断,但是要清除NVIC的中断使能寄存器避免进入中断处理。如此设置后,当外设产生中断时,NVIC的挂起中断寄存器会置位,但是又不会进入中断处理模式。挂起中断寄存器值的改变将唤醒使用WFE指令进入休眠的CPU。
初始化步骤
// 首先设置SCR寄存器,启用SEVONPEND特性
SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
// 清除NVIC中外设对应的使能位,屏蔽NVIC级的中断,这要在设置外设中断寄存器前,否则可能会产生非预期的中断处理
NVIC->ICER = USART1_IRQ;
// 设置外设中断使能寄存器,使能外设级的中断
USART1->CR1 |= USART_RXNEIE;
轮询然后休眠
for(;;) {
// 首先清除USART1的挂起状态
// 如果USART1已经处于中断挂起状态,那么执行WFE后即使USART1状态有变化,也不会唤醒CPU
NVIC->ICPR = USART1_IRQ;
// 检查外设的状态,如果有数据要处理,立即退出轮询
if(USART1->ISR & UART_FLAG_RXNE) {
break;
}
// 没有要处理的数据,进入休眠,等待数据的到来
__DSB(); // It is recommended to add a DSB to allow better portability
__WFE();
// 接收到数据后,USART1的中断被挂起,从而唤醒CPU,继续执行轮询检查外设状态
}
// 唤醒后处理后续工作
d = USART1->RDR;
SysTick的特殊之处
NVIC中没有SysTick的中断使能位,所以如果要考虑SysTick的话,只能设置PRIMASK屏蔽CPU中断了。SysTick的挂起状态清除位也不在NVIC中,而是在ICSR寄存器中,清除SysTick的中断挂起状态:
SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
参考
《The Definitive Guide to ARM® Cortex®-M0 and Cortex-M0+ Processors》
19.1.5 Using Send-Event-on-Pend Feature
The Send-Event-on-Pend feature allows any interrupt (including disabled ones) to wake up the processor if the processor entered sleep by executing the WFE instruction.
.
When the SEVONPEND bit in the System Control Register is set, an interrupt switching from inactive state to pending state generates an event, which wakes up the processor from WFE sleep.
.
If the pending status of an interrupt was already set before entering sleep, a new request from this interrupt during WFE sleep will not wake up the processor.