- Configuração dos Parâmetros da UART
A comunicação assíncrona depende de um conjunto de parâmetros comuns a todos os dispositivos envolvidos. Os principais são a taxa de transmissão (baud rate), o número de bits de dados, o bit de parada e a paridade.
1.1. Seleção e Cálculo do Baud Rate
O baud rate define a velocidade de modulação do sinal, determinando quantos bits são transmitidos por segundo. A escolha deve considerar o meio físico, a capacidade de processamento do microcontrolador e o nível de ruído esperado. Para calcular o valor do registrador que controla a taxa em muitos MCUs, utiliza‑se a fórmula:
// Cálculo do divisor para UART com oversampling de 16x
uint32_t clock_peripheral = 8000000; // 8 MHz
uint32_t baudrate_desejado = 115200;
uint32_t usart_div = (clock_peripheral + (baudrate_desejado / 2)) / baudrate_desejado;
O valor resultante é então carregado no registrador de baud rate da UART.
1.2. Bits de Dados, Parada e Paridade
Geralmente adota‑se 8 bits de dados, pois isso permite representar um byte completo. O bit de parada (1 ou 2) sinaliza o fim de cada caractere; normalmente 1 bit é suficiente. A paridade (par, ímpar ou nenhuma) adiciona um bit extra para detecção de erros simples. Em aplicações onde a integridade é crítica, habilita‑se a paridade par ou ímpar; em sistemas que priorizam velocidade, desabilita‑se a paridade.
- Inicialização do Periférico UART1
Antes de qualquer transmissão, o periférico deve ser inicializado. Esse processo normalmente inclui habilitar o clock do módulo, configurar os pinos TX e RX, definir os parâmetros de comunicação e ativar o bloco UART.
O exemplo a seguir ilustra a inicialização em um microcontrolador da família STM32, utilizando funções de baixo nível para manipulação direta dos registradores:
void UART1_Inicializar(uint32_t baudrate) {
// Habilita clock da UART1 e da GPIO
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// Configura PA9 (TX) como saída alternativa e PA10 (RX) como entrada alternativa
GPIOA->MODER |= (2 << 18) | (2 << 20);
GPIOA->AFR[1] |= (7 << 4) | (7 << 8); // AF7 para USART1
// Calcula divisor e configura baud rate
uint32_t pclk = 16000000; // relógio do barramento APB2
uint32_t brr = (pclk + baudrate / 2) / baudrate;
USART1->BRR = brr;
// Habilita transmissão, recepção e a própria USART
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
Após a execução dessa função, a UART1 está pronta para enviar e receber dados.
- Envio de Dados e Mecanismo de Repetição
3.1. Envio Simples
Para transmitir um byte, basta aguardar que o buffer de transmissão esteja vazio e então escrever no registrador de dados:
void UART1_EnviarByte(uint8_t dado) {
while (!(USART1->SR & USART_SR_TXE)); // aguarda TXE (TX buffer empty)
USART1->DR = dado;
}
3.2. Envio de Blocos e Repetição Controlada
Em muitas aplicações é necessário transmitir um conjunto de dados periodicamente. O códigoo abaixo mostra uma função de envio de buffer e um laço de repetição que chama essa função a cada intervalo definido.
void UART1_EnviarBuffer(const uint8_t *buffer, uint16_t tamanho) {
for (uint16_t i = 0; i < tamanho; i++) {
UART1_EnviarByte(buffer[i]);
}
}
// Exemplo de uso com repetição
uint8_t dados[] = {0xAA, 0x55, 0x01, 0x02};
uint16_t delay_ms = 1000;
while (1) {
UART1_EnviarBuffer(dados, sizeof(dados));
HAL_Delay(delay_ms); // atraso de 1 segundo
}
Esse padrão garante o envio contínuo do mesmo conjunto de bytes, com possibilidade de modificar o conteúdo do buffer dinamicamente entre as iterações.
- Estrutura de Pacote e Protocolo de Comunicação
Para que o receptor consiga interpretar corretamente os dados, é comum definir um formato de pacote. Um pacote mínimo pode conter um byte de início, os dados e um byte de verificação, como um checksum XOR.
void UART1_EnviarPacote(uint8_t *dados, uint8_t tamanho) {
uint8_t checksum = 0;
UART1_EnviarByte(0x7E); // byte de início
for (uint8_t i = 0; i < tamanho; i++) {
UART1_EnviarByte(dados[i]);
checksum ^= dados[i];
}
UART1_EnviarByte(checksum); // checksum
UART1_EnviarByte(0x7F); // byte de fim
}
O receptor pode então monitorar a chegada do byte de início, acumular os dados até o byte de fim e confirmar a integridade com o checksum. Caso o checksum não coincida, o pacote é descartado ou solicitada uma retransmissão.
- UART1 em Aplicações Embarcadas
Em sistemas embarcados, a UART1 é frequentemente utilizada para comunicação entre microcontroladores, conexão com módulos sem fio (Wi‑Fi, Bluetooth) ou depuração via console serial. Em cenários de Internet das Coisas, por exemplo, um sensor pode enviar leituras periodicamente a um gateway através da UART.
Problemas comuns incluem perda de sincronização, overflow de buffer e erros de quadro. A adoção de um protocolo com delimitadores e checksum, o uso de interrupções e o dimensionamento correto dos buffers de recepção ajudam a mitigar essas falhas.
- Desenvolvimento e Depuração de Drivers
Durante o desenvolvimento do driver da UART1, ferramentas como analisadores lógicos e conversores USB‑serial são indispensáveis. Um analisador lógico permite visualizar os sinais TX e RX, vreificar a temporização e identificar problemas de baud rate. Já a captura de logs pelo terminal serial auxilia na validação do protocolo.
Para depuração, inserir pontos de verificação no código que registram o estado dos registradores de status (como TXE, TC, RXNE) pode acelerar a identificação de falhas de transmissão ou recepção.
O código a seguir demonstra uma estratégia simples de logging condicional que pode ser ativada apenas durante a depuração:
#ifdef DEBUG_UART
#define UART_DEBUG(msg) UART1_EnviarBuffer((uint8_t *)msg, strlen(msg))
#else
#define UART_DEBUG(msg)
#endif
Com essa macro, mensagens de depuração são enviadas apenas quando a flag DEBUG_UART está definida, evitando overhead na versão final.