WK2114串口拓展芯片驱动
在现代嵌入式系统中,设备对通信接口的需求正变得越来越复杂。无论是工业控制柜里的多传感器接入、智能网关对接各类协议终端,还是边缘计算节点与外设频繁交互, UART资源不足 已成为许多MCU项目开发中的“老痛点”。尤其是一些成本敏感或空间受限的设计,主控芯片往往只集成1~2个硬件串口,而实际应用却需要连接蓝牙模块、电表、调试端口、RS485总线设备等多个串行外设。
这时候,开发者通常面临两个选择:换用更高阶的SoC(代价高),或者引入专用的 多串口扩展芯片 。后者显然更具性价比和灵活性。近年来,国产芯片厂商在这方面进步显著,其中无锡力源微电子推出的 WK2114 就是一款极具代表性的四通道UART桥接芯片。它通过SPI或I²C接口挂载到主控,即可轻松提供4路独立全双工串口,在性能、集成度和生态支持上都表现出色。
这颗小小的QFN-24封装芯片,究竟值不值得放进你的BOM清单?我们不妨从工程实践的角度深入聊聊它的设计逻辑、驱动实现的关键细节,以及如何避免踩坑。
为什么是WK2114?
先来看一组对比数据。传统常用的多串口扩展方案如NXP的SC16IS752,虽然稳定可靠,但存在几个明显短板:FIFO深度仅64字节,面对突发数据容易丢包;供货周期不稳定;价格偏高。而WK2114作为国产替代方案,不仅引脚兼容部分国际型号,还在关键指标上做了增强:
- 每通道128字节FIFO —— 是SC16IS752的两倍,大幅降低中断频率;
- 最高3Mbps波特率支持 —— 足以应对高速TTL通信场景;
- 内置可编程波特率发生器 —— 精度优于软件模拟,减少误码;
- 支持SPI(最高20MHz)和I²C双模式 —— 接口灵活,适应不同主控平台;
- 完整的错误检测机制(帧错误、奇偶校验、溢出等);
- 提供官方HAL库示例,尤其对STM32系列支持力度较好。
更关键的是,其小封装(4×4 mm QFN)非常适合PCB空间紧张的应用,比如手持终端、小型网关、模块化IO板卡等。
工作机制:主从协同的通信桥梁
WK2114本质上是一个“协议翻译器”+“硬件UART阵列”。它本身不具备运行程序的能力,而是作为主控MCU的一个从设备存在。整个系统采用典型的主从架构:
- 主控(Host)负责发起所有配置和数据操作;
- WK2114作为从机响应命令,并管理四个物理UART通道的数据收发;
- 数据流向完全由寄存器控制,内部状态机自动完成帧处理、FIFO调度和中断触发。
举个例子:当你想通过WK2114的UART1向某个RS485设备发送指令时,流程如下:
- MCU通过SPI写入目标通道(Channel=1)的THR寄存器;
- 数据进入该通道的发送FIFO;
- WK2114自动按设定波特率将并行数据转为串行信号输出;
- 对端设备回复后,接收数据被存入RX FIFO;
- 当接收数据达到预设门限(如8字节),WK2114拉高中断引脚;
- MCU响应中断,读取RHR寄存器获取数据。
这个过程中,主控无需参与每一帧的位定时,极大减轻了CPU负担。这也是为何即使在资源有限的Cortex-M3/M4平台上,也能实现稳定的多串口并发通信。
寄存器模型:一切皆可通过寄存器控制
WK2114的所有功能都围绕一组寄存器展开。理解这些寄存器的工作方式,是编写稳定驱动的前提。以下是核心寄存器摘要:
| 地址 | 名称 | 功能说明 |
|---|---|---|
| 0x00 | RHR/THR | 接收/发送数据寄存器 |
| 0x01 | IER | 中断使能控制 |
| 0x02 | FCR | FIFO使能与复位 |
| 0x03 | LCR | 数据位、停止位、校验设置 |
| 0x05 | LSR | 线路状态(含OE/PE/FE/Break) |
| 0x09 | TLR | FIFO中断触发级别(RX/TX) |
| 0x20/0x21 | DLL/DLH | 波特率分频系数(需先置LCR[7]=1) |
值得注意的是, 所有寄存器访问前必须先选择通道 。这是通过一个全局寄存器 CAR (Channel Address Register)实现的,但在SPI命令格式中已隐式包含——即每次读写操作的命令字本身就携带了通道信息。
以SPI为例,命令字结构如下:
[Bit7: Read=1 / Write=0]
[Bit6~4: Channel ID (0~3)]
[Bit3~0: Register Address]
这意味着你不需要额外发送“切换通道”的指令,只要在每次通信时正确构造命令头即可。
驱动实现:三层架构更易维护
为了提升代码可移植性和复用性,建议将驱动分为三层:
+------------------+
| 应用层 | ← 用户调用 open/write/read/close
+------------------+
| 驱动核心层 | ← 波特率设置、FIFO读写、中断处理
+------------------+
| 硬件抽象层(HAL)| ← SPI/I²C底层读写、延时函数
+------------------+
硬件抽象层:SPI读写是基础
以下是一个基于STM32 HAL库的SPI读写实现:
// spi_wk2114.h
#ifndef __SPI_WK2114_H__
#define __SPI_WK2114_H__
#include "stm32f4xx_hal.h"
#define WK2114_SPI hspi1
#define WK2114_CS_PIN GPIO_PIN_4
#define WK2114_CS_PORT GPIOA
void WK2114_CS_Select();
void WK2114_CS_Deselect();
uint8_t WK2114_ReadReg(uint8_t channel, uint8_t reg);
void WK2114_WriteReg(uint8_t channel, uint8_t reg, uint8_t value);
void WK2114_ReadData(uint8_t channel, uint8_t *buf, uint8_t len);
void WK2114_WriteData(uint8_t channel, const uint8_t *buf, uint8_t len);
#endif
// spi_wk2114.c
#include "spi_wk2114.h"
void WK2114_CS_Select() {
HAL_GPIO_WritePin(WK2114_CS_PORT, WK2114_CS_PIN, GPIO_PIN_RESET);
}
void WK2114_CS_Deselect() {
HAL_GPIO_WritePin(WK2114_CS_PORT, WK2114_CS_PIN, GPIO_PIN_SET);
}
uint8_t WK2114_ReadReg(uint8_t channel, uint8_t reg) {
uint8_t tx_data[2], rx_data[2];
WK2114_CS_Select();
tx_data[0] = 0x80 | (channel << 4) | reg; // 读命令
tx_data[1] = 0x00;
HAL_SPI_TransmitReceive(&WK2114_SPI, tx_data, rx_data, 2, 100);
WK2114_CS_Deselect();
return rx_data[1];
}
void WK2114_WriteReg(uint8_t channel, uint8_t reg, uint8_t value) {
uint8_t tx_data[2];
WK2114_CS_Select();
tx_data[0] = (channel << 4) | reg; // 写命令
tx_data[1] = value;
HAL_SPI_Transmit(&WK2114_SPI, tx_data, 2, 100);
WK2114_CS_Deselect();
}
这段代码看似简单,但有几个实战经验需要注意:
- CS片选必须严格控制 :不能与其他SPI设备共用而不隔离,否则可能造成命令错乱;
- SPI模式应设为Mode 0(CPOL=0, CPHA=0) ,这是WK2114默认要求;
- 传输超时不宜过短 :尤其是在中断上下文中调用时,避免因总线阻塞导致HardFault;
- 若使用DMA,注意缓冲区对齐问题(特别是接收连续流数据时)。
初始化:别忘了DLAB位!
配置一个UART通道的核心在于初始化函数。下面是一个典型实现:
void WK2114_UART_Init(uint8_t channel, uint32_t baudrate, uint8_t data_bits, uint8_t parity, uint8_t stop_bits) {
uint8_t lcr = 0;
uint16_t baud_div;
// 步骤1:开启DLL/DMODE写权限
WK2114_WriteReg(channel, 0x07, 0x80); // 设置LCR[7]=1
// 步骤2:计算并设置波特率(假设输入时钟为16MHz)
baud_div = (16000000 / 16) / baudrate;
WK2114_WriteReg(channel, 0x20, baud_div & 0xFF); // DLL
WK2114_WriteReg(channel, 0x21, (baud_div >> 8) & 0xFF); // DLH
// 步骤3:恢复LCR并配置数据格式
WK2114_WriteReg(channel, 0x07, 0x00); // 清除DLAB位
switch (data_bits) {
case 5: lcr |= 0x00; break;
case 6: lcr |= 0x01; break;
case 7: lcr |= 0x02; break;
case 8: lcr |= 0x03; break;
default: lcr |= 0x03; break;
}
if (parity == 1) lcr |= 0x18; // 奇校验
else if (parity == 2) lcr |= 0x1A; // 偶校验
if (stop_bits == 2) lcr |= 0x04; // 注意:仅当数据位<8时有效
WK2114_WriteReg(channel, 0x03, lcr);
// 步骤4:启用FIFO并设置中断阈值
WK2114_WriteReg(channel, 0x02, 0x07); // 使能FIFO,复位TX/RX
WK2114_WriteReg(channel, 0x09, 0x08); // RX IRQ当有8字节时触发
// 步骤5:使能接收中断(可选)
WK2114_WriteReg(channel, 0x01, 0x01);
}
这里最容易出错的地方就是 忘记先设置LCR[7]=1再写DLL/DLH 。如果不这样做,波特率根本不会生效,而且问题很难排查——现象往往是通信乱码或完全不通。建议在调试初期加入寄存器回读验证步骤。
收发函数:轮询 vs 中断
最简单的数据收发可以用轮询方式实现:
int WK2114_ReadData(uint8_t channel, uint8_t *buf, uint8_t maxlen) {
uint8_t lsr = WK2114_ReadReg(channel, 0x05);
uint8_t count = 0;
if (lsr & 0x01) {
while ((lsr & 0x01) && count < maxlen) {
buf[count++] = WK2114_ReadReg(channel, 0x00);
lsr = WK2114_ReadReg(channel, 0x05);
}
}
return count;
}
void WK2114_WriteData(uint8_t channel, const uint8_t *buf, uint8_t len) {
for (int i = 0; i < len; i++) {
while (!(WK2114_ReadReg(channel, 0x05) & 0x20)); // 等待THR空
WK2114_WriteReg(channel, 0x00, buf[i]);
}
}
虽然可行,但在高吞吐量场景下会严重占用CPU。更好的做法是结合中断:
- 在初始化时使能IER[0](接收就绪中断);
- 外部中断线连接WK2114的INT引脚;
- 在ISR中判断哪个通道触发中断(读MSR或轮询LSR);
- 将数据搬运至环形缓冲区,交由任务或线程处理。
若追求极致效率,还可配合DMA使用——不过WK2114本身不支持DMA直连,需借助MCU的SPI DMA间接实现,适用于大批量下行广播类场景。
实际应用中的设计要点
在一个真实项目中,仅仅让芯片“跑起来”还不够,还需考虑稳定性与可维护性。以下是几个常见问题及应对策略:
✅ 电源去耦不可省略
在VCC引脚附近放置 0.1μF陶瓷电容 + 10μF钽电容 组合,能有效抑制高频噪声。曾有案例因省掉滤波电容导致串口偶发性重启。
✅ 晶体精度影响波特率误差
推荐使用±10ppm精度的16MHz晶振。若使用内部RC振荡器,长期温漂可能导致通信失败,尤其在长距离RS485传输中更为敏感。
✅ RS485方向控制要可靠
若用于半双工RS485通信,DE/RE引脚可用GPIO控制,但要注意延时匹配。经验法则是:
// 发送前使能
HAL_GPIO_WritePin(DE_GPIO, DE_PIN, GPIO_PIN_SET);
us_delay(10); // 等待驱动器准备好
WK2114_WriteData(...);
// 发送完成后延迟关闭
while (!tx_fifo_empty()); // 确保最后一字节发出
HAL_GPIO_WritePin(DE_GPIO, DE_PIN, GPIO_PIN_RESET);
也可以选用带自动方向识别的收发电路,简化软件逻辑。
✅ 中断优先级合理分配
如果多个通道同时工作且速率较高(如>115200bps),建议将WK2114中断设为较高优先级,防止FIFO溢出。但也要避免抢占RTOS内核或其他关键任务。
结语
WK2114并非完美无缺——例如缺乏高级流量控制(RTS/CTS)、不支持IrDA或调制解调器信号完整集,但它精准地击中了大多数嵌入式项目的“甜点区间”:够用、稳定、便宜、易于集成。
更重要的是,随着国产芯片生态逐步成熟,像WK2114这样的器件正在改变以往“缺接口只能进口”的被动局面。对于工程师而言,掌握这类桥接芯片的驱动开发能力,不仅能解决眼前的设计瓶颈,更是构建高可靠性系统的必备技能之一。
未来,随着RISC-V平台普及和实时操作系统广泛应用,我们或许会看到更多类似WK2114的国产桥接芯片走向模块化、可配置化,甚至支持动态通道分配和虚拟串口映射。而现在,正是打好基础的时候。

5062

被折叠的 条评论
为什么被折叠?



