全志H3开发板实战:U-Boot中配置第二串口与外设通信的完整流程
2026/4/22 8:50:04 网站建设 项目流程

全志H3开发板U-Boot多串口开发实战:从DTS配置到驱动调试全解析

在嵌入式系统开发中,U-Boot作为系统启动加载器,其外设驱动能力往往被开发者低估。全志H3平台默认仅启用UART0作为调试串口,但当项目需要与多个外设通过串口通信时,如何安全高效地启用第二串口(UART1)就成为关键问题。本文将深入剖析从设备树配置到驱动调试的完整技术链条,帮助开发者避开常见陷阱。

1. 设备树深度配置与硬件映射

全志H3的串口控制器采用ns16550兼容架构,但时钟和引脚管理具有鲜明的Allwinner特色。正确的设备树配置是功能实现的基础。

1.1 DTS节点配置规范

sun8i-h3-nanopi.dtsi中需要确保以下关键配置:

&uart1 { pinctrl-names = "default"; pinctrl-0 = <&uart1_pins_a>; status = "okay"; };

同时需要在aliases节点中明确设备序号:

aliases { serial0 = &uart0; serial1 = &uart1; };

注意:全志H3的UART1默认引脚为GPG6(TX)和GPG7(RX),与部分开发板的丝印标注可能存在差异

1.2 时钟域与复位信号

全志H3的UART控制器时钟架构如下表所示:

时钟域源时钟分频器门控位复位位
APB2OSC24Mapb2_divbit16bit16
UART1APB2-bit1bit1

对应的时钟初始化代码应包含三个关键操作:

static void uart1_clock_init(void) { struct sunxi_ccm_reg *const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; // 设置APB2时钟源和分频 writel(APB2_CLK_SRC_OSC24M | APB2_CLK_RATE_N_1 | APB2_CLK_RATE_M(1), &ccm->apb2_div); // 打开UART1时钟门控 setbits_le32(&ccm->apb2_gate, CLK_GATE_OPEN << (APB2_GATE_UART_SHIFT + 2 - 1)); // 释放UART1复位 setbits_le32(&ccm->apb2_reset_cfg, 1 << (APB2_RESET_UART_SHIFT + 2 - 1)); }

2. U-Boot设备驱动模型深度解析

U-Boot的驱动模型(DM)在2015年后引入,与Linux设备模型有相似性但存在关键差异。

2.1 设备探测机制对比

操作类型Linux驱动模型U-Boot驱动模型
设备查找of_find_device_by_nodeuclass_find_device
设备初始化自动probe需显式调用device_probe
资源分配自动完成常需手动配置
时钟管理由CCF框架处理常需手动控制

关键代码示例展示了正确的设备获取方式:

struct udevice *dev; int ret = uclass_get_device(UCLASS_SERIAL, 1, &dev); if (ret) { printf("Failed to get UART1 device: %d\n", ret); return ret; }

2.2 串口操作封装实践

针对ns16550驱动特有的-EAGAIN返回问题,建议封装基础通信函数:

static int safe_serial_putc(struct udevice *dev, char ch) { struct dm_serial_ops *ops = serial_get_ops(dev); int ret; do { ret = ops->putc(dev, ch); if (ret == -EAGAIN) { WATCHDOG_RESET(); udelay(100); } } while (ret == -EAGAIN); return ret; }

3. 引脚复用与电气特性配置

全志H3的引脚复用配置需要特别注意GPIO组的时钟使能。

3.1 引脚功能映射表

引脚号默认功能UART1功能复用配置值
GPG6GPIOTXD2
GPG7GPIORXD2

对应的初始化代码:

static void uart1_pinmux_setup(void) { /* 使能GPG组时钟 */ setbits_le32(&ccm->bus_gate4, BIT(AHB_GATE_OFFSET_GPIOG)); /* 配置引脚复用 */ sunxi_gpio_set_cfgpin(SUNXI_GPG(6), SUN8I_H3_GPG_UART1); sunxi_gpio_set_cfgpin(SUNXI_GPG(7), SUN8I_H3_GPG_UART1); /* 配置RX引脚上拉 */ sunxi_gpio_set_pull(SUNXI_GPG(7), SUNXI_GPIO_PULL_UP); }

3.2 电气特性优化

对于长距离通信场景,建议增加以下配置:

/* 调整驱动强度为10mA */ sunxi_gpio_set_drv(SUNXI_GPG(6), SUNXI_GPIO_DRV_10MA); sunxi_gpio_set_drv(SUNXI_GPG(7), SUNXI_GPIO_DRV_10MA); /* 启用施密特触发器 */ sunxi_gpio_set_ioctl(SUNXI_GPG(7), SUNXI_GPIO_IOCTL_SCHMITT);

4. 调试技巧与性能优化

4.1 常见故障排查表

现象可能原因排查方法
数据发送卡住时钟未使能检查CCM寄存器APB2_GATE
接收数据全FF引脚复用错误用示波器检查引脚波形
偶发通信失败驱动强度不足增大GPIO驱动电流设置
波特率误差大时钟分频配置错误检查apb2_div寄存器值

4.2 性能优化建议

  1. 中断模式改造: 修改drivers/serial/ns16550.c,实现中断接收而非轮询:

    static int ns16550_serial_irq(int irq, void *dev_id) { struct ns16550_platdata *plat = dev_id; if (serial_in(&plat->regs->lsr) & UART_LSR_DR) { /* 处理接收数据 */ } return 0; }
  2. DMA传输配置: 对于高速通信场景,可启用UART的DMA功能:

    /* 使能UART1 DMA */ serial_out(UART_FCR_DMA_SELECT, &com_port->fcr);
  3. 波特率校准: 使用精确频率计测量实际波特率,调整分频系数:

    void uart1_baud_calibrate(void) { /* 基于实际测量值计算最佳分频 */ uint32_t actual_div = OSC24M_CLK / (16 * desired_baud); writel(actual_div, &com_port->divisor_latch); }

在实际项目中,我们发现GPG6引脚在部分批次H3芯片上存在硅缺陷,表现为发送波形畸变。解决方案是在PCB设计时将UART1更换到UART3(PA13/PA14)引脚,或在软件中增加预加重驱动:

sunxi_gpio_set_ioctl(SUNXI_GPG(6), SUNXI_GPIO_IOCTL_PRE_EMPHASIS);

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询