UART与USART技术详解

一、技术原理

1.1 UART与USART的区别

UART(通用异步收发器)和USART(通用同步异步收发器)是两种常见的串行通信接口,它们的主要区别如下:

  • 通信模式:UART仅支持异步通信,而USART支持同步和异步两种通信模式。
  • 时钟信号:UART不需要时钟信号,通过波特率同步;USART在同步模式下需要额外的时钟线(SCK)。
  • 硬件流控:USART支持硬件流控(RTS/CTS),而UART通常不支持。
  • 应用场景:UART适用于中低速异步通信,如传感器数据传输;USART适用于高速同步通信,如工业控制、SPI接口替代等。

1.2 UART帧结构

UART数据帧由以下部分组成:

  • 起始位:1位,低电平,表示数据传输开始。
  • 数据位:5-9位,通常为8位,传输实际数据。
  • 校验位:可选,用于错误检测,包括奇校验、偶校验、无校验等。
  • 停止位:1-2位,高电平,表示数据传输结束。

1.3 USART同步通信

USART同步模式下,主设备通过SCK引脚提供时钟信号,数据在时钟边沿采样。同步通信的优势在于:

  • 更高的传输速率,可达4.5Mbps。
  • 更精确的时序控制,减少波特率不匹配导致的错误。
  • 可替代SPI接口,减少硬件资源占用。

二、硬件实现

2.1 微控制器UART/USART外设

以STM32为例,USART外设主要包括:

  • 波特率发生器:支持分数波特率,提高精度。
  • 发送/接收缓冲区:支持FIFO,减少CPU中断次数。
  • 中断系统:支持发送完成、接收完成、错误中断等。
  • DMA接口:支持DMA传输,提高数据吞吐量。

2.2 接口电路设计

2.2.1 TTL电平转RS-232

使用MAX232芯片实现TTL电平到RS-232电平的转换,电路连接如下:

  • STM32 TX → MAX232 T1IN
  • STM32 RX → MAX232 R1OUT
  • MAX232 T1OUT → RS-232 TX
  • MAX232 R1IN → RS-232 RX
2.2.2 RS-485接口设计

RS-485支持多节点通信,电路设计要点:

  • 使用MAX485芯片,A、B线之间接120Ω终端电阻。
  • 控制引脚(DE/RE)连接MCU GPIO,实现收发方向切换。
  • 差分信号传输,提高抗干扰能力。

2.3 FPGA实现UART

FPGA实现UART的主要模块包括:

  • 波特率发生器:通过分频产生所需波特率时钟。
  • 发送器:将并行数据转换为串行数据,添加起始位、停止位等。
  • 接收器:将串行数据转换为并行数据,检测起始位、校验位等。
  • FIFO缓冲区:减少数据丢失,提高系统稳定性。

三、软件编程

3.1 STM32 HAL库实现

3.1.1 UART初始化
UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void) {
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  if (HAL_UART_Init(&huart1) != HAL_OK) {
    Error_Handler();
  }
}
3.1.2 DMA接收实现

使用DMA+空闲中断实现不定长数据接收:

uint8_t rx_buf[100];

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) {
  if (uartHandle->Instance == USART1) {
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    hdma_usart1_rx.Instance = DMA1_Channel5;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) {
      Error_Handler();
    }
    __HAL_LINKDMA(uartHandle, hdmarx, hdma_usart1_rx);
    
    HAL_NVIC_SetPriority(USART1_IRQn, 00);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  }
}

void USART1_IRQHandler(void) {
  HAL_UART_IRQHandler(&huart1);
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
  if (huart->Instance == USART1) {
    // 处理接收到的数据
    process_data(rx_buf, Size);
    // 重新启动DMA接收
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, sizeof(rx_buf));
  }
}

3.2 Arduino串口编程

Arduino UART初始化和数据收发示例:

void setup() {
  Serial.begin(115200); // 初始化串口,波特率115200
}

void loop() {
  if (Serial.available()) {
    char c = Serial.read(); // 读取串口数据
    Serial.print("Received: ");
    Serial.println(c); // 发送数据
  }
}

3.3 Linux系统串口编程

Linux下使用C语言实现串口通信:

#include <stdio.h>
#include <fcntl.h>
#include <termios.h>

int uart_open(const char *dev) {
  int fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd < 0) {
    perror("open");
    return -1;
  }
  struct termios options;
  tcgetattr(fd, &options);
  cfsetispeed(&options, B115200);
  cfsetospeed(&options, B115200);
  options.c_cflag |= (CLOCAL | CREAD);
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  tcsetattr(fd, TCSANOW, &options);
  return fd;
}

int main() {
  int fd = uart_open("/dev/ttyUSB0");
  if (fd < 0return -1;
  char buf[100] = "Hello, UART!";
  write(fd, buf, sizeof(buf));
  read(fd, buf, sizeof(buf));
  printf("Received: %s\n", buf);
  close(fd);
  return 0;
}

三、应用案例

3.1 工业传感器数据采集

硬件架构:STM32F4 + RS-485 + 温湿度传感器
软件实现:Modbus RTU协议
抗干扰设计

  • 使用屏蔽双绞线
  • 终端电阻120Ω
  • 光电隔离

3.3 低功耗串口设计

项目:基于nRF52840的低功耗UART通信
优化措施

  • 使用低功耗模式(LPCOMP)唤醒
  • 关闭未使用外设时钟
  • 缩短通信时间,增加休眠周期

四、调试技巧

4.1 常见故障及解决方案

故障现象 可能原因 解决方案
数据乱码 波特率不匹配 检查双方波特率设置
接收不到数据 TX/RX接线错误 交叉连接TX和RX
数据丢失 缓冲区溢出 启用FIFO或DMA
校验错误 校验位设置不一致 统一校验位配置

4.2 使用示波器调试

  • 测量波特率:测量相邻两个数据位的时间间隔,计算波特率。
  • 观察波形:检查起始位、数据位、停止位是否正常。
  • 检测干扰:观察信号是否有毛刺、噪声。

4.3 逻辑分析仪应用

使用逻辑分析仪抓取UART信号,分析时序是否正确,例如:

  • 起始位下降沿是否清晰
  • 数据位采样是否在中间时刻
  • 停止位是否为高电平

五、开源项目

5.1 ZipUART32

  • 简介:FPGA UART IP核,支持同步/异步模式。
  • 特点:可配置波特率、数据位、校验位。
  • 应用:嵌入式系统调试接口、工业控制。

5.2 AVR-UART-lib

  • 简介:AVR单片机UART库。
  • 功能:支持中断、DMA、环形缓冲区。
  • 示例代码
#include "uart.h"

int main(void) {
  uart_init(9600);
  uart_puts("Hello, UART!");
  while (1) {
    if (uart_available()) {
      char c = uart_getc();
      uart_putc(c);
    }
  }
}

六、未来发展趋势

  • 高速串口:支持更高波特率,如10Mbps以上。
  • 智能化:自动波特率检测、自适应噪声过滤。
  • 集成化:与无线通信融合,如LoRa、NB-IoT。
  • 低功耗:优化休眠模式,降低待机电流。"]
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐