4. UART串口通信

任务说明

在SA8155平台上配置UART控制器,实现轮询方式的串口数据发送与接收,并通过回环验证数据正确性。本案例以UART2为例,完成引脚配置、控制器初始化、轮询收发函数实现及验证流程。

硬件环境

  • 主控:SA8155P
  • 外设:UART2(TX: GPIO_12, RX: GPIO_13)
  • 回环方式:将TX与RX引脚用杜邦线短接

核心代码

// uart_polling.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/tty.h>
#include <linux/delay.h>
#include <linux/io.h>

#define UART2_BASE          0x00A80000
#define UART_IRQ_STAT       0x0000
#define UART_IRQ_CLR        0x0004
#define UART_IRQ_EN         0x0008
#define UART_TX_FIFO        0x0010
#define UART_RX_FIFO        0x0014
#define UART_FIFO_STAT      0x0018
#define UART_CTRL           0x001C

#define TX_FIFO_FULL        BIT(0)
#define RX_FIFO_EMPTY       BIT(1)
#define RX_FIFO_DATA        BIT(2)

static void __iomem *uart_base;

static void uart2_init(void)
{
    u32 val;

    /* 使能UART2时钟(由时钟框架管理,此处仅控制寄存器) */
    val = readl(uart_base + UART_CTRL);
    val |= BIT(0);          /* UART_EN */
    val |= BIT(4);          /* TX_EN */
    val |= BIT(5);          /* RX_EN */
    writel(val, uart_base + UART_CTRL);

    /* 清中断状态 */
    writel(0xFF, uart_base + UART_IRQ_CLR);
}

static void uart2_putc(char c)
{
    u32 stat;

    /* 等待TX FIFO非满 */
    do {
        stat = readl(uart_base + UART_FIFO_STAT);
    } while (stat & TX_FIFO_FULL);

    writel(c, uart_base + UART_TX_FIFO);
}

static int uart2_getc(char *c)
{
    u32 stat;

    stat = readl(uart_base + UART_FIFO_STAT);
    if (stat & RX_FIFO_EMPTY)
        return -1;          /* 无数据 */

    *c = readl(uart_base + UART_RX_FIFO) & 0xFF;
    return 0;
}

static int __init uart_polling_init(void)
{
    char tx_buf[] = "SA8155 UART Polling Test\r\n";
    char rx_char;
    int i, ret;

    uart_base = ioremap(UART2_BASE, 0x1000);
    if (!uart_base)
        return -ENOMEM;

    uart2_init();

    /* 发送测试字符串 */
    for (i = 0; i < sizeof(tx_buf) - 1; i++)
        uart2_putc(tx_buf[i]);

    /* 回环接收验证:读取短接后收到的数据 */
    msleep(100);
    for (i = 0; i < 10; i++) {
        ret = uart2_getc(&rx_char);
        if (ret == 0)
            pr_info("RX[%d]: 0x%02X '%c'\n", i, rx_char, rx_char);
        else
            pr_info("RX[%d]: no data\n", i);
        msleep(10);
    }

    iounmap(uart_base);
    return 0;
}

static void __exit uart_polling_exit(void)
{
    /* 模块退出时无需额外操作 */
}

module_init(uart_polling_init);
module_exit(uart_polling_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SA8155 UART2 Polling TX/RX Test");

编译与加载

# 交叉编译(以SDK环境为例)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C /path/to/kernel M=$(pwd) modules

# 将生成的uart_polling.ko拷贝至目标板后加载
insmod uart_polling.ko

# 查看内核日志
dmesg | tail -20

验证方法

  1. 硬件准备:将UART2的TX(GPIO_12)与RX(GPIO_13)短接。
  2. 加载模块:执行insmod uart_polling.ko。
  3. 判定通过条件:dmesg中应看到发送的字符串SA8155 UART Polling Test对应的回环接收日志,且接收到的字符与发送内容一致(例如RX[0]: 0x53 'S')。若出现no data,说明回环未生效或引脚配置错误。

常见问题

  • 无数据接收:检查TX/RX引脚是否短接正确;确认UART2控制器时钟已由bootloader或时钟驱动使能。
  • 数据乱码:检查波特率配置。本案例默认使用bootloader已配置的波特率(通常为115200),若需修改,需在uart2_init()中增加波特率寄存器配置。
  • 模块加载失败:确认ioremap地址与SA8155内存映射表中UART2基地址一致。