Siendo curioso, he realizado algunas mediciones simples en un tablero 32L152CDISCOVERY Mi programa de prueba consta de 4 pruebas, cada una muestra los segundos transcurridos en la pantalla LCD. La diferencia entre la prueba es lo que hacen hasta que transcurre un segundo.
-
La primera prueba no hace nada dentro del bucle. Es un "bucle de ocupado" puro, que gira hasta que cambia una variable.
-
El segundo bucle tiene una única instrucción NOP
en su interior.
-
El tercer bucle ejecuta 1000 NOP
de instrucciones seguidas.
-
El cuarto bucle ejecuta una instrucción WFI
, durmiendo hasta que se produce una interrupción.
Al presionar el botón azul se avanza a la siguiente prueba.
Hay dos parámetros de tiempo de compilación.
- La definición de
SLOWTICK
cambia la frecuencia de interrupción de SysTick
de 1 kHz a 1 Hz.
- La definición de
ALIGN_OFF
inserta un NOP
antes de cada ciclo de prueba, cambiando su alineación.
El código:
#define STM32L152xC
#include "stm32l1xx.h"
#include "lcd.h"
//#define SLOWTICK
//#define ALIGN_OFF
volatile int tick_s;
void SysTick_Handler() {
#ifdef SLOWTICK
tick_s += 1;
#else
static int tick_ms;
tick_ms += 1;
if(tick_ms == 1000) {
tick_ms = 0;
tick_s += 1;
}
#endif
}
void hw_init() {
// use the 16MHz internal HSI as clock source, no PLL
RCC->CR |= RCC_CR_HSION;
while(!(RCC->CR & RCC_CR_HSIRDY))
;
RCC->CFGR |= RCC_CFGR_SW_HSI;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI)
;
SystemCoreClockUpdate();
// enable GPIOs for the LCD and the pushbutton
RCC->AHBENR = RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN;
__ISB(); // wait a bit, see STM32L1 errata on RCC
lcd_init();
#ifdef SLOWTICK
SysTick_Config(SystemCoreClock);
#else
SysTick_Config(SystemCoreClock / 1000);
#endif
}
int last_s = -1;
void display_s() {
int d1 = (last_s / 10) % 10;
int d2 = last_s % 10;
lcd_displaychar(d1 + '0', 0, 0, 5);
lcd_displaychar(d2 + '0', 0, 0, 6);
lcd_update();
}
// repeats an instruction 10 times
#define TEN(x) ({ ({ x; }); ({ x; }); ({ x; }); ({ x; }); ({ x; }); \
({ x; }); ({ x; }); ({ x; }); ({ x; }); ({ x; }); })
int button_pressed() {
return GPIOA->IDR & 1;
}
int main() {
hw_init();
int temp_s;
while(1) {
lcd_displaytext("0NOP");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
lcd_displaytext("1NOP");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
asm volatile("nop");
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
lcd_displaytext("xNOP");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
// triple nesting repeats 10*10*10 times
TEN(TEN(TEN(asm volatile("nop"))));
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
lcd_displaytext("WFI");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
asm volatile("wfi");
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
}
}
El consumo de energía de la MCU se midió conectando un amperímetro entre los pines 1 y 2 de JP1. La guerra del depurador se desconectó eliminando las tapas de los puentes de CN3.
+--------+--------+--------+--------+
| Test 1 | Test 2 | Test 3 | Test 4 |
| 0NOP | 1NOP | xNOP | WFI |
+---------------------+--------+--------+--------+--------+
| //#define SLOWTICK | | | | |
| //#define ALIGN_OFF | 4.3 mA | 4.7 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
| //#define SLOWTICK | | | | |
| #define ALIGN_OFF | 5.0 mA | 4.8 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
| #define SLOWTICK | | | | |
| //#define ALIGN_OFF | 4.3 mA | 4.7 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
| #define SLOWTICK | | | | |
| #define ALIGN_OFF | 4.9 mA | 4.8 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
Conclusiones:
- Cambiar la alineación del código puede afectar significativamente el consumo.
- La ejecución de muchas instrucciones
NOP
reduce el consumo, pero puede ser complicado generar la cantidad exacta de instrucciones necesarias para un retraso en particular. Sin mencionar el requisito de memoria.
- Poner el controlador en suspensión es el verdadero ahorro de energía.