Arquitetura Firmware para Controle de Botão Multifuncional em MCUs

A interface de interação em sistemas embarcados define diretamente a usabilidade e a confiabilidade do produto final. Em microcontroladores com restrições de memória e processamento, consolidar múltiplas ações em um único botão físico exige uma arquitetura de software previsível e desacoplada da lógica de negócio. A implementação de uma Máquina de Estados Finita (FSM) fornece o modelo mais eficiente para traduzir sinais elétricos brutos em comandos de alto nível, garantindo isolamento de falhas e escalabilidade.

Modelagem de FSM e Gerenciamento de Eventos Temporais

Uma FSM em firmware embarcado opera mapeando um conjunto discreto de condições (estados), gatilhos externos (eventos) e regras determinísticas (transições). Para manipulação de teclas, a máquina deve filtrar ruídos mecânicos, identificar padrões temporais (curto, longo, duplo clique) e rotear os sinais para manipuladores específicos. A adoção de um loop não bloqueante com contagem de ticks permite que o MCU execute outras tarefas críticas enquanto aguarda a estabilização do sinnal, eliminando a necessidade de rotinas delay() que congestionam o barramento.

#include <stdint.h>
#include <stdbool.h>

typedef enum {
    KEY_STANDBY = 0,
    KEY_FILTER_ACTIVE,
    KEY_CONFIRM_SHORT,
    KEY_CONFIRM_LONG,
    KEY_RELEASE_PENDING
} KeyPhase_t;

typedef struct {
    uint32_t timestamp_ms;
    KeyPhase_t current_phase;
    bool pin_is_active;
    uint8_t press_counter;
} BtnCtx_t;

static void process_key_event(BtnCtx_t *ctx, bool raw_state, uint32_t now_ms) {
    uint32_t delta = now_ms - ctx->timestamp_ms;
    
    switch (ctx->current_phase) {
        case KEY_STANDBY:
            if (raw_state) {
                ctx->timestamp_ms = now_ms;
                ctx->current_phase = KEY_FILTER_ACTIVE;
            }
            break;
            
        case KEY_FILTER_ACTIVE:
            if (!raw_state) {
                ctx->current_phase = KEY_STANDBY;
            } else if (delta >= 25U) {
                ctx->pin_is_active = true;
                ctx->current_phase = (delta >= 800U) ? KEY_CONFIRM_LONG : KEY_CONFIRM_SHORT;
            }
            break;
            
        case KEY_CONFIRM_SHORT:
            if (!raw_state) {
                trigger_command(CMD_MODE_TOGGLE);
                ctx->current_phase = KEY_STANDBY;
                ctx->pin_is_active = false;
            }
            break;
            
        case KEY_CONFIRM_LONG:
            if (!raw_state) {
                trigger_command(CMD_FACTORY_RESET);
                ctx->current_phase = KEY_STANDBY;
                ctx->pin_is_active = false;
            }
            break;
            
        default:
            ctx->current_phase = KEY_STANDBY;
            break;
    }
}

Leitura Matricial e Resolução de Conflitos

A expansão para múltiplos botões ou combinações exige uma estratégia de varredura que evite leitura fantasma e condições de corrida. Ao invés de polling contínuo, a abordagem moderna utiliza vetores de bits para rastrear o estado atual do array e prioriza a execução com base na criticidade do comando. A máscara de bits atua como filtro, garantindo que apenas sequências válidas sejam encaminhadas ao decodificador de ações.

#define ROW_LINES  4
#define COL_LINES  4
#define MAX_KEY_EVENTS 16

static volatile uint8_t key_bitmap = 0;

void scan_matrix_row(uint8_t row_idx, uint8_t col_mask) {
    activate_row_gpio(row_idx);
    for (uint8_t c = 0; c < COL_LINES; ++c) {
        if (read_col_pin(c)) {
            key_bitmap |= (0x01 << ((row_idx * COL_LINES) + c));
        }
    }
    disable_row_gpio(row_idx);
}

uint8_t dispatch_combined_input(void) {
    uint8_t snapshot = key_bitmap;
    key_bitmap = 0; // Limpa para próxima varredura

    if ((snapshot & (1<<0 | 1<<5)) != 0) return EVT_REBOOT_SEQUENCE;
    if (snapshot & (1<<4)) return EVT_NAV_UP;
    if (snapshot & (1<<6)) return EVT_NAV_DOWN;
    if (snapshot & (1<<15)) return EVT_CONFIRM_SELECTION;
    
    return EVT_IDLE;
}

Toolchain, Otimização e Greenciamento de Recursos

A cadiea de ferramentas (toolchain) e os parâmetros do linker impactam diretamente o footprint de memória e a latência de resposta. A compilação para arquiteturas ARM Cortex-M ou RISC-V requer configuração explícita de otimização, remoção de seções não utilizadas e alocação estática de memória. A análise de mapas de ligação (.map) permite identificar vazamentos de RAM e ajustar o layout do linker para a aplicação específica.

ARCH  = arm-none-eabi
CC    = $(ARCH)-gcc
LD    = $(ARCH)-ld
OBJCP = $(ARCH)-objcopy

CFLAGS = -mcpu=cortex-m4 -mthumb -Wall -Os \
         -ffunction-sections -fdata-sections \
         -fno-builtin -fshort-enums
LDFLAGS = -T memory_map.ld --gc-sections -Wl,-Map=output.map
SRC = $(wildcard core/*.c) $(wildcard drv/*.c)
OBJ = $(SRC:.c=.o)
TARGET = app.bin

$(TARGET): $(OBJ)
	$(CC) $(OBJ) $(LDFLAGS) -o firmware.elf
	$(OBJCP) -O binary firmware.elf $(TARGET)

%.o: %.c
	$(CC) $(CFLAGS) -I. -c $< -o $@

clean:
	$(RM) $(OBJ) firmware.elf $(TARGET) *.map
Flag do Compilador Tamanho em Flash (KB) Alocação RAM (B) Latência ISR (µs)
-O0 42.1 1024 14
-Os 28.4 816 9
-O2 33.7 880 5
-Ofast 35.2 912 3

Mapeamento de Modos e Fluxo de Operação

A transição entre perfis funcionais deve ser abstraída por uma camada de roteamento que avalia o contexto do sistema antes de executar qualquer ação crítica. Variáveis de estado persistentes, validação de limites e mecanismos de timeout garantem que o dispositivo retorne a um estado seguro caso o usuário interrompa a sequência de comandos. A inclusão de indicadores visuais ou sonoros em janelas temporais reduz a ambiguidade e previne operações acidentais.

Quando o sistema inicia, o controlador configura os periféricos, carrega os parâmetros de fábrica e entra no estado padrão. A detecção de um evento válido aciona o verificador de modo, que avalia se a entrada corresponde a uma navegação, confirmação ou solicitação de parâmetro. Caso o evento seja mantido além de um limiar pré-definido, o dispatcher redireciona o fluxo para o submódulo de configuração avançada, bloqueando temporariamente ações de baixo nível até que o timeout expire ou uma confirmação explícita seja recebida. Essa segmentação lógica assegura que a execução de rotinas sensíveis nunca colida com a varredura de interface, mantendo a integridade dos dados e a estabilidade do ciclo de relógio.

Tags: Microcontroladores máquina-de-estados otimização-firmware toolchain-arm debounce-digital

Publicado em 8-25 10:47