17 votos

Uso de variables globales en sistemas empotrados

Empecé a escribir firmware para mi producto y soy un novato aquí. He leído muchos artículos sobre no usar variables o funciones globales. ¿Hay algún límite para el uso de variables globales en un sistema de 8 bits o es un completo 'No-No'. ¿Cómo debo usar las variables globales en mi sistema o debo evitarlas completamente?

Me gustaría recibir consejos valiosos de ustedes sobre este tema para hacer mi firmware más compacto.

31voto

JRT Puntos 270

Puedes utilizar variables globales con éxito, siempre que tengas en cuenta las directrices de @Phil. Sin embargo, aquí hay algunas buenas maneras de evitar sus problemas sin hacer el código compilado menos compacto.

  1. Utilice variables estáticas locales para el estado persistente al que sólo desea acceder dentro de un función.

    #include <stdint.h>
    void skipper()
    {
        static uint8_t skip_initial_cycles = 5;
        if (skip_initial_cycles > 0) {
            skip_initial_cycles -= 1;
            return;
        }
        /* ... */
    }
  2. Utiliza un struct para mantener juntas las variables relacionadas, para que quede más claro dónde deben estar y dónde no.

    struct machine_state {
         uint8_t level;
         uint8_t error_code;
    } machine_state;
    
    struct led_state {
        uint8_t red;
        uint8_t green;
        uint8_t blue;
    } led_state;
    
    void machine_change_state()
    {
        machine_state.level += 1;
        /* ... */
        /* We can easily remember not to use led_state in this function. */
    }
    
    void machine_set_io()
    {
        switch (machine_state.level) {
        case 1:
            PIN_MACHINE_IO_A = 1;
            /* ... */
        }
    }
  3. Utilice variables estáticas globales para que las variables sólo sean visibles dentro del archivo C actual. Esto evita el acceso accidental por el código en otros archivos debido a conflictos de nombres.

    /* time_machine.c */
    static uint8_t current_time;
    /* ... */
    
    /* delay.c */
    static uint8_t current_time; /* A completely separate variable for this C file only. */
    /* ... */

Como nota final, si estás modificando una variable global dentro de una rutina de interrupción y leyéndola en otro lugar:

  • Marcar la variable volatile .
  • Asegúrese de que es atómico para la CPU (es decir, 8 bits para una CPU de 8 bits).

O

  • Utilice un mecanismo de bloqueo para proteger el acceso a la variable.

24voto

Kip Diskin Puntos 11

Las razones por las que no querrías usar variables globales en un sistema de 8 bits son las mismas por las que no querrías usarlas en ningún otro sistema: dificultan el razonamiento sobre el comportamiento del programa.

Sólo los malos programadores se aferran a reglas como "no utilices variables globales". Los buenos programadores entienden el porqué de las reglas y las tratan más bien como directrices.

¿Es su programa fácil de entender? ¿Es predecible su comportamiento? ¿Es fácil modificar partes del programa sin romper otras? Si la respuesta a cada una de estas preguntas es entonces estás en el camino hacia un buen programa.

5voto

Nick Alexeev Puntos 20994

No deberías evitar por completo el uso de variables globales ("globals" para abreviar). Pero debes usarlas con criterio. Los problemas prácticos con el uso excesivo de globales:

  • Los globales son visibles en toda la unidad de compilación. Cualquier código de la unidad de compilación puede modificar un global. Las consecuencias de una modificación pueden aparecer en cualquier lugar donde se evalúe este global.
  • Como resultado, los globales hacen que el código sea más difícil de leer y entender. El programador siempre tiene que tener en cuenta todos los lugares donde se evalúa y asigna el global.
  • El uso excesivo de globales hace que el código sea más propenso a los defectos.

Es una buena práctica añadir un prefijo g_ al nombre de las variables globales. Por ejemplo, g_iFlags . Cuando veas la variable con el prefijo en el código, reconocerás inmediatamente que se trata de una global.

4voto

Nam G VU Puntos 3494

La ventaja de las estructuras de datos globales en el trabajo integrado es que son estáticas. Si todas las variables que necesitas son globales, nunca te quedarás sin memoria accidentalmente cuando se introduzcan funciones y se haga espacio para ellas en la pila. Pero entonces, ¿para qué tener funciones? Por qué no una gran función que maneje toda la lógica y los procesos - como un programa BASIC sin GOSUB permitido. Si llevas esta idea lo suficientemente lejos tendrás un típico programa en lenguaje ensamblador de los 70's. Eficiente, e imposible de mantener y solucionar.

Así que usa globals juiciosamente, como variables de estado (por ejemplo, si cada función necesita saber si el sistema está en estado de interpretación o de ejecución) y otras estructuras de datos que necesitan ser vistas por muchas funciones y como dice @PhilFrost, ¿es predecible el comportamiento de tus funciones? ¿Existe la posibilidad de llenar la pila con una cadena de entrada que nunca termina? Estas son cuestiones de diseño de algoritmos.

Tenga en cuenta que static tiene un significado diferente dentro y fuera de una función. https://stackoverflow.com/questions/5868947/difference-between-static-variable-inside-and-outside-of-a-function

https://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c

4voto

Alex Andronov Puntos 178

Las variables globales sólo deben utilizarse para estados verdaderamente globales. Utilizar una variable global para representar algo como, por ejemplo, la latitud del límite norte del mapa, sólo funcionará si sólo puede haber un "límite norte del mapa". Si en el futuro el código tiene que trabajar con varios mapas que tienen diferentes límites septentrionales, el código que utiliza una variable global para el límite septentrional probablemente tendrá que ser reelaborado.

En las aplicaciones informáticas típicas, a menudo no hay ninguna razón en particular para suponer que nunca habrá más de uno de algo. Sin embargo, en los sistemas embebidos, estas suposiciones suelen ser mucho más razonables. Mientras que es posible que un programa informático típico tenga que dar soporte a varios usuarios simultáneos, la interfaz de usuario de un sistema embebido típico estará diseñada para ser utilizada por un único usuario que interactúe con sus botones y su pantalla. Como tal, en cualquier momento tendrá un único estado de interfaz de usuario. Diseñar el sistema para que varios usuarios puedan interactuar con varios teclados y pantallas requeriría mucha más complejidad y tardaría mucho más en implementarse que diseñarlo para un único usuario. Si el sistema nunca tiene que soportar múltiples usuarios, cualquier esfuerzo extra invertido para facilitar dicho uso será en vano. A menos que sea probable que se requiera soporte multiusuario, probablemente sería más inteligente arriesgarse a tener que descartar el código utilizado para una interfaz monousuario en el caso de que se requiera soporte multiusuario, que gastar tiempo extra añadiendo soporte multiusuario que probablemente nunca se necesitará.

Un factor relacionado con los sistemas embebidos es que en muchos casos (especialmente en lo que se refiere a interfaces de usuario), la única forma práctica de soportar tener más de una cosa sería utilizar múltiples hilos. En ausencia de alguna otra necesidad de multihilo, es probablemente mejor utilizar un diseño simple de un solo hilo que aumentar la complejidad del sistema con multihilos que probablemente nunca serán realmente necesarios. Si la adición de más de uno de algo requeriría un enorme rediseño del sistema de todos modos, no importará si también requiere reelaborar el uso de algunas variables globales.

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