3. UART串口打印与接收
任务目标
在玄铁C906/C910开发板上配置UART外设,实现两个功能:字符串打印输出和中断接收回显。通过本案例理解UART通信协议的基本帧格式(起始位、数据位、停止位)以及中断驱动的收发流程。
硬件连接与依赖
- 开发板调试串口(UART0)通过USB转串口模块连接PC,默认参数:115200-8-N-1(波特率115200,8数据位,无校验,1停止位)。
- 依赖玄铁SDK提供的寄存器操作宏(
readl/writel)及中断控制器驱动(PLIC),SDK默认已初始化系统时钟。
UART寄存器配置要点
玄铁C906/C910的UART兼容NS16550A标准,关键寄存器如下:
- THR/RBR(偏移0x00):发送保持/接收缓冲寄存器
- IER(偏移0x04):中断使能寄存器(bit0=接收中断)
- LCR(偏移0x0C):线路控制寄存器(bit7=DLAB,bit[1:0]=数据位,bit[3]=校验使能,bit[2]=校验类型,bit[1:0]=停止位)
- LSR(偏移0x14):线路状态寄存器(bit5=THR空,bit0=数据就绪)
- DLL/DLM(偏移0x00/0x04,需DLAB=1):波特率分频系数
关键完整代码
// uart_demo.c
#include <stdio.h>
#include <stdint.h>
#include "platform.h"
#define UART_BASE 0x02500000UL // UART0基地址(C906/C910)
#define UART_THR (UART_BASE + 0x00)
#define UART_RBR (UART_BASE + 0x00)
#define UART_IER (UART_BASE + 0x04)
#define UART_LCR (UART_BASE + 0x0C)
#define UART_LSR (UART_BASE + 0x14)
#define UART_DLL (UART_BASE + 0x00)
#define UART_DLM (UART_BASE + 0x04)
// 系统时钟频率(根据实际PLL配置修改,此处示例为24MHz)
#define SYS_CLK 24000000UL
#define BAUD_RATE 115200UL
static volatile uint8_t rx_buffer[256];
static volatile uint16_t rx_head = 0, rx_tail = 0;
// 初始化UART0:115200-8-N-1,使能接收中断
void uart_init(void) {
uint32_t divisor = SYS_CLK / (16 * BAUD_RATE);
// 设置DLAB=1,配置波特率分频
writel(0x80, UART_LCR);
writel(divisor & 0xFF, UART_DLL);
writel((divisor >> 8) & 0xFF, UART_DLM);
// 配置帧格式:8数据位,无校验,1停止位(LCR=0x03),关闭DLAB
writel(0x03, UART_LCR);
// 使能接收中断(IER bit0=1)
writel(0x01, UART_IER);
}
// 发送单个字符(轮询THR空标志)
void uart_putc(char c) {
while (!(readl(UART_LSR) & 0x20)); // 等待THR为空
writel(c, UART_THR);
}
// 发送字符串
void uart_puts(const char *str) {
while (*str) {
if (*str == '\n') uart_putc('\r'); // 自动加回车
uart_putc(*str++);
}
}
// 中断接收处理(由PLIC中断服务程序调用)
void uart_rx_isr(void) {
while (readl(UART_LSR) & 0x01) { // 数据就绪
uint8_t ch = readl(UART_RBR);
uint16_t next = (rx_head + 1) % sizeof(rx_buffer);
if (next != rx_tail) { // 缓冲区未满
rx_buffer[rx_head] = ch;
rx_head = next;
}
uart_putc(ch); // 回显
}
}
// 主函数
int main(void) {
uart_init();
uart_puts("UART Demo Start\r\n");
// 注册中断(需根据SDK的PLIC接口实现)
// plic_enable_irq(UART0_IRQ, uart_rx_isr);
while (1) {
// 主循环可处理其他任务,接收由中断完成
}
return 0;
}
编译与运行
- 将上述代码放入玄铁SDK的
baremetal工程目录,替换main.c。 - 确认
platform.h中系统时钟频率与SYS_CLK一致(若不一致需修改)。 - 编译生成
.elf文件,使用make或SDK提供的编译脚本。 - 通过JTAG或SD卡烧录到开发板,连接串口线到PC。
验证方法
- 打印验证:上电后PC串口终端(如minicom/PuTTY)应显示
UART Demo Start。 - 回显验证:在串口终端输入任意字符(如
abc123),终端应实时回显相同字符,证明中断接收与发送链路正常。 - 通过条件:打印无乱码、回显无丢字符,且连续输入100字符以上不出现卡死。
常见问题
- 乱码:检查
SYS_CLK是否与PLL实际配置一致,或波特率分频计算错误。 - 无回显:确认
uart_rx_isr已正确注册到PLIC中断表,且IER寄存器bit0已置1。 - 丢字符:接收缓冲区过小或中断处理耗时过长,可增大缓冲区或优化回显逻辑。