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
发送过程详细流程:
-
状态检查:CPU读取LSR寄存器的BIT5(TX_IDLE)
- 如果为0,表示发送器忙,需要等待
- 如果为1,表示发送保持寄存器空闲,可以写入新数据
-
数据写入:CPU将要发送的字符写入THR寄存器
-
硬件自动处理:
- UART将THR中的数据转移到发送移位寄存器
- 自动添加起始位(0)和停止位(1)
- 按照设定的波特率进行并串转换
- 通过TX引脚逐位发送出去
-
发送完成:当数据完全移出后,LSR.TX_IDLE再次变为1,准备接收下一个字符
接收过程详细流程:
- 线路监测:UART持续监测RX引脚的电平变化
- 起始位检测:检测到从高到低的跳变(起始位开始)
- 数据采样:在每位中间点进行采样,确保数据稳定
- 数据组装:将8个数据位组合成一个字节
- 数据就绪:将接收到的数据存入RHR寄存器,并设置LSR.RX_READY=1
- 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=发送器就绪可接收新数据)
初始化流程:
- 关闭所有中断(IER=0x00)
- 设置DLAB=1访问分频器
- 配置波特率(DLL=0x03, DLM=0x00)
- 设置通信格式(8位数据位,1位停止位,无奇偶校验)
- 设置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位数据位
}