跳转至

UART0

UART通信基础

UART(Universal Asynchronous Receiver/Transmitter)是一种异步串行通信协议,特点如下:

物理层特性:

  • 使用TX(发送)和RX(接收)两根数据线
  • 采用NRZ(不归零)编码
  • 通信双方需要预先约定相同的波特率

数据帧格式(8N1配置):

起始位(0) + 数据位(D0-D7) + 停止位(1)
┌───┬──┬──┬──┬──┬──┬──┬──┬──┬───┐
│ 0 │D0│D1│D2│D3│D4│D5│D6│D7│ 1 │
└───┴──┴──┴──┴──┴──┴──┴──┴──┴───┘
  1bit 8bits             1bit

发送过程详细流程:

  1. 状态检查:CPU读取LSR寄存器的BIT5(TX_IDLE)

    • 如果为0,表示发送器忙,需要等待
    • 如果为1,表示发送保持寄存器空闲,可以写入新数据
  2. 数据写入:CPU将要发送的字符写入THR寄存器

    uart_write_reg(THR, ch);  // 字符被存入发送保持寄存器
    

  3. 硬件自动处理

    • UART将THR中的数据转移到发送移位寄存器
    • 自动添加起始位(0)和停止位(1)
    • 按照设定的波特率进行并串转换
    • 通过TX引脚逐位发送出去
  4. 发送完成:当数据完全移出后,LSR.TX_IDLE再次变为1,准备接收下一个字符

接收过程详细流程:

  1. 线路监测:UART持续监测RX引脚的电平变化
  2. 起始位检测:检测到从高到低的跳变(起始位开始)
  3. 数据采样:在每位中间点进行采样,确保数据稳定
  4. 数据组装:将8个数据位组合成一个字节
  5. 数据就绪:将接收到的数据存入RHR寄存器,并设置LSR.RX_READY=1
  6. CPU读取:CPU检测到LSR.RX_READY=1后,从RHR读取数据

波特率与定时:

  • 波特率 = 基准时钟频率 / (分频系数 × 16)
  • 每个位的时间 = 1 / 波特率
  • 采样点通常位于每位的时间中点

UART0寄存器地址和作用

寄存器 地址偏移 读写模式 名称 作用描述
RHR 0x0 接收保持寄存器 读取接收到的数据
THR 0x0 发送保持寄存器 写入要发送的数据
DLL 0x0 分频器锁存低字节 波特率分频值的低8位(DLAB=1时)
IER 0x1 中断使能寄存器 控制各类中断的使能
DLM 0x1 分频器锁存高字节 波特率分频值的高8位(DLAB=1时)
FCR 0x2 FIFO控制寄存器 控制FIFO的使能和清除
ISR 0x2 中断状态寄存器 查询中断源和状态
LCR 0x3 读写 线路控制寄存器 设置数据格式、停止位、奇偶校验等
MCR 0x4 读写 调制解调器控制寄存器 控制调制解调器信号线
LSR 0x5 线路状态寄存器 查询发送和接收状态
MSR 0x6 调制解调器状态寄存器 查询调制解调器状态变化
SPR 0x7 读写 暂存寄存器 可随意读写的辅助寄存器

关键位说明:

  • LCR寄存器BIT7(DLAB):分频器锁存访问位,置1时访问DLL/DLM,置0时访问RHR/THR/IER
  • LSR寄存器BIT0:接收就绪标志(1=有数据可读)
  • LSR寄存器BIT5:发送空闲标志(1=发送器就绪可接收新数据)

初始化流程:

  1. 关闭所有中断(IER=0x00)
  2. 设置DLAB=1访问分频器
  3. 配置波特率(DLL=0x03, DLM=0x00)
  4. 设置通信格式(8位数据位,1位停止位,无奇偶校验)
  5. 设置DLAB=0恢复正常寄存器映射

UART0比特率配置详解

QEMU环境下的实际情况

QEMU的默认行为:

  • 在QEMU virt机器中,UART通常使用预定义的固定波特率
  • 很多QEMU实现会忽略实际的分频器设置
  • 无论程序设置什么波特率,QEMU都可能按照115200或其他固定速率工作

配置逻辑分析

void uart_init()
{
    /* 禁用中断 */
    uart_write_reg(IER, 0x00);

    /* 设置分频器访问模式 */
    uint8_t lcr = uart_read_reg(LCR);
    uart_write_reg(LCR, lcr | (1 << 7));  // 设置DLAB=1

    /* 配置波特率分频值 */
    uart_write_reg(DLL, 0x03);  // 分频器低字节 = 3
    uart_write_reg(DLM, 0x00);  // 分频器高字节 = 0

    /* 配置通信格式并退出分频器模式 */
    lcr = 0;
    uart_write_reg(LCR, lcr | (3 << 0));  // 8位数据位,DLAB=0
}

波特率计算:

  • 假设基准时钟频率 = 1.8432 MHz(传统UART常用值)
  • 分频值 = (DLM << 8) | DLL = 0x0003 = 3
  • 波特率 = 1.8432 MHz / (3 × 16) = 38,400 bps

实际开发建议

对于QEMU开发:

// QEMU环境下可以简化初始化,因为波特率设置可能被忽略
void uart_init_simple() {
    uart_write_reg(IER, 0x00);  // 禁用中断即可
    // 其他设置QEMU通常会使用合理的默认值
}

对于真实硬件:

// 真实硬件必须正确配置波特率
void uart_init_real_hardware() {
    uart_write_reg(IER, 0x00);

    // 根据实际硬件时钟频率计算分频值
    uint32_t clock_freq = 1843200;  // 需要根据具体硬件确定
    uint32_t baud_rate = 115200;    // 目标波特率
    uint16_t divisor = clock_freq / (baud_rate * 16);

    // 设置分频器
    uint8_t lcr = uart_read_reg(LCR);
    uart_write_reg(LCR, lcr | (1 << 7));  // DLAB=1
    uart_write_reg(DLL, divisor & 0xFF);  // 低字节
    uart_write_reg(DLM, (divisor >> 8) & 0xFF);  // 高字节

    // 设置通信格式
    uart_write_reg(LCR, (1 << 0) | (1 << 1));  // 8位数据位
}