Máquinas de estado en sistemas embebidos

Inicio / Laboratorio de Computadoras Electrónicas / Máquinas de estado en sistemas embebidos

Qué es una máquina de estado

Una máquina de estados finita (FSM “Finite State Machine”) es un conjunto de estados y transiciones entre ellos que describen el comportamiento de un sistema.
Todos los estados son conocidos y definidos (Por eso, máquina de estado “finita”)

Cada estado describe la situación en que se encuentra el sistema en un momento determinado.

Las máquinas de estado se evaluan constantemente cada cierto tiempo (Ej. clock) y cada vez que esto sucede, se elige un nuevo estado -que puede ser el mismo en el que ya se encuentra- y si corresponde, se actualizan sus salidas. Para funcionar correctamente, por lo tanto, las máquinas de estado deben “conocer” su estado acual para lo que cuentan con alguna forma de memoria que recuerde el estado actual: variable de estado.

Moore

(fig. 1 – Esquema general de una Máquina de Moore)

La salida depende solo del estado actual y el estado futuro depende del estado actual y las entradas.

Mealy

(fig. 2 – Esquema general de una Máquina de Mealy)

La salida depende del estado actual y de las entradas.

Diagrama de estados

Mapa de todos los estados y las posibles traansiciones entre ellos.


(fig. 3 – Elementos de un Diagrama de estados)

  • Cada estado se representa con un círculo
    – El nombre de cada estado se coloca en la mitad superior
    – El esado de las salidas se coloca en la mitad inferior
  • Las transiciones entre estados se indican con flechas
    Sobre cada flecha se indica x / y:
    x son las condiciones que deben satisfacerse para que se poduzca el cambio de estado
    y son las acciones que se realizan una vez al cambiar de estado*
Ejemplos de máquinas de estados

(fig. 4) Blink

(fig. 5) Blink con timeout

Programando una FSM en C

Codificar una máquina de estados es muy sencillo:

La máquina de estados está representada por una estructura condicional que evalua la variable de estado: la variable donde se guarda el estado actual.

Esta estructura condicional suele ser switch y cada estado va a estar representado por un case.

Dentro de cada case se codifican primero las sentencias que establecen las salidas, y a continuación las estructuras condicionales para las transiciones.

Los ejemplos anteriores en C

/* Blink */
switch (estado) {
  case LED_ON:
    LED = 1;
    break;

  case LED_OFF:
    LED = 0;
    break;
}
/* 
  Blink con tiempo 
*/
switch (estado) {
  case LED_ON:
    LED = 1;                    // salida del estado LED_ON
    if (cntTimeout >= 1000) {   // transición para cambiar a otro estado
      estado = LED_OFF;         
      cntTimeout = 0;           // acción asociada a la transición
    }
    break;

  case LED_OFF:
    LED = 0;                    // salida del estado LED_OFF
    if (cntTimeout >= 1000) {   // transición para cambiar a otro estado
      estado = LED_OFF;       
      cntTimeout = 0;           // acción asociada a la transición
    }
    break;
}

En los ejemplos anteriores se usan constantes para identificar los estados.
La idea es que el nombre del estado en el código coincida con el nombre en el diagrama.
Esto hace más fácil entender el programa, evidencia la relación entre el código y el diagrama de estados correspondiente y hace más sencillo y confiable el mantenimiento.

*
Ejemplos más complejos:

(fig. 6 – diagrama de estados de un molinete de acceso)

// estados
#define   ESPERA   1
#define   LIBRE    3
#define   ERROR    5

#define   ID_BIEN   1
#define   ID_MAL    2

// funciones que modifican las salidas
void encenderLuzRoja();
void apagarLuzRoja();
void invertirLuzRoja();
void encenderLuzVerde();
void apagarLuzVerde();
void invertirLuzVerde();
void destrabar();
void trabar();

// condiciones de transición

int leerIdentificacion(); // --- devuelve 0 si no hay lectura
                          // --- devuelve 1 si es correcto
                          // --- devuelve 2 si es INcorrecto

// acciones de inicialización
void inicializa();

// void me_molinete
void me_molinete();

void main() {
    inicializa();

    for(;;){
        me_molinete();
    }
}

// definición de función de máquina de estados molinete
void me_molinete(){
    switch( estado_molinete) {
        case ESPERA:
          apagarLuzVerde();
          encenderLuzRoja();
          trabar();

          if (leerIdentificacion() == ID_BIEN) {
              estado_molinete = LIBRE;
              tiempo_espera = 3000;
          } else 
          if (leerIdentificacion() == ID_MAL) {
              estado_molinete = ERROR;
              tiempo_espera = 5000;

              tiempo_titila_rojo = 250; // --- !!!!!
          }
          
          break;

        case LIBRE:
          apagarLuzRoja();
          encenderLuzVerde();
          destrabar();

          if (tiempo_espera == 0) {
            estado_molinete = ESPERA;
          }

          break;

        case ERROR:
          apagarLuzVerde();
          trabar();

          if (tiempo_espera == 0) {
            estado_molinete = ESPERA;
          } else {
            // titila led rojo ¿Cómo mejorar este estado? 
            if (tiempo_titila_rojo == 0) {
              invertirLuzRoja();
              tiempo_titila_rojo = 250;
            }
          }

          break;
    }
}

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *