恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

STM32F2串口不够用?WK2114扩展4路UART驱动详解

  • 首页
  • 资讯中心
  • /
  • STM32F2串口不够用?WK2114扩展4路UART驱动详解

相关资讯

本地化YOLO图形界面工具:从模型推理到可视化交互的完整实现 2026/10/17 16:25:26
开源机器人集群KKSwarm:ROS多机架构、编队控制与避坑指南 2026/10/17 16:25:26
猫品种识别实战:基于CNN迁移学习与调参全流程解析 2026/10/17 16:20:26

最新资讯

Flink流处理实战:状态管理、时间语义与生产调优指南
CTF Web题实战:JWT弱密钥与zip软链接攻击组合getshell
随机森林模式识别系统设计与MATLAB实现——银行贷款审批三分类
Claude Code安装教程:用cc-switch管理API Key与Node.js环境
6个MCP服务平台推荐:从阿里百炼一键部署到TaoToken统一接入
数据库原理实验:从DBMS安装到SQL查询的完整交付方案

今日推荐

Qwen3.5-122B-A10B-singprobe幻觉检测实战:AUC 0.7806,如何实时揪出大模型一本正经的胡话?
NetSpot 实战:用信道分析与频宽优化解决 Wi-Fi 卡顿
C++ split() 字符串分割的五种实现与性能实测

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

STM32F2串口不够用?WK2114扩展4路UART驱动详解

发布时间:2026/10/17 16:25:26
STM32F2串口不够用?WK2114扩展4路UART驱动详解 简介串口拓展芯片驱动面向嵌入式开发者提供WK2114芯片与STM32F2系列微控制器的完整适配方案。该驱动通过STM32F2的UART1接口与WK2114交互可扩展出更多UART端口有效解决单颗微控制器串口资源不足的问题广泛适用于物联网设备、工业自动化等多设备串行通信场景。压缩包内共2个文件包含一个C源文件和一个头文件源码干净紧凑整包仅4KB便于快速集成到现有工程。驱动覆盖WK2114初始化、波特率与数据格式配置、控制信号处理以及数据收发等核心函数头文件同时定义了函数原型、常量和结构体开发人员可直接调用相关接口实现串口拓展并可结合STM32F2的HAL库或寄存器操作深入理解底层驱动流程。资源已有1263人学习适合正在调试多串口项目或学习嵌入式驱动开发的工程师参考有助于快速掌握串口扩展芯片的驱动编写思路与调试方法。1. 串口不够用的时候WK2114 是怎么给 STM32F2 变出 4 路 UART 的一个板子上同时要跑两路传感器、一个 GNSS 模组和一个调试口数下来要五路串口可手里的 STM32F2 能腾出来的 USART 只有三个——这种「串口永远比外设少」的场景做嵌入式的应该都不陌生。这次拆的是一份 WK2114 串口拓展芯片驱动它把 4 路独立 UART 通过一组 I2C或 SPI引脚扩出来主控只剩一个总线接口的压力。这套驱动从芯片寄存器配置到 STM32F2 上的裸机收发都有完整实现适合正在给老旧板卡加串口、或打算用 WK2114 做多路采集的开发者照着跑通很容易读进去还能看到驱动分层的设计思路。2. 硬件与寄存器视角WK2114 的 4 路串口是怎么从一组 I2C 引脚变出来的2.1 一颗芯片怎么同时管四路串口引脚规划和内部资源WK2114 本质上是一个总线到 UART 的桥片。主控侧只接一组 I2C 或 SPI 从机接口芯片侧则引出 4 组独立的 TX/RX 引脚每组对应一路可独立配置的 UART。内部每个通道有自己的发送 FIFO、接收 FIFO、波特率分频器、中断状态寄存器所以四路的波特率、校验位、数据位可以完全不相关——这在多传感器采集场景里很实用比如一路跑 9600 的电表数据另一路跑 115200 的 GNSS 定位报文互不干扰。与 STM32F2 连接最少只需 6 根线。以 I2C 模式为例SCL、SDA 用于读写寄存器INTA 作为中断输出通知主控「有数据可达」MCLK 是芯片的工作时钟输入加上电源和地。A0、A1 两个引脚决定 I2C 从机地址的后两位基址一块板子最多可以挂 4 片 WK2114把串口总数扩到 16 路。SPI 模式则占用 SCLK、MOSI、MISO、CS 四根线跑高位宽数据时吞吐更稳代价是一片芯片独占一组 SPI 总线。引脚名方向功能MCLK输入外部工作时钟典型 14.7456 MHzSCL / SDA输入 / 双向I2C 从机接口INTA输出中断请求低有效TX0~TX3输出4 路 UART 发送RX0~RX3输入4 路 UART 接收A0 / A1输入I2C 从机地址选择I2C 地址这件事值得多写两句。常见配置是芯片出厂固定一个基地址A0、A1 各接地或接高拉出一个 2 位偏移组合出 4 个可选地址。A0、A1 都接地时地址最小逐次加大。实际调试时最好先写一个遍历地址的 I2C 扫描函数打印出所有能拿到 ACK 的地址再和 A0/A1 实际电平对照能省掉不少「为什么读不到芯片 ID」的排查时间。提示MCLK 不能省。WK2114 内部没有自激时钟UART 的波特率发生器完全依赖这颗外部时钟。典型接法是用 14.7456 MHz 晶振这个频率对 115200 及整数分波特率非常友好算分频系数时几乎不产生小数误差。选 I2C 还是 SPI主要看目标波特率。I2C 模式引脚省、支持多片级联对 115200 级别的多路传感器采集完全够用SPI 模式适合高波特率低延迟的场景比如把某一路串口提到 921600 甚至更高。如果只是接 9600、38400 这类常见外设I2C 模式显然处理起来更顺手。2.2 寄存器组织全局区、通道区、16550 风格的兼容设计寄存器组织带一点经典 16550 的影子。芯片级寄存器管复位、全局中断汇总、时钟分频和版本每路通道的寄存器组则沿袭串口芯片通用的 IER中断使能、IIR中断标识、FCRFIFO 控制、LCR线路控制、DLL/DLH波特率分频低高字节、THR/RBR发送保持/接收缓冲、LSR线路状态这一套。对从 STM32 标准库或 HAL 走过来的开发者这套寄存器名字不会陌生。寄存器类别典型成员作用全局芯片 ID、软件复位、全局中断状态判活、复位、中断汇总通道控制LCR、IER、IIR、FCR线路参数、中断开关、FIFO 触发通道数据DLL、DLH、THR、RBR、LSR波特率分频、数据收发、线路状态IER 位 0 置 1 表示「接收数据可用」时触发中断位 1 置 1 表示「发送 FIFO 空」时触发中断这俩是收发路径上最常打交道的。IIR 的位 0 在无中断时是 1读取时先判断这一位可以快速跳过无事件通道。LCR 的最高位是 DLAB除数锁存访问位访问 DLL/DLH 之前必须把它置 1写完后要恢复为 0否则数据会落到别的寄存器上。FCR 则用来开启 FIFO 并选择接收中断触发阈值常见档位是收到 1 字节、4 字节、8 字节还是 16 字节才拉一次中断。波特率公式沿用串口芯片经典算法Baud MCLK / (16 × Div)算分频系数的函数如下uint16_t wk2114_calc_div(uint32_t mclk_hz, uint32_t baud) { uint32_t div mclk_hz / (16u * baud); if (div 1) { div 1; } if (div 0xFFFF) { div 0xFFFF; } return (uint16_t)div; }说明16 是过采样倍数公式的意义是把 MCLK 分成单位时间内能采到的位时钟数。整数除法会悄悄舍掉小数直接用 12 MHz 算 115200 会得到 div6实际波特率 125000误差接近 8%接收端采样点逐渐偏出数据位必乱码。所以选 MCLK 时钟时把目标波特率的倍数关系先算一遍比事后调寄存器省心得多。驱动里我加了一个内部函数把实际波特率反算回来误差超过 ±2% 就打印告警。对 STM32F2 的接线还有三个小建议I2C 的 SCL/SDA 一定要接上拉电阻常见 4.7kΩ否则芯片 ACK 阶段会被拉低卡死MCLK 如果来自 MCU 的 MCO 引脚而不是晶振记得先确认 MCO 输出使能并等待时钟稳定否则芯片上电后一段时间完全不应答INTA 引脚建议接到 STM32 的 EXTI 输入并配内部上拉避免芯片未初始化时电平悬空导致误触发。3. 驱动核心拆解STM32F2 上 WK2114 从初始化到收发一帧数据的完整链路3.1 驱动分层传输抽象让 I2C 和 SPI 共用一套核心逻辑驱动没有把 I2C 和 SPI 各写一套而是拆成三层。核心的 WK2114 逻辑只认「读寄存器、写寄存器」两个操作底层是 I2C 还是 SPI 由回调函数决定这样一来换总线只需要换一个适配文件。项目里的文件组织如下wk2114.h // 对外 API、通道数据结构 wk2114.c // 初始化、中断入口、收发逻辑 wk2114_i2c.c // STM32 HAL 平台下的 I2C 读写适配 platform_config.h // 引脚号、MCLK 频率、通道数量、环形缓冲大小 test_wk2114.c // 回环测试与压力统计分层关系是应用层调用 wk2114 的收发 API核心驱动通过 read_reg/write_reg 回调落到传输适配层最终走 STM32 的 HAL I2C 外设。核心层看不到总线的存在以后换 SPI 或者换 MCU 平台核心代码一行都不用动。通道数据结构如下typedef struct wk2114_channel { uint8_t addr; /* I2C 从机地址 */ uint8_t ch_index; /* 0~3通道编号 */ uint32_t baud; /* 初始化时写入分频器 */ ringbuf_t rx_ring; /* 接收环形缓冲 */ SemaphoreHandle_t rx_sem; /* 接收信号量 */ } wk2114_channel_t; typedef struct wk2114 { uint8_t i2c_addr; uint32_t mclk_hz; int (*read_reg)(uint8_t addr, uint8_t reg, uint8_t *val); int (*write_reg)(uint8_t addr, uint8_t reg, uint8_t val); wk2114_channel_t ch[4]; } wk2114_t;说明read_reg/write_reg 的 addr 参数是 I2C 从机地址SPI 模式固定填 0 或忽略。通道结构体里存了环形缓冲和 RTOS 信号量为的是把中断上下文里的数据搬移和应用任务的数据消费彻底解耦业务代码不需要关心数据是哪个瞬间进芯片的。3.2 初始化从地址探测到四路串口全部就绪初始化顺序有讲究第一步先判活再复位最后逐通道配参数避免在一堆未定义状态下写寄存器。int wk2114_init(wk2114_t *dev) { uint8_t chip_id 0; if (dev-read_reg(dev-i2c_addr, WK2114_REG_ID, chip_id) ! 0) { return -1; /* 读不到说明芯片不在线 */ } dev-write_reg(dev-i2c_addr, WK2114_REG_SWRST, 0x01); vTaskDelay(pdMS_TO_TICKS(2)); for (int ch 0; ch 4; ch) { uint8_t lcr 0x03; /* 8 数据位无校验1 停止位 */ dev-write_reg(dev-i2c_addr, WK2114_REG_LCR ch * CH_OFFSET, lcr | 0x80); /* DLAB1 */ uint16_t div wk2114_calc_div(dev-mclk_hz, dev-ch[ch].baud); dev-write_reg(dev-i2c_addr, WK2114_REG_DLH ch * CH_OFFSET, div 8); dev-write_reg(dev-i2c_addr, WK2114_REG_DLL ch * CH_OFFSET, div 0xFF); dev-write_reg(dev-i2c_addr, WK2114_REG_LCR ch * CH_OFFSET, lcr); /* DLAB0 */ dev-write_reg(dev-i2c_addr, WK2114_REG_FCR ch * CH_OFFSET, 0x01 | 0x10); dev-write_reg(dev-i2c_addr, WK2114_REG_IER ch * CH_OFFSET, 0x03); } return 0; }第一步读芯片 ID本质上是一次「我在不在线」的握手读不到直接 return -1 比后面一堆寄存器白写强得多。第二步写软件复位把芯片恢复到已知初态之后延时 2ms 等内部逻辑稳定。第三步进入通道初始化循环每个通道按同样的顺序做四件事LCR 写线路参数并置 DLAB1、写分频高低字节、恢复 DLAB0、配置 FIFO 和中断使能。FCR 里 0x01 是开启 FIFO0x10 表示接收中断在 16 字节触发这个值直接决定了后面中断进来的频率。这里最容易写成「四路配置一样就只写一遍」——实际每路的波特率来自不同的配置项代码要从一个 baud_table[4] 数组取而不是用同一个变量。上电默认寄存器值不总为 0强烈建议初始化开头先走一遍软件复位否则 DLAB 位状态不明初次写波特率会失败得毫无头绪。3.3 收发热路径EXTI 中断、环形缓冲和 RTOS 任务唤醒接收的完整链路是芯片收到数据→FIFO 达到触发阈值→INTA 拉低→STM32 EXTI 中断→中断函数读 IIR 判断事件类型→读 LSR 拿到可读字节数→批量读 RBR 进环形缓冲→给接收任务发二值信号量→任务消费数据。中断处理函数长这样void wk2114_irq_handler(wk2114_t *dev) { BaseType_t higher pdFALSE; for (int ch 0; ch 4; ch) { uint8_t iir 0; do { dev-read_reg(dev-i2c_addr, WK2114_REG_IIR ch * CH_OFFSET, iir); if (iir 0x01) { break; /* 无挂起中断 */ } if (iir 0x04) { /* 接收数据可用 */ uint8_t lsr 0; dev-read_reg(dev-i2c_addr, WK2114_REG_LSR ch * CH_OFFSET, lsr); while (lsr 0x01) { uint8_t byte 0; dev-read_reg(dev-i2c_addr, WK2114_REG_RBR ch * CH_OFFSET, byte); ringbuf_put(dev-ch[ch].rx_ring, byte); dev-read_reg(dev-i2c_addr, WK2114_REG_LSR ch * CH_OFFSET, lsr); } } if (iir 0x02) { /* 发送 FIFO 空 */ wk2114_flush_tx(dev-ch[ch]); } } while (!(iir 0x01)); } xSemaphoreGiveFromISR(dev-rx_sem, higher); portYIELD_FROM_ISR(higher); }外层循环遍历 4 个通道内层用 do-while 把所有待处理中断清完再退出。IIR 的位 0 是「是否有挂起中断」标志为 1 表示当前没有事件可以 break。接收分支里反复读 LSR 并检查位 0为 1 就继续读一个字节进环形缓冲读到 0 才停这样一次中断最多把 FIFO 里所有数据搬完。发送分支调用 wk2114_flush_tx 把之前没写下的残余数据补写进 THR。最后统一给一次信号量——这是关键否则每读一个字节给一次信号量接收任务会被高频唤醒上下文切换开销直接抹掉 FIFO 带来的收益。接收侧任务端则是典型的「等信号量-搬数据」模式void rx_task(void *arg) { wk2114_t *dev (wk2114_t *)arg; while (1) { xSemaphoreTake(dev-rx_sem, portMAX_DELAY); for (int ch 0; ch 4; ch) { wk2114_channel_t *c dev-ch[ch]; while (ringbuf_available(c-rx_ring) 0) { uint8_t byte ringbuf_get(c-rx_ring); handle_chunk(ch, byte); /* 业务侧协议解析入口 */ } } } }任务被信号量唤醒后一次性消费完所有环形缓冲数据再回到等待状态。handle_chunk 是业务侧的协议解析入口比如按帧头拆包、校验、分发。环形缓冲的大小建议按最大报文长度的 2 倍去配太小会丢太大浪费 RAM——对 STM32F2 这种内存 128KB 起步的芯片给每个通道 512 字节环形缓冲通常够用。发送路径相对简单应用层调 wk2114_write 时先查剩余 FIFO 空间能塞多少塞多少塞不完的记录 pending 长度等发送 FIFO 空中断进来再补这部分逻辑在 wk2114_flush_tx 里完成。4. 调试避坑波特率乱码、丢字节、中断风暴的几个实战问题调试串口扩展芯片时最容易踩的坑集中在三个地方总线层、时钟层、中断层。下面四条是实际调过的按从「板子没反应」到「数据链路不稳」的顺序排开。4.1 I2C 扫描不到设备地址和时钟都可能是元凶现象代码里读芯片 ID 一直返回 -1逻辑分析仪看 SCL 有波形但芯片全程没有回 ACK。原因最常是 A0/A1 引脚电平没焊对导致从机地址和程序里写死的常量不一致I2C 总线上永远找不到目标。另一个隐蔽原因是 MCLK 没起振——WK2114 的 I2C 从机状态机依赖外部时钟晶体虚焊或者 MCO 未使能芯片连 ACK 都吐不出来。解决先拿示波器量 MCLK 引脚确认有时钟再用一个 I2C 扫描例程遍历 0x08~0x77 全都打印一遍把实际 ACK 地址找出来。用实际地址替换常量后能读到芯片 ID基本就脱离了玄学范畴。批量生产的板子建议在初始化失败时打印错误码区分「无 ACK」和「ID 不匹配」而不是统一报 -1。4.2 波特率不匹配全是乱码整数分频误差是隐形杀手现象115200 打印一切正常换 57600 报文的头部多一个错字符长报文尾巴丢数据。原因MCU 侧和 WK2114 两侧波特率用的同一个分频公式但 MCU 的 USART 可以从系统时钟精确分频得到目标值WK2114 的 MCLK 若是非友好频率整数分频后实际波特率偏差会超出接收端容忍范围。12MHz 时钟算 115200分频 6实际 125000偏差超 8%接收端采样点会逐渐偏出数据位。解决检查 MCLK 选择而不是去调寄存器。14.7456MHz 对 115200、57600、9600 都是整数关系换上之后不用改代码就稳定。驱动里我加了一段启动自检把每个通道的目标波特率和实际波特率回算一遍误差超过 ±2% 直接告警——从那以后几乎没再被「换了波特率就乱码」这种事绊住。4.3 长报文后面丢字节FIFO 触发级别太低加上中断里逐字节读现象短帧收发没问题连续 100 字节以上的数据流末尾总是少几个字节。原因FCR 把接收中断触发级别设成了 1 字节意味着每个字节都产生一次中断中断服务里再逐字节读寄存器配合信号量上下文切换单位时间能处理的事件数跟不上 115200 的速率FIFO 溢出后新数据覆盖旧数据。解决触发级别调到 16 字节FCR 对应位中断函数里用「读一次 LSR、循环读干净 FIFO」的方式批量搬数据。再把环形缓冲加大到两倍最大帧长任务侧一次消费完。同样 115200 波特率下中断频率下降了十几倍丢字节的问题随之消失。4.4 中断风暴和完全不触发EXTI 边沿与 IIR 清除时序现象有时 INTA 一拉低就再也回不去程序卡在中断里出不来有时外部中断进不去接收全靠轮询。原因WK2114 的中断输出是电平型低电平保持到 IIR 事件被清。如果 STM32 的 EXTI 配了上升沿触发这个低电平永远产生不了边沿。反过来中断服务里没把所有事件读干净就返回芯片电平不释放EXTI 即使配了下降沿也要等下一次下降沿才能再触发表现为丢中断。解决EXTI 配下降沿触发中断函数里 do-while 把 IIR 读到位 0 为 1 再退出清完事件后在末尾补一次 dummy 读确保芯片内部清除逻辑完成。四个通道共用一个 INTA 时禁止在某个通道处理完就 return必须四个通道全扫一遍再统一返回。5. 二次开发把四通道封装成设备对象并接入 RTOS 设备模型5.1 从数组索引到设备对象现在的驱动里通道以 ch[0]~ch[3] 暴露给应用业务代码要传索引耦合感很强。我一般会把每个通道包一层设备对象注册进 RTOS 的设备表应用侧只认名字typedef struct { const char *name; /* uart_ext_0 这类命名 */ wk2114_channel_t *chan; /* 指向通道实例 */ int (*write)(struct wk_dev *dev, const uint8_t *buf, size_t len); int (*read)(struct wk_dev *dev, uint8_t *buf, size_t max_len); } wk_dev_t; static wk_dev_t ext_uart[4]; void wk_dev_register(int idx, const char *name) { ext_uart[idx].name name; ext_uart[idx].chan wk2114_inst.ch[idx]; ext_uart[idx].write wk_dev_write_impl; ext_uart[idx].read wk_dev_read_impl; }改造要点有三个一是每个通道配一把互斥锁多任务同时 write 时保护寄存器操作的原子性二是错误返回码要统一别一会儿 -1 一会儿返回 0上层才能放心处理超时和 FIFO 满三是这些结构体放静态区不要运行时 malloc避免内存碎片。5.2 回环验证先让数据自己跑一圈改造完成后别直接接外设先做回环测试——把某通道 TX 和 RX 用杜邦线短接任务定时发带递增序号的数据帧接收侧校验序号连续性uint8_t tx[4] {0xAA, (i 8) 0xFF, i 0xFF, 0x55}; wk_write(ext_uart[0], tx, sizeof(tx)); uint8_t rx[4]; if (wk_read(ext_uart[0], rx, sizeof(rx)) sizeof(rx) rx[0] 0xAA rx[3] 0x55) { seq_error (rx[2] ! i); }序号断了说明并发改造引入问题没断再谈业务。这套验证跑完后我习惯再连续发 10 万帧做压力统计收尾看误码率。从那以后我每次改驱动分层或者换 MCLK 时钟都强制先跑一轮回环计数再去接真实外设这套流程帮我挡掉过好几回「看似正常、一上业务就丢帧」的翻车现场。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号