MCU & FPGA Algoritimos Máquina de Estados em Sistemas Embarcados com FreeRTOS: Projeto Cíclico, Recuperável e Orientado a Eventos

Máquina de Estados em Sistemas Embarcados com FreeRTOS: Projeto Cíclico, Recuperável e Orientado a Eventos


Estrutura do projeto FreeRTOS: tipos, filas e as duas tasks

A partir daqui a gente começa a “travar o concreto”: definir o contrato de mensagens entre as threads e o esqueleto das tasks. O desenho é simples e robusto:

  • Task 1 (SM Task): dona da máquina de estados. Ela tem uma fila de entrada (smQueue) e processa eventos em run-to-completion.
  • Task 2 (IO Task): simula/representa o mundo externo (sensor, UART, SPI…). Ela recebe requisições por outra fila (ioQueue) e responde para a SM com eventos (EVT_IO_DONE / EVT_IO_FAIL).

Isso implementa, na prática, Active Object + Message Queue, e evita que o fluxo de estado dependa de chamadas bloqueantes.

Abaixo está um código completo (exemplo didático) apenas com a infraestrutura. Na próxima seção a gente preenche os handlers e a política de recuperação.

/**
 * Exemplo didático: Máquina de Estados (FSM) com FreeRTOS usando duas tasks.
 *
 * - StateMachineTask (SM): recebe eventos, executa transições e governa o ciclo.
 * - IOTask (IO): processa requisições de I/O e responde via eventos.
 *
 * Padrões aplicados:
 * - Active Object: cada "objeto" (FSM e IO) roda em sua própria task e consome filas.
 * - Message Queue: comunicação por mensagens (eventos e requisições) evita acoplamento direto.
 * - Recovery Block: estados explícitos de erro e recuperação.
 *
 * Observação:
 * Este exemplo usa "simulação" de IO. Em firmware real, a IOTask chamaria drivers,
 * faria DMA, aguardaria interrupção, etc., e sinalizaria resultado por mensagem.
 */

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include <stdint.h>
#include <stdbool.h>

/* =========================
 *  Configurações do exemplo
 * ========================= */
#define SM_QUEUE_LEN            16
#define IO_QUEUE_LEN            16

#define SM_EVENT_WAIT_MS        10     /* Quanto a SM bloqueia esperando eventos */
#define IO_REQ_WAIT_MS          50     /* IO task espera requisição */
#define IO_SIM_LATENCY_MS       15     /* Latência simulada do "hardware" */
#define IO_SIM_FAIL_EVERY_N     5      /* Falha simulada: a cada N operações, falha 1 */

/* Timeouts do ciclo */
#define IO_DEADLINE_MS          50     /* Deadline para receber resposta do IO */
#define MAX_RETRIES             3      /* Máximo de tentativas antes de entrar em SAFE */

/* =========================
 *  Tipos (Eventos e Estados)
 * ========================= */

/**
 * @brief Tipos de eventos consumidos pela FSM.
 */
typedef enum {
    EVT_NONE = 0,
    EVT_START,
    EVT_TICK,
    EVT_IO_DONE,
    EVT_IO_FAIL,
    EVT_TIMEOUT,
    EVT_RESET_LOGIC,
    EVT_FAULT_CLEAR
} SmEventType;

/**
 * @brief Estados da FSM (máquina cíclica e recuperável).
 */
typedef enum {
    ST_BOOT = 0,
    ST_INIT,
    ST_IDLE,
    ST_CYCLE_START,
    ST_WAIT_IO,
    ST_PROCESS,
    ST_ERROR,
    ST_RECOVER,
    ST_SAFE
} SmState;

/**
 * @brief "Causas" de erro para diagnóstico e decisões de recuperação.
 */
typedef enum {
    ERR_NONE = 0,
    ERR_TIMEOUT,
    ERR_IO_FAIL,
    ERR_BAD_DATA
} SmError;

/**
 * @brief Payload do evento da FSM.
 * Em sistemas reais, aqui você coloca dados vindos do driver (buffer, status, etc.).
 */
typedef struct {
    SmEventType type;
    uint32_t    timestamp_ms;   /* opcional: "agora" no momento da emissão */
    union {
        struct {
            uint8_t data[8];
            uint8_t len;
        } io;
        struct {
            SmError cause;
        } fault;
    } u;
} SmEvent;

/* =========================
 *  Requisições para a IOTask
 * ========================= */

/**
 * @brief Tipos de requisições que a FSM pode pedir para a IOTask.
 */
typedef enum {
    IOREQ_NONE = 0,
    IOREQ_READ_SAMPLE,
    IOREQ_RESET_BUS
} IoReqType;

/**
 * @brief Mensagem de requisição para a IO task.
 */
typedef struct {
    IoReqType type;
    uint32_t  req_id;     /* ID para correlacionar request/response */
} IoRequest;

/* =========================
 *  Contexto da FSM
 * ========================= */

/**
 * @brief Contexto interno da FSM.
 * Mantém estado atual, contadores e dados para decisões.
 */
typedef struct {
    SmState   st;
    SmError   last_error;

    uint32_t  cycle_count;
    uint32_t  retry_count;

    uint32_t  pending_req_id;
    TickType_t io_deadline_tick; /* tick em que o WAIT_IO expira */
} SmContext;

/* =========================
 *  Filas globais (exemplo)
 * ========================= */
static QueueHandle_t smQueue = NULL;   /* eventos para a StateMachineTask */
static QueueHandle_t ioQueue = NULL;   /* requisições para a IOTask */

/* =========================
 *  Utilitário de tempo
 * ========================= */
static inline uint32_t now_ms(void) {
    return (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS);
}

/**
 * @brief Envia um evento para a FSM de forma segura.
 */
static bool sm_send_event(const SmEvent *ev, TickType_t to) {
    if (!smQueue) return false;
    return (xQueueSend(smQueue, ev, to) == pdPASS);
}

/**
 * @brief Envia uma requisição para a IO task.
 */
static bool io_send_req(const IoRequest *req, TickType_t to) {
    if (!ioQueue) return false;
    return (xQueueSend(ioQueue, req, to) == pdPASS);
}

/* =========================
 *  Protótipos das tasks
 * ========================= */
static void StateMachineTask(void *arg);
static void IOTask(void *arg);

/* =========================
 *  Setup: criação das tasks
 * ========================= */
void app_start(void) {
    smQueue = xQueueCreate(SM_QUEUE_LEN, sizeof(SmEvent));
    ioQueue = xQueueCreate(IO_QUEUE_LEN, sizeof(IoRequest));

    /* Em firmware real, valide retorno e trate falta de heap. */
    xTaskCreate(StateMachineTask, "SM", 1024, NULL, tskIDLE_PRIORITY + 2, NULL);
    xTaskCreate(IOTask,          "IO", 1024, NULL, tskIDLE_PRIORITY + 1, NULL);
}

/* =========================================================
 *  Task 2: IO Task (simulação de mundo externo / periférico)
 * ========================================================= */
static void IOTask(void *arg) {
    (void)arg;
    IoRequest req;
    uint32_t op_count = 0;

    for (;;) {
        if (xQueueReceive(ioQueue, &req, pdMS_TO_TICKS(IO_REQ_WAIT_MS)) == pdPASS) {
            op_count++;

            /* Simula latência do hardware */
            vTaskDelay(pdMS_TO_TICKS(IO_SIM_LATENCY_MS));

            SmEvent ev = {0};
            ev.timestamp_ms = now_ms();

            /* Falha simulada periódica */
            bool fail = (IO_SIM_FAIL_EVERY_N > 0) && ((op_count % IO_SIM_FAIL_EVERY_N) == 0);

            if (req.type == IOREQ_READ_SAMPLE && !fail) {
                ev.type = EVT_IO_DONE;
                ev.u.io.len = 3;
                ev.u.io.data[0] = 0xAA;
                ev.u.io.data[1] = (uint8_t)(req.req_id & 0xFF);
                ev.u.io.data[2] = 0x55;
            } else {
                ev.type = EVT_IO_FAIL;
                ev.u.fault.cause = ERR_IO_FAIL;
            }

            (void)sm_send_event(&ev, 0);
        }

        /* A IO task poderia também emitir EVT_TICK/telemetria, mas aqui deixamos simples. */
    }
}

/* =========================================================
 *  Task 1: State Machine Task (a dona da FSM)
 * ========================================================= */
static void StateMachineTask(void *arg) {
    (void)arg;

    SmContext ctx = {
        .st = ST_BOOT,
        .last_error = ERR_NONE,
        .cycle_count = 0,
        .retry_count = 0,
        .pending_req_id = 0,
        .io_deadline_tick = 0
    };

    /* Evento inicial: start */
    SmEvent start = {.type = EVT_START, .timestamp_ms = now_ms()};
    (void)sm_send_event(&start, 0);

    for (;;) {
        SmEvent ev = {0};

        /* A FSM bloqueia pouco tempo esperando eventos (event-driven). */
        if (xQueueReceive(smQueue, &ev, pdMS_TO_TICKS(SM_EVENT_WAIT_MS)) != pdPASS) {
            /* Sem evento -> podemos gerar EVT_TICK ou checar timeout interno. */
            ev.type = EVT_TICK;
            ev.timestamp_ms = now_ms();
        }

        /* Aqui entra o "motor" da FSM (switch de estado + tratamento). */
        /* Nesta seção deixamos como esqueleto; a próxima seção implementa. */
        (void)ctx;
        (void)ev;

        /* placeholder: o motor de transições vem na próxima seção */
    }
}

Até aqui você já tem a arquitetura correta: duas tasks, duas filas, mensagens tipadas, e um contexto interno da FSM (onde vamos guardar deadline, contadores e estado). O ponto crítico é que a FSM é “dona” do comportamento: ela decide quando pedir I/O, quanto esperar, quando declarar timeout e como recuperar.

0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

IoT-Lite: Ontologia Leve para Sistemas Embarcados e Internet das CoisasIoT-Lite: Ontologia Leve para Sistemas Embarcados e Internet das Coisas

SumárioMotivação e Problemas da Interoperabilidade na IoTVisão Geral da Ontologia IoT-LitePrincipais Classes e Propriedades da IoT-LiteClasses FundamentaisPropriedades-ChaveExemplos de Uso da IoT-Lite em Sistemas Embarcados e IoTMonitoramento Ambiental com Sensores LoRaAutomação

0
Adoraria saber sua opinião, comente.x