MCU & FPGA UART (Serial) UART não é Porta Serial: Como Projetar Protocolos Robustos em Sistemas Embarcados

UART não é Porta Serial: Como Projetar Protocolos Robustos em Sistemas Embarcados


Recuperação, perda e por que sistemas reais não assumem perfeição

Até aqui, já tratamos UART como ela realmente é: um canal bruto, sem garantias, que exige bufferização, framing e validação explícita. Falta agora a pergunta mais importante de todas:

O que o sistema faz quando algo dá errado?

Porque vai dar.

O erro clássico: assumir que “não acontece”

Muitos protocolos caseiros funcionam sob a suposição implícita de que:

  • Nenhum byte será perdido
  • Frames chegam sempre completos
  • O transmissor e o receptor estão sempre sincronizados
  • O sistema pode ser resetado se algo sair do controle

Essa mentalidade cria sistemas frágeis, dependentes de reset e impossíveis de operar de forma autônoma.

// ❌ Sistema que só funciona se tudo der certo
if (packet_received) {
    process(packet);
}

Aqui não existe estado, não existe timeout, não existe tentativa de recuperação. Se o pacote não vier, o sistema simplesmente para de evoluir.

Sistemas reais assumem falha como condição normal

Em sistemas industriais, robóticos ou médicos, a comunicação é tratada como estatisticamente imperfeita. Isso muda tudo.

Um protocolo maduro define explicitamente:

  • O que acontece se um frame não chega
  • O que acontece se um frame chega corrompido
  • Quando um comando expira
  • Como o sistema volta a um estado conhecido

Timeouts: o relógio também é parte do protocolo

Se o parser entra em um estado intermediário e os bytes param de chegar, ele não pode ficar preso ali para sempre.

// ✅ Timeout de recepção
#define RX_TIMEOUT_MS 50

static uint32_t last_byte_time = 0;

void parser_process_byte(uint8_t byte) {
    last_byte_time = millis();
    // processamento normal
}

void parser_tick(void) {
    if ((millis() - last_byte_time) > RX_TIMEOUT_MS) {
        state = WAIT_SOF;
        index = 0;
    }
}

Isso garante que:

  • Frames incompletos não travem o parser
  • O sistema se auto-recupera
  • Ruído não causa deadlock lógico

Re-sincronização explícita

Um bom framing permite que o sistema se realinhe naturalmente. Basta ignorar tudo até encontrar um novo SOF.

case WAIT_SOF:
    if (byte == SOF) {
        state = WAIT_LEN;
    }
    break;

Essa lógica simples é extremamente poderosa: qualquer erro é absorvido e o sistema continua funcionando.

Perda de pacotes não é exceção

UART não garante entrega. Portanto, protocolos sérios tratam perda como algo esperado.

Isso pode ser feito de várias formas:

  • Comandos idempotentes (repetição não causa efeito colateral)
  • Sequência de pacotes
  • ACK / NACK
  • Estados confirmáveis

Exemplo simples com número de sequência:

typedef struct {
    uint8_t seq;
    uint8_t cmd;
    uint8_t data;
} packet_t;

static uint8_t last_seq = 0xFF;

void handle_packet(packet_t *p) {
    if (p->seq == last_seq) {
        return; // pacote duplicado
    }
    last_seq = p->seq;
    execute_command(p);
}

Isso evita:

  • Execução duplicada
  • Estados inconsistentes
  • Ações repetidas por retransmissão

UART como transporte muda o jogo

Quando você adiciona:

  • Bufferização
  • Framing explícito
  • Integridade
  • Parsing defensivo
  • Timeouts
  • Re-sincronização

UART deixa de ser “uma porta serial” e passa a ser um transporte confiável por construção, mesmo sem ajuda do hardware.

É nesse ponto que sistemas começam a:

  • Conversar entre si
  • Cooperar
  • Escalar
  • Ser atualizáveis
  • Rodar por anos sem intervenção humana

A frase que resume tudo

Não se escreve para UART.
Se projeta para ela.

Quem trata UART como printf() constrói demos.
Quem a trata como transporte constrói sistemas distribuídos embarcados.


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

0
Adoraria saber sua opinião, comente.x