RK3576开发板UART驱动实战教程(下),基于触觉智能开发板演示

在前文中,我们已经完成了RK3576平台的Linux内核编译与设备树配置,掌握了基本的驱动开发环境。接下来,我们将深入探讨UART(通用异步收发传输器)驱动的实现。UART是嵌入式系统中最为常用的外设之一,用于串行通信。

本章将从三个层面展开: UART驱动开发、调试方法、常见问题和解决办法

对上篇感兴趣 可点此浏览

四、UART驱动开发

4.1 内核空间UART操作

复制代码
#include <linux/serial_core.h>
#include <linux/tty_flip.h>
#include <linux/of.h>
struct my_uart_dev {
    struct uart_port port;
    struct clk *clk;
    void __iomem *regs;
    int irq;
};
/* UART 寄存器偏移 */
#define UART_TX     0x00
#define UART_RX     0x00
#define UART_IER    0x04
#define UART_IIR    0x08
#define UART_LSR    0x14
/* 发送数据 */
static void my_uart_tx_char(struct my_uart_dev *priv, unsigned char ch)
{
    /* 等待发送 FIFO 空 */
    while (!(readl(priv->regs + UART_LSR) & 0x20))
        cpu_relax();

    /* 写入数据 */
    writel(ch, priv->regs + UART_TX);
}
/* 接收中断处理 */
static irqreturn_t my_uart_irq(int irq, void *dev_id)
{
    struct my_uart_dev *priv = dev_id;
    unsigned int iir, status;
    unsigned char ch;

    iir = readl(priv->regs + UART_IIR);

    /* 处理接收中断 */
    if (iir & 0x04) {
        status = readl(priv->regs + UART_LSR);

        while (status & 0x01) { /* 数据就绪 */
            ch = readl(priv->regs + UART_RX);

            /* 将数据推送到 TTY 层 */
            tty_insert_flip_char(&priv->port.state->port, ch, TTY_NORMAL);
            tty_flip_buffer_push(&priv->port.state->port);

            status = readl(priv->regs + UART_LSR);
        }
    }

    return IRQ_HANDLED;
}
/* UART 操作接口 */
static const struct uart_ops my_uart_ops = {
    .tx_empty   = my_uart_tx_empty,
    .set_mctrl  = my_uart_set_mctrl,
    .get_mctrl  = my_uart_get_mctrl,
    .stop_tx    = my_uart_stop_tx,
    .start_tx   = my_uart_start_tx,
    .stop_rx    = my_uart_stop_rx,
    .enable_ms  = my_uart_enable_ms,
    .break_ctl  = my_uart_break_ctl,
    .startup    = my_uart_startup,
    .shutdown   = my_uart_shutdown,
    .set_termios = my_uart_set_termios,
    .type       = my_uart_type,
    .release_port = my_uart_release_port,
    .request_port = my_uart_request_port,
    .config_port = my_uart_config_port,
    .verify_port = my_uart_verify_port,
};

4.2 使用 Platform UART 驱动框架

复制代码
#include <linux/platform_device.h>
#include <linux/serial.h>
static int my_uart_probe(struct platform_device *pdev)
{
    struct my_uart_dev *priv;
    struct resource *res;
    int ret;

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /* 获取寄存器资源 */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    priv->regs = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(priv->regs))
        return PTR_ERR(priv->regs);

    /* 获取中断 */
    priv->irq = platform_get_irq(pdev, 0);
    if (priv->irq < 0)
        return priv->irq;

    /* 获取时钟 */
    priv->clk = devm_clk_get(&pdev->dev, "baudclk");
    if (IS_ERR(priv->clk))
        return PTR_ERR(priv->clk);

    clk_prepare_enable(priv->clk);

    /* 初始化 uart_port */
    priv->port.iotype = UPIO_MEM;
    priv->port.membase = (void __iomem *)priv->regs;
    priv->port.mapbase = res->start;
    priv->port.irq = priv->irq;
    priv->port.ops = &my_uart_ops;
    priv->port.dev = &pdev->dev;
    priv->port.line = pdev->id; /* UART 编号 */

    /* 注册 UART */
    ret = uart_add_one_port(&my_uart_driver, &priv->port);
    if (ret)
        goto err_clk;

    platform_set_drvdata(pdev, priv);

    return 0;

err_clk:
    clk_disable_unprepare(priv->clk);
    return ret;
}

4.3 用户空间UART操作

复制代码
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
int uart_open(const char *device)
{
    int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0) {
        perror("Failed to open UART");
        return -1;
    }

    /* 清除非阻塞标志 */
    fcntl(fd, F_SETFL, 0);

    return fd;
}
int uart_config(int fd, int baudrate)
{
    struct termios options;

    /* 获取当前配置 */
    tcgetattr(fd, &options);

    /* 设置波特率 */
    switch (baudrate) {
    case 115200:
        cfsetispeed(&options, B115200);
        cfsetospeed(&options, B115200);
        break;
    case 9600:
        cfsetispeed(&options, B9600);
        cfsetospeed(&options, B9600);
        break;
    default:
        return -1;
    }

    /* 8N1 配置 */
    options.c_cflag |= (CLOCAL | CREAD);    /* 本地连接,使能接收 */
    options.c_cflag &= ~CSIZE;               /* 清除数据位掩码 */
    options.c_cflag |= CS8;                  /* 8 数据位 */
    options.c_cflag &= ~PARENB;              /* 无校验 */
    options.c_cflag &= ~CSTOPB;              /* 1 停止位 */

    /* 原始模式 */
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;

    /* 设置新配置 */
    tcsetattr(fd, TCSANOW, &options);

    return 0;
}
int uart_send(int fd, const char *data, int len)
{
    return write(fd, data, len);
}
int uart_receive(int fd, char *buf, int len)
{
    return read(fd, buf, len);
}
int main()
{
    int fd;
    char buf[256];
    int n;

    /* 打开 UART2 */
    fd = uart_open("/dev/ttyS2");
    if (fd < 0)
        return -1;

    /* 配置 115200 8N1 */
    uart_config(fd, 115200);

    /* 发送数据 */
    const char *msg = "Hello UART!\n";
    uart_send(fd, msg, strlen(msg));

    /* 接收数据 */
    while (1) {
        n = uart_receive(fd, buf, sizeof(buf) - 1);
        if (n > 0) {
            buf[n] = '\0';
            printf("Received: %s\n", buf);
        }
        usleep(10000);
    }

    close(fd);
    return 0;
}

五、调试方法

5.1 查看UART设备

复制代码
# 查看注册的 UART
cat /proc/tty/drivers
# 查看串口设备
ls -la /dev/ttyS*
# 查看 console 配置
cat /proc/consoles

5.2 测试串口通信

复制代码
# 配置串口参数
stty -F /dev/ttyS2 115200 cs8 -cstopb -parenb
# 发送测试数据
echo "test" > /dev/ttyS2
# 接收数据(阻塞模式)
cat /dev/ttyS2
# 使用 minicom 工具
minicom -D /dev/ttyS2 -b 115200

5.3 抓取串口波形

六、常见问题

复制代码
# 使用逻辑分析仪工具
# 或查看内核日志
dmesg | grep tty

6.1 串口无法打开

•检查设备节点是否存在:ls /dev/ttyS*

•检查权限:chmod 666 /dev/ttyS2

6.2 数据乱码

•波特率配置是否正确

•检查数据位/停止位/校验位

6.3 无法接收数据

•检查 RX 引脚连接

•确认中断是否正常触发

•检查流控设置(RTS/CTS)

参考文档

•RKDocs/common/UART/

•Documentation/serial/driver

本教程适用于Android14/Linux6.1内核

相关推荐
xuxie992 小时前
start7 电机闭环控制
单片机·嵌入式硬件
嵌入式-老费3 小时前
esp32开发与应用(播放wav音频文件)
嵌入式硬件
某林2123 小时前
ROS2 + WebRTC + MQTT 异构系统架构
架构·系统架构·机器人·硬件架构·webrtc·ros2
piaoyiren3 小时前
基于 STM32F103 蓝牙遥控云台超声波避障小车
stm32·单片机·嵌入式硬件·蓝牙·超声波测距·直流电机
ShallWeL4 小时前
Orin 上 USB / CSI 相机采集踩坑
人工智能·嵌入式硬件·智能硬件·agi
a4493153624 小时前
iPhone 充电故障的全链路诊断——从 Lightning/USB-C 接口到 Tristar 充电 IC
c语言·macos·ios·电脑·硬件架构·iphone
JSMSEMI115 小时前
JSM4430 30V N 沟道增强型 MOSFET
单片机·嵌入式硬件
XMAIPC_Robot5 小时前
RK3588+FPGA半导体设备量产踩坑大全|发热、时序漂移、推理抖动、成像干扰、EMC干扰方案
人工智能·嵌入式硬件·fpga开发·arm+fpga
8K超高清5 小时前
博冠获中国电影电视技术学会科技进步奖
人工智能·科技·算法·安全·接口隔离原则·智能硬件