2 votos

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

Tuve una discusión con alguien sobre el uso de un software delay(), y cómo éste sólo ejecuta comandos NOP durante un número específico de ciclos de reloj de acuerdo con su retraso, y por lo tanto está constantemente en funcionamiento y consumiendo energía. La alternativa según mi lectura es usar temporizadores del sistema. Pero, una vez que estos temporizadores están habilitados también se incrementan durante el mismo número de periodos de reloj por lo que pensaría que el consumo de energía sería similar.

Quería saber si hay alguna diferencia en el uso de energía entre ambos, dejando de lado el hecho de que delay() bloquea tu programa y demás. ¿Existen métodos de retardo alternativos que yo desconozca y que conduzcan a un mejor consumo de energía?

Como hay demasiadas variables para responder adecuadamente, intentaré dar un ejemplo. La tarea del ejemplo es mostrar continuamente el número 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 de usar una función vs. SysTick IRQ

7voto

berendi Puntos 316

Siendo curioso yo mismo he hecho algunas mediciones simples en un 32L152CDISCOVERY tablero. Mi programa de prueba consiste en 4 pruebas, cada una de las cuales muestra los segundos transcurridos en la pantalla LCD. La diferencia entre las pruebas es lo que hacen hasta que transcurre un segundo.

  • La primera prueba no hace nada dentro del bucle. Es un puro "bucle ocupado", que gira hasta que una variable cambia.

  • El segundo bucle tiene un único NOP instrucción en el interior.

  • El tercer bucle ejecuta 1000 NOP instrucciones seguidas.

  • El cuarto bucle ejecuta un WFI instrucción, durmiendo hasta que se produzca una interrupción.

Pulsando el botón azul se pasa a la siguiente prueba.

Hay dos parámetros de compilación.

  • Definición de SLOWTICK cambia la SysTick frecuencia de interrupción de 1 kHz a 1 Hz.
  • Definición de ALIGN_OFF inserta un 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. El depurador se desconectó quitando los tapones del puente 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 al consumo.
  • Ejecutar un montón de NOP instrucciones reduce el consumo, pero puede ser complicado generar el número exacto de instrucciones necesarias para un determinado retraso. Por no hablar de los requisitos de memoria.
  • Poner el controlador en reposo es el verdadero ahorro de energía.

2voto

DmitrySandalov Puntos 129

Así, un bucle ocupado (como el delay() que estás describiendo, probablemente no sea el delay que estás usando realmente) mantendrá el núcleo de tu CPU funcionando a toda velocidad.

Cuando sólo se pone en reposo el núcleo de la CPU y se espera una interrupción, sólo se ejecuta un contador de hardware mucho, mucho más simple. "Mucho más sencillo" significa que el reloj conmuta muchos menos transistores, y el consumo de energía en los circuitos digitales suele ser la energía que se pierde al conmutar un transistor.

Para tiempos prolongados, realmente se puede apagar el generador de reloj del núcleo de la CPU, y eso ahorra aún más energía.

0voto

Bernd Puntos 61

Esta pregunta va a estar influida por tantas variables que resulta esencialmente incontestable. He aquí algunas razones:

  1. En el caso de los bucles NOP, ¿el temporizador ya está en marcha haciendo otra cosa?
  2. En el caso de utilizar el temporizador para el retardo, ¿el software realiza un sondeo esperando que el temporizador expire?
  3. Cuando el temporizador está en uso para el retardo, ¿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 posición de memoria?
  5. Si se utiliza una ubicación de memoria, ¿la memoria está en el chip o es externa en otro componente?
  6. Si es externo, ¿el procesador implementa algún tipo de algoritmo de caché de memoria externa?

También hay otras consideraciones.

Sospecho que si estás usando una MCU moderna con memoria de programa y memoria de datos incorporada, será difícil ver una gran diferencia de potencia para los retrasos cortos. En este escenario puede haber un ahorro de energía medible para los retrasos largos cuando se utiliza el modo SLEEP de la MCU.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X