martes, 12 de marzo de 2019

Máquina de Estados Finitos en C [PIC18F45K50]

Para esta implementación en software de una máquina de estados finitos (FSM: Finite State Machine) haremos uso de punteros a funciones [click en el enlace para ver un video-tutorial]. Usaremos también  el compilador XC8 en MPLAB X y un PIC18F45K50 con las mismas configuraciones que en la entrada anterior.

En una gran cantidad de problemas a resolver mediante un sistema embebido es necesario llevar a cabo un proceso estrictamente secuencial. En estos casos es muy útil y elegante estructurar nuestros programas como una máquina de estados. Quizá muchos de ustedes estén tentados a usar una estructura case para elegir un segmento de código cuando se cumple una condición. Si el numero de estados es pequeño, quizá se una opción fácil. Desafortunadamente el uso de case's es muy ineficiente cuando la máquina de estados y su tabla de transiciones es muy compleja. La implementación que se mostrará en esta entrada parecerá un tanto rebuscada pero tiene la ventaja de tener buen despeño a la hora de escalarla a numero grande de estados.

El diagrama de estados de la FSM de este ejemplo es el siguiente:
Diagrama hecho en LaTeX con TIKz
Las entradas 0/1 representan los estados de un switch conectado a RB0. Los estados se harán visibles enviando el numero correspondiente al Puerto D [esta acción será definida en la función asociada a cada estado]. Obsérvese que está máquina describe a un contador ascendente o descendente según la entrada. Se agregará un retardo de un segundo para poder visualizar las transiciones.

Movámonos al programa. Primero, debemos definir un tipo enumerado para codificar nuestro conjunto de estados:

//-- Definición de tipo de estados
typedef enum {
    Estado_0,      // Estado inicial
    Estado_1,
    Estado_2,
    Estado_3,
    Estado_4,      
    No_de_Edos
} ESTADOS;

Creamos con esto una variable enumerada global que usaremos para almacenar el estado actual de la máquina:

//-- Variable global de estado
ESTADOS Estado_Actual = Estado_0;

El siguiente paso es definir una estructura que nos permitirá construir un objeto para nuestra FSM:

//-- Estructura de Maquina de Estados (FSM))
typedef struct {
    ESTADOS Estado; // Estado miembro del conjunto enumerado ESTADOS
    void (*func)(int); // Acciones asociadas al estado         
} FSM;

Con lo anterior podemos ahora crear una instancia que sera nuestra FSM:

//-- Prototipos de funciones de estado
void Codigo_EDO_0(int);
void Codigo_EDO_1(int);
void Codigo_EDO_2(int);
void Codigo_EDO_3(int);
void Codigo_EDO_4(int);

//-- Creación de la estructura global de la FSM
FSM Maquina_de_Estados[] = {
    {Estado_0,Codigo_EDO_0},
    {Estado_1,Codigo_EDO_1},
    {Estado_2,Codigo_EDO_2},
    {Estado_3,Codigo_EDO_3},
    {Estado_4,Codigo_EDO_4},
};

Es importante que los prototipos de las funciones asociadas a cada estado aparezcan antes de la creación de la estructura (estas funciones serán definidas más adelante en el código). El segmento de código principal queda reducido elegantemente de la siguiente manera:

void main(void) {
    int sw = 0;
    setup();
   
    //-- FSM ---
    while(1){
        sw = PORTBbits.RB0;
        (*Maquina_de_Estados[Estado_Actual].func)(sw);
    }
    return;
}

Código completo:
Circuito utilizado:

No hay comentarios: