Tuve una discusión con alguien sobre el uso de un retraso de software (), y cómo ejecuta los comandos NOP durante una cantidad específica de ciclos de reloj de acuerdo con su retraso y, por lo tanto, está funcionando y consumiendo energía constantemente. La alternativa según mi lectura es usar temporizadores del sistema. Pero, una vez que estos temporizadores están habilitados, también aumentan durante la misma cantidad de períodos de reloj, por lo que creo que el consumo de energía sería similar.
Quería saber si hay una diferencia en el uso de energía entre los dos, dejando de lado el hecho de que delay() bloquea su programa y esas cosas. ¿Existen métodos de retraso alternativos que desconozco y que conducen a un mejor consumo de energía?
Como hay demasiadas variables para responder correctamente, intentaré dar un ejemplo. La tarea de ejemplo es mostrar continuamente la cantidad de segundos transcurridos en una pantalla LED de 7 segmentos, volviendo a 0 una vez que se alcanzan los 60 segundos. Así que supongo que el objetivo sería comparar la eficiencia de un retraso de 1 segundo en el uso de una función frente a SysTick IRQ
Siendo curioso, hice algunas medidas simples en una placa 32L152CDISCOVERY . Mi programa de prueba consta de 4 pruebas, cada una de las cuales 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 ciclo. Es un "bucle ocupado" puro, que gira hasta que cambia una variable.
El segundo ciclo tiene una sola NOP
instrucción dentro.
El tercer bucle ejecuta 1000 NOP
instrucciones seguidas.
El cuarto ciclo ejecuta una WFI
instrucción, durmiendo hasta que ocurre una interrupción.
Presionar el botón azul avanza a la siguiente prueba.
Hay dos parámetros de tiempo de compilación.
SLOWTICK
cambia la SysTick
frecuencia de interrupción de 1 kHz a 1 Hz.ALIGN_OFF
de insertos NOP
antes de cada bucle 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ó quitando 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:
NOP
instrucciones 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.Por lo tanto, un ciclo ocupado (como el delay()
que está describiendo, probablemente no el que delay
realmente está usando) mantendrá el núcleo de su CPU funcionando a toda velocidad.
Cuando simplemente inactiva el núcleo de la CPU y espera una interrupción, solo se ejecuta un contador de hardware mucho, mucho más simple. "Mucho más simple" significa que el reloj cambia muchos menos transistores y el uso de energía en los circuitos digitales suele ser la energía que se pierde al cambiar un transistor.
Durante mucho tiempo, realmente puede apagar el generador de reloj del núcleo de la CPU, y eso ahorra aún más energía.
Esta pregunta va a estar influenciada por tantas variables que se vuelve esencialmente incontestable. Aquí hay algunas razones de por qué:
Hay otras consideraciones también.
Sospecho que si está utilizando una MCU moderna con memoria de programa integrada y memoria de datos, será difícil ver mucha diferencia de potencia para retrasos cortos. En este escenario, se pueden obtener ahorros de energía medibles durante largos retrasos cuando se utiliza el modo SLEEP de la MCU.
jsotola
jsotola
chris stratton
akivjh
chris stratton
sleep(1)
es a la vez bloqueador y potencialmente eficiente.