3. UART串口打印与接收

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;
}

编译与运行

  1. 将上述代码放入玄铁SDK的baremetal工程目录,替换main.c。
  2. 确认platform.h中系统时钟频率与SYS_CLK一致(若不一致需修改)。
  3. 编译生成.elf文件,使用make或SDK提供的编译脚本。
  4. 通过JTAG或SD卡烧录到开发板,连接串口线到PC。

验证方法

  • 打印验证:上电后PC串口终端(如minicom/PuTTY)应显示UART Demo Start。
  • 回显验证:在串口终端输入任意字符(如abc123),终端应实时回显相同字符,证明中断接收与发送链路正常。
  • 通过条件:打印无乱码、回显无丢字符,且连续输入100字符以上不出现卡死。

常见问题

  • 乱码:检查SYS_CLK是否与PLL实际配置一致,或波特率分频计算错误。
  • 无回显:确认uart_rx_isr已正确注册到PLIC中断表,且IER寄存器bit0已置1。
  • 丢字符:接收缓冲区过小或中断处理耗时过长,可增大缓冲区或优化回显逻辑。
UART帧格式(8-N-1) 起始 D0 D1 D2 D3 D4 D5 D6 D7 停止 LSR bit0=1 表示数据就绪 LSR bit5=1 表示THR空,可发送 中断回显流程 接收数据就绪 触发PLIC中断 ISR读取RBR并回显 写入THR发送