¿El uso de una función de demora () del software tiene un mayor consumo de energía que el uso de una interrupción del temporizador del sistema? [cerrado]

1

Tuve una discusión con alguien sobre el uso de un retardo de software (), y cómo simplemente ejecuta los comandos NOP para un número específico de ciclos de reloj de acuerdo con su retardo, y por lo tanto está constantemente funcionando y consumiendo energía. La alternativa de acuerdo con mi lectura es usar los temporizadores del sistema. Pero, una vez que se habilitan estos temporizadores, también se incrementan durante el mismo número 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. ¿Hay métodos alternativos de retardo que desconozco y que llevan a un mejor consumo de energía?

Como hay demasiadas variables para responder adecuadamente, intentaré dar un ejemplo. La tarea de ejemplo es mostrar continuamente la cantidad de segundos pasados 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 al usar una función en lugar de SysTick IRQ

    
pregunta akivjh

3 respuestas

5

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.
respondido por el berendi
2

Por lo tanto, un ciclo ocupado (como el delay() que estás describiendo, probablemente no el delay que realmente estás usando) mantendrá el núcleo de tu CPU funcionando a toda velocidad.

Cuando simplemente inactivas el núcleo de la CPU y esperas una interrupción, solo se ejecuta un contador de hardware mucho más simple. "Mucho más simple" significa que el reloj que conmuta 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 puedes apagar el generador de reloj del núcleo de la CPU, y eso ahorra aún más energía.

    
respondido por el Marcus Müller
0

Esta pregunta va a ser influenciada por tantas variables que se vuelve esencialmente imposible de responder. Aquí hay algunas razones por las que:

  1. En el caso de los bucles NOP, ¿el temporizador ya está ejecutando otra cosa?
  2. En el caso de usar el temporizador para la demora, ¿el sondeo del software está esperando que el temporizador caduque?
  3. Cuando el temporizador está en uso para el retraso, ¿la ejecución del software se pone en modo SLEEP?
  4. ¿El software mantiene el contador de bucle para el contador NOP en un registro o en una ubicación de memoria?
  5. Si se usa una ubicación de memoria, ¿la memoria está en un chip o es externa en otro componente?
  6. ¿Si el procesador externo implementa algún tipo en el algoritmo de almacenamiento en caché de la memoria externa?

También hay otras consideraciones.

Sospecho que si está utilizando una MCU moderna con memoria de programa integrada y memoria de datos, será difícil ver una gran diferencia de potencia para retrasos breves. En este escenario, puede haber ahorros de energía medibles durante largos retrasos cuando se utiliza el modo SLEEP de la MCU.

    
respondido por el Michael Karas

Lea otras preguntas en las etiquetas