
清空记录
历史记录
取消
清空记录
历史记录

一、UART基本概念
1.1 什么是UART?
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是最常用的串行通信接口,用于:
•调试输出:打印内核启动信息
•设备通信:与GPS、蓝牙、WiFi等模块通信
•数据透传:与上位机或其他MCU交换数据
1.2 UART通信原理
参数 | 说明 | 常用值 |
波特率 | 每秒传输的比特数 | 115200, 9600 |
数据位 | 每个字符的数据位数 | 8 bits |
停止位 | 字符结束标志 | 1 bit |
校验位 | 错误检测 | None/Even/Odd |
1.3 RK3576 UART资源
RK3576提供12路UART,触觉智能Purple Pi OH2配套核心板SOM7609系列全引脚引出:
•uart0 ~ uart11
•uart0 通常用作调试串口(Debug)
从设备树别名可见:
serial0 = &uart0; // 通常 Debug 串口serial1 = &uart1;serial2 = &uart2;...serial11 = &uart11;
如需购买触觉智能RK3576开发板,可通过触觉智能官方店购买:
https://item.taobao.com/item.htm?id=978595600333
开发板视频介绍:
二、UART硬件连接
2.1 Purple Pi OH2 板载UART接口(GPIO排针)
2.2 UART引脚定义
信号 | 方向 | 说明 |
TX | 输出 | 发送数据 |
RX | 输入 | 接收数据 |
RTS | 输出 | 请求发送(流控) |
CTS | 输入 | 清除发送(流控) |
2.3 UART双工模式
UART支持两种通信模式,由硬件设计和软件配置决定。
模式 | 特点 | 硬件要求 | 应用场景 |
全双工 | TX、RX同时工作,独立传输 | TX、RX分开连接 | 调试串口、蓝牙、GPS |
半双工 | TX、RX分时工作,共享总线 | 需要方向控制引脚 | RS-485、Modbus |
全双工配置
设备树配置(全双工):
&uart2 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart2m0_xfer>; /* TX/RX 分开 */};半双工配置
软件实现(半双工RS-485):
/* 发送数据 */void rs485_send(struct uart_dev *priv, u8 *data, int len){/* 拉高 DE 引脚,切换到发送模式 */gpio_set_value(priv->de_gpio, 1);/* 发送数据 */uart_write(priv, data, len);/* 等待发送完成 */uart_wait_tx_complete(priv);/* 拉低 DE 引脚,切换到接收模式 */gpio_set_value(priv->de_gpio, 0);}
设备树配置(半双工 + 方向控制):
&uart2 {status = "okay";pinctrl-names = "default", "rs485";pinctrl-0 = <&uart2m0_xfer>;pinctrl-1 = <&uart2m0_rs485>;rs485-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>;rs485-rts-active-high;linux,rs485-enabled-at-boot-time;};
流控模式(硬件流控)
设备树配置(硬件流控):
&uart2 {status = "okay";pinctrl-names = "default";/* 包含 TX/RX/RTS/CTS */pinctrl-0 = <&uart2m0_xfer &uart2m0_ctsn &uart2m0_rtsn>;/* 启用 CTS/RTS 流控 */cts-cts-cts;rts-cts-rts;};
双工模式对比
特性 | 全双工 | 半双工 | 硬件流控 |
数据线 | TX/RX 分开 | TX/RX 共用 | TX/RX + RTS/CTS |
同时收发 | 支持 | 不支持 | 支持 |
方向切换 | 不需要 | 需要 GPIO | 自动 |
速率限制 | 高 | 低(切换开销) | 高 |
应用场景 | 调试、蓝牙 | RS-485、Modbus | 高速可靠传输 |
三、UART设备树配置
3.1 使能UART节点
编辑ido-evb7609-v1a.dtsi:
&uart2 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart2m0_xfer &uart2m0_ctsn>;};3.2 UART蓝牙配置
wireless_bluetooth: wireless-bluetooth { compatible = "bluetooth-platdata"; clocks = <&hym8563>; clock-names = "ext_clock"; uart_rts_gpios = <&gpio1 RK_PC4 GPIO_ACTIVE_LOW>; pinctrl-names = "default", "rts_gpio"; pinctrl-0 = <&uart2m0_rtsn>; pinctrl-1 = <&uart2_gpios>; BT,reset_gpio = <&gpio1 RK_PC3 GPIO_ACTIVE_HIGH>; BT,wake_gpio = <&gpio1 RK_PD4 GPIO_ACTIVE_HIGH>; BT,wake_host_irq = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>; status = "okay";};status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart3m0_xfer>;};
3.2 pinctrl引脚配置
&pinctrl {uart2 {uart2m0_xfer: uart2m0-xfer {rockchip,pins =/* uart2_rxm0 */<4 RK_PA4 8 &pcfg_pull_up>,/* uart2_txm0 */<4 RK_PA5 8 &pcfg_pull_none>;};};uart3 {uart3m0_xfer: uart3m0-xfer {rockchip,pins =/* uart3_rxm0 */<3 RK_PC6 9 &pcfg_pull_up>,/* uart3_txm0 */<3 RK_PC7 9 &pcfg_pull_none>;};};};
四、UART驱动开发
4.1 内核空间UART操作
struct my_uart_dev {struct uart_port port;struct clk *clk;void __iomem *regs;int irq;};/* UART 寄存器偏移 *//* 发送数据 */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 驱动框架
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操作
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设备
# 查看注册的 UARTcat /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内核
