14 Introducción al modo de ahorro de energia
Administración de energía:
Los modos de suspensión del procesador Cortex-M4F reducen el consumo de energía:
■ El modo sleep:
Detiene el reloj del procesador.
■ El modo sleep profundo:
Detiene el reloj del sistema, apaga el PLL y la memoria Flash. El bit SLEEPDEEP del registro de control del sistema (SYSCTRL) selecciona qué modo de suspensión se usa.
Entrar en los modos sleep:
Esta sección describe los mecanismos que el software puede usar para poner el procesador en uno de los modos de ahorro de energía. El sistema puede generar eventos de activación espurios, por ejemplo, una operación de depuración activa el procesador. Por lo tanto, el software debe poder volver a configurar el procesador en modo de ahorro de energia después de tal evento. Un programa puede tener un bucle inactivo para volver a poner el procesador en modo sleep.
Esperando por un evento de interrupccion:
La instrucción en esamblador wait for interrupt (WFI), causa inmediatamente que el microcontrolador pase a modo de ahorro de energía.
Cuando el procesador ejecuta la instrucción WFI, se detiene el flujo de programa y el microcontrolador entra al modo sleep.
Adicionalmente se tiene el modo esperar por un evento usando la instrucción (WFE). Esta instruccion esta fuera del alcance del curso pero si usted quiere antenderla puede ir directamente a la hoja de datos en la pagina 114.
Despertar del modo sleep.
Es el mecanismo que permite despertar al procesador del modo de ahorro de energía dependiendo del mecanismo que cause el modo de ahorro de energía ya sea WFI o WFE.
Despertar del modo de energía WFI.
Normalmente el procesador se despierta cuando el NVIC detecta una excepción/Interrupción con suficiente prioridad. Algunos sistemas embebidos pueden ejecutar rutinas de restatauracion del sistema justo despues de despertar de una interrupcion/excepción pero antes de ejecutar la rutina de interrupción. Esto para evitar posibles fallos durante la ejecuccion de la rutina de interrupción o segun sea conveniente para el funcionamiento del sistema.
Para realizar esto último se requiere poner a 1 lógico el bit del PRIMASK y poner a 0 lógico el bit FAULTMASK.
Entonces is la interrupcion occurre (si esta es de mayor prioridad) el procesador retarda antender a la rutina de interrupción hasta que se coloque a cero lógico el bit PRIMASK.
Crear la funcion para entrar en modo de ahorro de energia
En Keil que es el compilador que estamos utilizando se requiere de hacer una rutina hibrida de C y Ensamblador como lo hicimos para los retardos usando ASM.
__asm void
sleep(void)
{
WFI
BX LR ;//the link register is providing the address to branch to.
}
Como puedes observar usamos (__asm void) para hibridar y posteriormente nombramos a nuestra rutuna como sleep. Este nombre es arbritrario, por ejemplo algunos autores nombran a la funcion como:
Wait_For_Interrupt()
quiza sea una mejor descripción ya que sleep() es muy general y no indica claramente que el procesador entrará a modo de ahorro de energía y esperará hasta que el NVIC lo despierte.
Por último es importante notar que en la rutina se emplea la instrucción de ensamblador WFI para llevar al procesador a modo de horro de energia, enseguida de la instrucción BX que simplemente hace que el micro brinque a la siguiente instruccion que se estaba ejecutando antes de ser llamada.
En el siguiente ejemplo pego un codigo completo para probar como entrar y despertar al procesador del modo de ahorro de energía a traves de una excepción/Interrupción. Esta interrupción es debido a una transición en la terminal PA5 como se presento en el ejemplo pasado.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "TM4C123.h" // Device header
#define NVIC_EN0_R (*((volatile unsigned long *)0xE000E100)) // IRQ 0 to 31 Set Enable Register
#define NVIC_PRI0_R (*((volatile unsigned long *)0xE000E400)) // IRQ 0 to 31 Priority Register
// begins the definition of the registers of Port A to be used as a GPIO
#define SYSCTL_RCGC2_R (*((volatile unsigned long *)0x400FE108))
#define GPIO_PORTA_PCTL_R (*((volatile unsigned long *)0x4000452C))
#define GPIO_PORTA_AMSEL_R (*((volatile unsigned long *)0x40004528))
#define GPIO_PORTA_DIR_R (*((volatile unsigned long *)0x40004400))
#define GPIO_PORTA_AFSEL_R (*((volatile unsigned long *)0x40004420))
#define GPIO_PORTA_DEN_R (*((volatile unsigned long *)0x4000451C))
#define GPIO_PORTA_DATA_R (*((volatile unsigned long *)0x400043FC))
// ends the definition of the registers of Port A
//PLL Registers
#define SYSCTL_RIS_R (*((volatile unsigned long *)0x400FE050))
#define SYSCTL_RCC_R (*((volatile unsigned long *)0x400FE060))
#define SYSCTL_RCC2_R (*((volatile unsigned long *)0x400FE070))
void GPIOA_Handler(void){
GPIOA->ICR |= 0x20; // clear the corresponding interrupt
GPIOF->DATA ^= 0x04;
}
__asm void
sleep(void)
{
WFI
BX LR ;//the link register is providing the address to branch to.
}
void Trigger_PA5(void){
volatile unsigned long delay;
SYSCTL_RCGC2_R |= 0x00000001; //1) activate clock for Port A
delay = SYSCTL_RCGC2_R; //allow time for clock to start
GPIO_PORTA_AMSEL_R &= ~0x20; // 3) disable analog on PA5
GPIO_PORTA_PCTL_R &= ~0x00F00000; // 4) PCTL GPIO on PA5
GPIO_PORTA_DIR_R &= ~0x20; // 5) direction PA5 input
GPIO_PORTA_AFSEL_R &= ~0x20; // 6) PA5 regular port function
GPIO_PORTA_DEN_R |= 0x20; // 7) enable PA5 digital port
}
void Trigger_Interrupt_Init()
{
GPIOA->IS &= ~0x20; // The pin PA5 is set as edge sensitive
GPIOA->IBE &= ~0x20; // interrup event is controlled by GPIOIEV register
GPIOA->IEV |= 0x20; // Rising edge or low level in PF4 triggers and interrupt
GPIOA->ICR |= 0x20; // Corresponding interrupt is cleared
GPIOA->IM |= 0x20; // Interrupt from the pin is sent to the interrupt controller
NVIC_PRI0_R = (NVIC_PRI0_R&0xFF00FFFF)|0x00000000; //
NVIC_EN0_R |= 0x00000001; // (h) enable interrupt 0 in NVIC
//NVIC->IP[7] = 0x03 << 21; /* set interrupt priority to 3 */
//NVIC->ISER[0] |= 0x01; /* enable IRQ0 */
__enable_irq(); // Interrupt enable using startup.s functions
}
void PLL_Init(void){
// 0) Use RCC2
SYSCTL_RCC2_R |= 0x80000000; // USERCC2
// 1) bypass PLL while initializing
SYSCTL_RCC2_R |= 0x00000800; // BYPASS2, PLL bypass
// 2) select the crystal value and oscillator source
SYSCTL_RCC_R = (SYSCTL_RCC_R &~0x000007C0) // clear XTAL field, bits 10-6
+ 0x00000540; // 10101, configure for 16 MHz crystal
SYSCTL_RCC2_R &= ~0x00000070; // configure for main oscillator source 10001111
// 3) activate PLL by clearing PWRDN
SYSCTL_RCC2_R &= ~0x00002000;
// 4) set the desired system divider
SYSCTL_RCC2_R |= 0x40000000; // use 400 MHz PLL, DIV400
SYSCTL_RCC2_R = (SYSCTL_RCC2_R&~ 0x1FC00000) // clear system clock divider
+ (4<<22); // configure for 80 MHz clock, 400/(4+1) = 80MHz
// 5) wait for the PLL to lock by polling PLLLRIS
while((SYSCTL_RIS_R&0x00000040)==0){}; // wait for PLLRIS bit
// 6) enable use of PLL by clearing BYPASS
SYSCTL_RCC2_R &= ~0x00000800;
}
void PortF_config(){
SYSCTL->RCGC2|=(0x1<<5);// The clock of PORTF
GPIOF->DEN |= 0xFF; //PORTF is Digital enable now
GPIOF->DIR|=(0x1<<2)|(0x1<<3)|(0x1<<1);//LEDs are outputs
}
int main()
{
PLL_Init();
Trigger_PA5();
Trigger_Interrupt_Init();
PortF_config();
while(1){
sleep();
}
} // end of main
Reto para el estudiante:
Modifique el codigo y genere una función que encienda un LED diferente al Azul del Puerto F cuando el procesador despierte pero antes de que ejecute la rutiana de interrupcion como se explico usando el registro PRIMASK. Prube y comprenda el codigo.
Envie el codigo a [email protected] solo hasta que este funcione correctamente.
Modifique el codigo y genere una función que encienda un LED diferente al Azul del Puerto F cuando el procesador despierte pero antes de que ejecute la rutiana de interrupcion como se explico usando el registro PRIMASK. Prube y comprenda el codigo.
Envie el codigo a [email protected] solo hasta que este funcione correctamente.