简介:这份资源面向嵌入式开发工程师与STM32学习者,提供WK2114串口扩展芯片的驱动实现,用于解决STM32F2系列微控制器UART接口不足、难以同时与多个外设进行串行通信的问题,适用于物联网设备、工业自动化等需要多路串口通信的场景。压缩包共2个文件,包含1个c源文件与1个h头文件,整体约4KB,其中源文件负责WK2114的初始化、波特率与数据格式设置、控制信号及收发数据等函数实现,头文件则定义函数原型、常量与结构体,便于其他模块调用与编译。目前已有1263人学习下载。通过该驱动,读者可掌握WK2114芯片与STM32F2 UART1的配合方式、HAL库或寄存器操作思路,以及串口扩展的测试与调试方法,为多串口嵌入式项目提供可直接复用的代码基础。1. 从一颗冷门串口芯片说起:WK2114 到底能解决什么问题去年帮一个做工业网关的朋友调板子,STM32F207 上已经挂了 4 路 UART,结果项目临时要接 8 个 RS232 传感器,主控的串口资源直接见底。换 MCU 不现实,软件模拟串口又扛不住 115200 的稳定收发。最后选了一颗 WK2114 串口拓展芯片,一颗 SPI 挂上去,硬生生多出 4 路独立 UART,问题当场解决。这份资源就是围绕 WK2114 在 STM32F2 平台上的驱动实现,包含寄存器操作、SPI 通信封装、中断收发和环形缓冲区管理。适合正在做多串口扩展、工业采集、嵌入式网关的开发者,尤其是主控串口不够用又不想换方案的人。它不是什么通用库,而是一份能直接嵌进你工程里的驱动代码,改改引脚和 SPI 句柄就能跑。2. 芯片与平台选型:WK2114 凭什么挂在 STM32F2 上2.1 WK2114 的定位与关键特性WK2114 是一颗四通道 UART 扩展芯片,通过 SPI 或并口与主控通信。它内部集成了 4 个独立的 UART 控制器,每个通道都有独立的波特率发生器、收发 FIFO 和中断控制。对主控来说,它就是一个 SPI 从设备,你往对应寄存器写数据,它帮你把串口时序全部搞定。关键特性直接决定驱动怎么写:通信接口:SPI 从模式,最高时钟支持到 10MHz 左右,STM32F2 的 SPI1 跑在 18MHz 以下都没问题通道数:4 路独立 UART,每路可单独配置波特率、数据位、停止位、校验位FIFO:每通道收发各 16 字节,减少主控中断频率中断输出:一根 IRQ 引脚,低电平有效,主控通过读中断状态寄存器判断是哪个通道触发供电:3.3V,和 STM32F2 电平直接匹配,不需要电平转换我一般会先确认板子上 WK2114 的片选接在哪个 GPIO,IRQ 接在哪个 EXTI 线上,这两根线定下来,驱动框架就清晰了。2.2 为什么选 STM32F2 而不是 F1/F4STM32F2 系列在工业现场很常见,Cortex-M3 内核,主频 120MHz,SPI 外设支持 8 位和 16 位数据帧,DMA 通道也够用。相比 F1,F2 的 SPI 时钟更高,配合 WK2114 的 FIFO 可以做到更低的 CPU 占用;相比 F4,F2 的外设寄存器布局更简单,驱动代码不需要处理太多缓存一致性问题。常见做法是 SPI1 跑在 9MHz 左右,用 DMA 发送、中断接收。WK2114 的 SPI 时序是标准模式 0,CPOL=0,CPHA=0,MSB 先行。STM32F2 的 SPI_CR1 寄存器里把 CPOL 和 CPHA 都清零就行。2.3 硬件连接与引脚分配以某工业采集板为例,典型连接如下:WK2114 引脚STM32F2 引脚说明SCLKPA5 (SPI1_SCK)SPI 时钟MOSIPA7 (SPI1_MOSI)主出从入MISOPA6 (SPI1_MISO)主入从出CSPA4 (GPIO)软件片选,低有效IRQPB0 (EXTI0)中断输出,低有效RSTPB1 (GPIO)复位,低有效片选我习惯用软件控制,因为 WK2114 的 SPI 事务之间需要拉高 CS 来锁存数据,硬件 NSS 有时候时序不好控。IRQ 接 EXTI 下降沿触发,进中断后先读 WK2114 的中断状态寄存器,再决定处理哪个通道。3. 驱动架构拆解:SPI 通信层与 UART 通道管理3.1 分层设计思路驱动分成三层:最底层是 SPI 读写字节函数,中间层是 WK2114 寄存器读写封装,最上层是 4 个 UART 通道的收发管理。这样分层的好处是,换主控平台只需要改 SPI 底层,换芯片只需要改寄存器定义。目录结构大致如下:wk2114_driver/ ├── inc/ │ ├── wk2114.h // 寄存器定义、结构体、API 声明 │ └── wk2114_port.h // 平台相关宏,SPI 句柄、CS/IRQ 引脚 ├── src/ │ ├── wk2114.c // 寄存器读写、初始化、中断处理 │ └── wk2114_uart.c // 4 通道收发、环形缓冲区 └── example/ └── main.c // 初始化示例、收发测试3.2 SPI 读写与寄存器操作WK2114 的寄存器地址是 8 位,数据也是 8 位。SPI 事务格式:先发地址字节,最高位表示读/写(1 读 0 写),然后跟数据字节。下面是我常用的两个基础函数:// wk2114.c #include "wk2114.h" #include "wk2114_port.h" // 写一个寄存器 void WK2114_WriteReg(uint8_t addr, uint8_t data) { WK2114_CS_LOW(); // 片选拉低 SPI_TransferByte(addr & 0x7F); // 最高位 0 表示写 SPI_TransferByte(data); // 写入数据 WK2114_CS_HIGH(); // 片选拉高,锁存 } // 读一个寄存器 uint8_t WK2114_ReadReg(uint8_t addr) { uint8_t val; WK2114_CS_LOW(); SPI_TransferByte(addr | 0x80); // 最高位 1 表示读 val = SPI_TransferByte(0xFF); // 发哑元,收数据 WK2114_CS_HIGH(); return val; }逻辑说明:addr & 0x7F把地址最高位清零表示写操作,addr | 0x80置一表示读操作。SPI_TransferByte是平台相关函数,STM32F2 上可以用标准库的SPI_I2S_SendData加等待SPI_I2S_GetFlagStatus实现,也可以用 HAL 的HAL_SPI_TransmitReceive。片选必须在每个事务结束后拉高,否则 WK2114 不会锁存数据,这是血泪经验,早期调试时数据写不进去就是 CS 忘了拉高。参数说明:addr范围 0x00~0x7F,data是 8 位寄存器值。读操作时发 0xFF 是因为 SPI 全双工,必须给时钟才能收到数据,哑元值无所谓。3.3 通道初始化与波特率配置WK2114 每个通道有一组寄存器:波特率分频低字节、高字节、线路控制寄存器、FIFO 控制寄存器。初始化时先软复位,再逐通道配置。// 初始化 WK2114,配置 4 个通道 void WK2114_Init(uint32_t baudrate) { WK2114_RST_LOW(); // 硬件复位 Delay_ms(10); WK2114_RST_HIGH(); Delay_ms(10); // 全局中断使能,FIFO 使能 WK2114_WriteReg(WK2114_REG_GLOBAL_CTRL, 0x01); for (uint8_t ch = 0; ch < 4; ch++) { uint16_t divisor = WK2114_CLK_FREQ / (baudrate * 16); WK2114_WriteReg(WK2114_REG_BR_L(ch), divisor & 0xFF); WK2114_WriteReg(WK2114_REG_BR_H(ch), (divisor >> 8) & 0xFF); // 8 数据位,1 停止位,无校验,使能收发 FIFO WK2114_WriteReg(WK2114_REG_LCR(ch), 0x03); WK2114_WriteReg(WK2114_REG_FCR(ch), 0x07); // 使能接收中断 WK2114_WriteReg(WK2114_REG_IER(ch), 0x01); } }逻辑说明:WK2114_CLK_FREQ是芯片内部基准时钟,常见为 14.7456MHz 或 7.3728MHz,具体看板子晶振。分频公式是基准时钟 / (波特率 * 16),算出 16 位分频值。LCR寄存器 0x03 表示 8N1,FCR寄存器 0x07 表示使能 FIFO 并清空收发缓冲。IER寄存器 0x01 表示接收数据可用中断。参数说明:baudrate支持 9600、19200、38400、57600、115200 等标准值。如果波特率有偏差,优先检查基准时钟频率是否和实际晶振一致,这个坑我踩过,板子焊的是 7.3728MHz 但代码里写 14.7456MHz,结果所有通道波特率翻倍,数据全是乱码。3.4 中断处理与环形缓冲区WK2114 的 IRQ 引脚是 4 个通道共享的,进中断后必须读全局中断状态寄存器,判断是哪个通道有数据,然后读该通道的接收寄存器,塞进对应的环形缓冲区。// 环形缓冲区结构 typedef struct { uint8_t buf[256]; volatile uint16_t head; volatile uint16_t tail; } RingBuf_t; static RingBuf_t rx_buf[4]; // EXTI 中断服务函数 void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { uint8_t int_status = WK2114_ReadReg(WK2114_REG_INT_STATUS); for (uint8_t ch = 0; ch < 4; ch++) { if (int_status & (1 << ch)) { while (WK2114_ReadReg(WK2114_REG_LSR(ch)) & 0x01) { uint8_t data = WK2114_ReadReg(WK2114_REG_RBR(ch)); uint16_t next = (rx_buf[ch].head + 1) % 256; if (next != rx_buf[ch].tail) { rx_buf[ch].buf[rx_buf[ch].head] = data; rx_buf[ch].head = next; } } } } EXTI_ClearITPendingBit(EXTI_Line0); } }逻辑说明:INT_STATUS寄存器的 bit0~bit3 分别对应 4 个通道。LSR寄存器的 bit0 是数据就绪标志,循环读到没有数据为止。环形缓冲区用 head 和 tail 两个索引,head 由中断写,tail 由主循环读,不需要关中断,因为单生产者单消费者模型下读写各自索引不会冲突。参数说明:缓冲区大小 256 字节,可以根据实际数据量调整。如果波特率 115200 且数据连续,建议至少 512 字节,否则主循环处理慢会丢数据。WK2114_REG_LSR的 bit0 是 DR(Data Ready),bit1 是 OE(Overrun Error),如果发现 OE 置位说明缓冲区满了,需要加大缓冲区或提高主循环读取频率。4. 避坑与排查:调 WK2114 时最容易翻车的几个点4.1 数据写进去读出来全是 0xFF现象:初始化后读任何寄存器都返回 0xFF,写进去的值也读不回来。原因:SPI 模式不对,或者 MISO 线没接好。WK2114 要求 SPI 模式 0,如果 CPHA 设成 1,采样边沿错了,读出来全是高电平。另外如果 MISO 虚焊,主控读到的就是浮空的高电平。解决:用示波器看 SCLK 和 MOSI,确认时钟空闲低、第一个边沿采样。检查 MISO 焊接。STM32F2 的 SPI_CR1 寄存器里 CPOL=0、CPHA=0,BR 分频先设大一点,比如 64 分频,低速调通再提速。4.2 能读能写但串口发不出数据现象:寄存器读写正常,但外部串口工具收不到任何数据。原因:通道的发送使能没开,或者波特率分频算错。WK2114 的 LCR 寄存器里除了数据位停止位,还有一位是发送使能,有些型号需要单独置位。另外分频值算错会导致波特率偏差太大,接收端识别不了。解决:先确认 LCR 的值,8N1 通常是 0x03,如果芯片手册要求发送使能位,就改成 0x43。然后用示波器测 TX 引脚,看有没有波形,有波形但数据错就是波特率问题,重新算分频值。4.3 中断进不去或者进去出不来现象:IRQ 引脚一直是低电平,或者中断触发一次后再也不触发。原因:中断状态寄存器没清,或者 EXTI 触发边沿设成了双边沿。WK2114 的 IRQ 是电平方式,低电平表示有中断,读走数据后自动变高。如果 EXTI 设成下降沿触发,但中断服务里没读数据,IRQ 一直低,就不会再有下降沿。解决:EXTI 配置成下降沿触发,中断服务里必须读到 LSR 的 DR 位为 0 才退出。另外确认 WK2114 的全局中断使能位已经打开,有些批次芯片默认是关闭的。4.4 多通道同时收发时数据串扰现象:通道 0 收到的数据跑到了通道 1 的缓冲区里。原因:读接收寄存器时地址算错,或者中断状态判断时通道索引搞混。WK2114 的通道寄存器地址是基址加通道偏移,如果偏移量算错,读的就是隔壁通道的数据。解决:把通道基址定义成宏,比如#define WK2114_REG_RBR(ch) (0x00 + (ch) * 0x08),确保每个通道的寄存器块间隔正确。中断里用for循环遍历 4 个通道时,索引和寄存器地址要对应上。4.5 高速收发时丢数据现象:115200 波特率下连续发几百字节,接收端偶尔少几个字节。原因:环形缓冲区太小,或者主循环读取太慢。WK2114 的 FIFO 只有 16 字节,如果主循环不及时读,FIFO 溢出就丢数据。解决:加大环形缓冲区到 512 或 1024 字节,主循环里用while循环把缓冲区数据全部读走再处理。如果还丢,考虑用 DMA 把 SPI 接收也做成自动搬运,减少中断延迟。5. 二次开发与验证:从能跑到好用的改造思路5.1 把驱动移植到其他 STM32 系列这份驱动的平台相关部分集中在wk2114_port.h和 SPI 底层函数里。移植到 STM32F1 或 F4 时,只需要改三个地方:SPI 句柄初始化、CS/IRQ 引脚宏定义、延时函数。寄存器操作和环形缓冲区逻辑完全不用动。我一般会先把SPI_TransferByte换成目标平台的实现,然后跑一个寄存器读写测试,读 WK2114 的版本寄存器或者随便写一个测试寄存器再读回来,确认 SPI 通路正常。这一步过了,后面基本不会有大问题。5.2 增加 DMA 收发降低 CPU 占用如果 4 个通道都跑 115200 且数据量大,中断方式会吃掉不少 CPU。改造思路是:SPI 接收用 DMA,WK2114 的 IRQ 触发后启动一次 DMA 传输,把 FIFO 里的数据批量搬到内存。发送方向也可以做 DMA 队列,主循环把数据塞进发送缓冲区,DMA 自动搬运。关键点是 DMA 传输长度要动态计算,根据 LSR 寄存器里的数据个数来决定搬多少字节。STM32F2 的 DMA 支持单次传输,配置好外设地址和内存地址,使能通道就行。5.3 用 shell 命令验证每个通道调通之后,我习惯在工程里加一个简单的 shell,通过主串口输入命令来测试 4 个扩展通道。比如:// 测试命令:wk2114 send <ch> <string> void Cmd_WK2114_Send(int ch, char *str) { while (*str) { while (!(WK2114_ReadReg(WK2114_REG_LSR(ch)) & 0x20)); WK2114_WriteReg(WK2114_REG_THR(ch), *str++); } }逻辑说明:LSR的 bit5 是发送保持寄存器空标志,等待它置位再写下一个字节。这样可以在不接外部设备的情况下,用回环线把 TX 和 RX 短接,自己发自己收,验证通道是否正常。参数说明:ch范围 0~3,str是要发送的字符串。回环测试时把对应通道的 TX 和 RX 用杜邦线短接,发什么就应该收到什么。5.4 一个我踩过的坑早期调试时,我把 WK2114 的 IRQ 接在了 STM32F2 的 PB0 上,但 EXTI0 的中断优先级设得比 SysTick 还低,结果主循环里HAL_Delay一执行,中断就被挂起,数据丢了一大半。后来把 EXTI0 的抢占优先级调到比 SysTick 高,问题消失。从那以后我每次配中断优先级都强制走一遍优先级分组检查,确保串口中断不会被系统滴答打断。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份资源面向嵌入式开发工程师与STM32学习者,提供WK2114串口扩展芯片的驱动实现,用于解决STM32F2系列微控制器UART接口不足、难以同时与多个外设进行串行通信的问题,适用于物联网设备、工业自动化等需要多路串口通信的场景。压缩包共2个文件,包含1个c源文件与1个h头文件,整体约4KB,其中源文件负责WK2114的初始化、波特率与数据格式设置、控制信号及收发数据等函数实现,头文件则定义函数原型、常量与结构体,便于其他模块调用与编译。目前已有1263人学习下载。通过该驱动,读者可掌握WK2114芯片与STM32F2 UART1的配合方式、HAL库或寄存器操作思路,以及串口扩展的测试与调试方法,为多串口嵌入式项目提供可直接复用的代码基础。1. 从一颗冷门串口芯片说起:WK2114 到底能解决什么问题去年帮一个做工业网关的朋友调板子,STM32F207 上已经挂了 4 路 UART,结果项目临时要接 8 个 RS232 传感器,主控的串口资源直接见底。换 MCU 不现实,软件模拟串口又扛不住 115200 的稳定收发。最后选了一颗 WK2114 串口拓展芯片,一颗 SPI 挂上去,硬生生多出 4 路独立 UART,问题当场解决。这份资源就是围绕 WK2114 在 STM32F2 平台上的驱动实现,包含寄存器操作、SPI 通信封装、中断收发和环形缓冲区管理。适合正在做多串口扩展、工业采集、嵌入式网关的开发者,尤其是主控串口不够用又不想换方案的人。它不是什么通用库,而是一份能直接嵌进你工程里的驱动代码,改改引脚和 SPI 句柄就能跑。2. 芯片与平台选型:WK2114 凭什么挂在 STM32F2 上2.1 WK2114 的定位与关键特性WK2114 是一颗四通道 UART 扩展芯片,通过 SPI 或并口与主控通信。它内部集成了 4 个独立的 UART 控制器,每个通道都有独立的波特率发生器、收发 FIFO 和中断控制。对主控来说,它就是一个 SPI 从设备,你往对应寄存器写数据,它帮你把串口时序全部搞定。关键特性直接决定驱动怎么写:通信接口:SPI 从模式,最高时钟支持到 10MHz 左右,STM32F2 的 SPI1 跑在 18MHz 以下都没问题通道数:4 路独立 UART,每路可单独配置波特率、数据位、停止位、校验位FIFO:每通道收发各 16 字节,减少主控中断频率中断输出:一根 IRQ 引脚,低电平有效,主控通过读中断状态寄存器判断是哪个通道触发供电:3.3V,和 STM32F2 电平直接匹配,不需要电平转换我一般会先确认板子上 WK2114 的片选接在哪个 GPIO,IRQ 接在哪个 EXTI 线上,这两根线定下来,驱动框架就清晰了。2.2 为什么选 STM32F2 而不是 F1/F4STM32F2 系列在工业现场很常见,Cortex-M3 内核,主频 120MHz,SPI 外设支持 8 位和 16 位数据帧,DMA 通道也够用。相比 F1,F2 的 SPI 时钟更高,配合 WK2114 的 FIFO 可以做到更低的 CPU 占用;相比 F4,F2 的外设寄存器布局更简单,驱动代码不需要处理太多缓存一致性问题。常见做法是 SPI1 跑在 9MHz 左右,用 DMA 发送、中断接收。WK2114 的 SPI 时序是标准模式 0,CPOL=0,CPHA=0,MSB 先行。STM32F2 的 SPI_CR1 寄存器里把 CPOL 和 CPHA 都清零就行。2.3 硬件连接与引脚分配以某工业采集板为例,典型连接如下:WK2114 引脚STM32F2 引脚说明SCLKPA5 (SPI1_SCK)SPI 时钟MOSIPA7 (SPI1_MOSI)主出从入MISOPA6 (SPI1_MISO)主入从出CSPA4 (GPIO)软件片选,低有效IRQPB0 (EXTI0)中断输出,低有效RSTPB1 (GPIO)复位,低有效片选我习惯用软件控制,因为 WK2114 的 SPI 事务之间需要拉高 CS 来锁存数据,硬件 NSS 有时候时序不好控。IRQ 接 EXTI 下降沿触发,进中断后先读 WK2114 的中断状态寄存器,再决定处理哪个通道。3. 驱动架构拆解:SPI 通信层与 UART 通道管理3.1 分层设计思路驱动分成三层:最底层是 SPI 读写字节函数,中间层是 WK2114 寄存器读写封装,最上层是 4 个 UART 通道的收发管理。这样分层的好处是,换主控平台只需要改 SPI 底层,换芯片只需要改寄存器定义。目录结构大致如下:wk2114_driver/ ├── inc/ │ ├── wk2114.h // 寄存器定义、结构体、API 声明 │ └── wk2114_port.h // 平台相关宏,SPI 句柄、CS/IRQ 引脚 ├── src/ │ ├── wk2114.c // 寄存器读写、初始化、中断处理 │ └── wk2114_uart.c // 4 通道收发、环形缓冲区 └── example/ └── main.c // 初始化示例、收发测试3.2 SPI 读写与寄存器操作WK2114 的寄存器地址是 8 位,数据也是 8 位。SPI 事务格式:先发地址字节,最高位表示读/写(1 读 0 写),然后跟数据字节。下面是我常用的两个基础函数:// wk2114.c #include "wk2114.h" #include "wk2114_port.h" // 写一个寄存器 void WK2114_WriteReg(uint8_t addr, uint8_t data) { WK2114_CS_LOW(); // 片选拉低 SPI_TransferByte(addr & 0x7F); // 最高位 0 表示写 SPI_TransferByte(data); // 写入数据 WK2114_CS_HIGH(); // 片选拉高,锁存 } // 读一个寄存器 uint8_t WK2114_ReadReg(uint8_t addr) { uint8_t val; WK2114_CS_LOW(); SPI_TransferByte(addr | 0x80); // 最高位 1 表示读 val = SPI_TransferByte(0xFF); // 发哑元,收数据 WK2114_CS_HIGH(); return val; }逻辑说明:addr & 0x7F把地址最高位清零表示写操作,addr | 0x80置一表示读操作。SPI_TransferByte是平台相关函数,STM32F2 上可以用标准库的SPI_I2S_SendData加等待SPI_I2S_GetFlagStatus实现,也可以用 HAL 的HAL_SPI_TransmitReceive。片选必须在每个事务结束后拉高,否则 WK2114 不会锁存数据,这是血泪经验,早期调试时数据写不进去就是 CS 忘了拉高。参数说明:addr范围 0x00~0x7F,data是 8 位寄存器值。读操作时发 0xFF 是因为 SPI 全双工,必须给时钟才能收到数据,哑元值无所谓。3.3 通道初始化与波特率配置WK2114 每个通道有一组寄存器:波特率分频低字节、高字节、线路控制寄存器、FIFO 控制寄存器。初始化时先软复位,再逐通道配置。// 初始化 WK2114,配置 4 个通道 void WK2114_Init(uint32_t baudrate) { WK2114_RST_LOW(); // 硬件复位 Delay_ms(10); WK2114_RST_HIGH(); Delay_ms(10); // 全局中断使能,FIFO 使能 WK2114_WriteReg(WK2114_REG_GLOBAL_CTRL, 0x01); for (uint8_t ch = 0; ch < 4; ch++) { uint16_t divisor = WK2114_CLK_FREQ / (baudrate * 16); WK2114_WriteReg(WK2114_REG_BR_L(ch), divisor & 0xFF); WK2114_WriteReg(WK2114_REG_BR_H(ch), (divisor >> 8) & 0xFF); // 8 数据位,1 停止位,无校验,使能收发 FIFO WK2114_WriteReg(WK2114_REG_LCR(ch), 0x03); WK2114_WriteReg(WK2114_REG_FCR(ch), 0x07); // 使能接收中断 WK2114_WriteReg(WK2114_REG_IER(ch), 0x01); } }逻辑说明:WK2114_CLK_FREQ是芯片内部基准时钟,常见为 14.7456MHz 或 7.3728MHz,具体看板子晶振。分频公式是基准时钟 / (波特率 * 16),算出 16 位分频值。LCR寄存器 0x03 表示 8N1,FCR寄存器 0x07 表示使能 FIFO 并清空收发缓冲。IER寄存器 0x01 表示接收数据可用中断。参数说明:baudrate支持 9600、19200、38400、57600、115200 等标准值。如果波特率有偏差,优先检查基准时钟频率是否和实际晶振一致,这个坑我踩过,板子焊的是 7.3728MHz 但代码里写 14.7456MHz,结果所有通道波特率翻倍,数据全是乱码。3.4 中断处理与环形缓冲区WK2114 的 IRQ 引脚是 4 个通道共享的,进中断后必须读全局中断状态寄存器,判断是哪个通道有数据,然后读该通道的接收寄存器,塞进对应的环形缓冲区。// 环形缓冲区结构 typedef struct { uint8_t buf[256]; volatile uint16_t head; volatile uint16_t tail; } RingBuf_t; static RingBuf_t rx_buf[4]; // EXTI 中断服务函数 void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { uint8_t int_status = WK2114_ReadReg(WK2114_REG_INT_STATUS); for (uint8_t ch = 0; ch < 4; ch++) { if (int_status & (1 << ch)) { while (WK2114_ReadReg(WK2114_REG_LSR(ch)) & 0x01) { uint8_t data = WK2114_ReadReg(WK2114_REG_RBR(ch)); uint16_t next = (rx_buf[ch].head + 1) % 256; if (next != rx_buf[ch].tail) { rx_buf[ch].buf[rx_buf[ch].head] = data; rx_buf[ch].head = next; } } } } EXTI_ClearITPendingBit(EXTI_Line0); } }逻辑说明:INT_STATUS寄存器的 bit0~bit3 分别对应 4 个通道。LSR寄存器的 bit0 是数据就绪标志,循环读到没有数据为止。环形缓冲区用 head 和 tail 两个索引,head 由中断写,tail 由主循环读,不需要关中断,因为单生产者单消费者模型下读写各自索引不会冲突。参数说明:缓冲区大小 256 字节,可以根据实际数据量调整。如果波特率 115200 且数据连续,建议至少 512 字节,否则主循环处理慢会丢数据。WK2114_REG_LSR的 bit0 是 DR(Data Ready),bit1 是 OE(Overrun Error),如果发现 OE 置位说明缓冲区满了,需要加大缓冲区或提高主循环读取频率。4. 避坑与排查:调 WK2114 时最容易翻车的几个点4.1 数据写进去读出来全是 0xFF现象:初始化后读任何寄存器都返回 0xFF,写进去的值也读不回来。原因:SPI 模式不对,或者 MISO 线没接好。WK2114 要求 SPI 模式 0,如果 CPHA 设成 1,采样边沿错了,读出来全是高电平。另外如果 MISO 虚焊,主控读到的就是浮空的高电平。解决:用示波器看 SCLK 和 MOSI,确认时钟空闲低、第一个边沿采样。检查 MISO 焊接。STM32F2 的 SPI_CR1 寄存器里 CPOL=0、CPHA=0,BR 分频先设大一点,比如 64 分频,低速调通再提速。4.2 能读能写但串口发不出数据现象:寄存器读写正常,但外部串口工具收不到任何数据。原因:通道的发送使能没开,或者波特率分频算错。WK2114 的 LCR 寄存器里除了数据位停止位,还有一位是发送使能,有些型号需要单独置位。另外分频值算错会导致波特率偏差太大,接收端识别不了。解决:先确认 LCR 的值,8N1 通常是 0x03,如果芯片手册要求发送使能位,就改成 0x43。然后用示波器测 TX 引脚,看有没有波形,有波形但数据错就是波特率问题,重新算分频值。4.3 中断进不去或者进去出不来现象:IRQ 引脚一直是低电平,或者中断触发一次后再也不触发。原因:中断状态寄存器没清,或者 EXTI 触发边沿设成了双边沿。WK2114 的 IRQ 是电平方式,低电平表示有中断,读走数据后自动变高。如果 EXTI 设成下降沿触发,但中断服务里没读数据,IRQ 一直低,就不会再有下降沿。解决:EXTI 配置成下降沿触发,中断服务里必须读到 LSR 的 DR 位为 0 才退出。另外确认 WK2114 的全局中断使能位已经打开,有些批次芯片默认是关闭的。4.4 多通道同时收发时数据串扰现象:通道 0 收到的数据跑到了通道 1 的缓冲区里。原因:读接收寄存器时地址算错,或者中断状态判断时通道索引搞混。WK2114 的通道寄存器地址是基址加通道偏移,如果偏移量算错,读的就是隔壁通道的数据。解决:把通道基址定义成宏,比如#define WK2114_REG_RBR(ch) (0x00 + (ch) * 0x08),确保每个通道的寄存器块间隔正确。中断里用for循环遍历 4 个通道时,索引和寄存器地址要对应上。4.5 高速收发时丢数据现象:115200 波特率下连续发几百字节,接收端偶尔少几个字节。原因:环形缓冲区太小,或者主循环读取太慢。WK2114 的 FIFO 只有 16 字节,如果主循环不及时读,FIFO 溢出就丢数据。解决:加大环形缓冲区到 512 或 1024 字节,主循环里用while循环把缓冲区数据全部读走再处理。如果还丢,考虑用 DMA 把 SPI 接收也做成自动搬运,减少中断延迟。5. 二次开发与验证:从能跑到好用的改造思路5.1 把驱动移植到其他 STM32 系列这份驱动的平台相关部分集中在wk2114_port.h和 SPI 底层函数里。移植到 STM32F1 或 F4 时,只需要改三个地方:SPI 句柄初始化、CS/IRQ 引脚宏定义、延时函数。寄存器操作和环形缓冲区逻辑完全不用动。我一般会先把SPI_TransferByte换成目标平台的实现,然后跑一个寄存器读写测试,读 WK2114 的版本寄存器或者随便写一个测试寄存器再读回来,确认 SPI 通路正常。这一步过了,后面基本不会有大问题。5.2 增加 DMA 收发降低 CPU 占用如果 4 个通道都跑 115200 且数据量大,中断方式会吃掉不少 CPU。改造思路是:SPI 接收用 DMA,WK2114 的 IRQ 触发后启动一次 DMA 传输,把 FIFO 里的数据批量搬到内存。发送方向也可以做 DMA 队列,主循环把数据塞进发送缓冲区,DMA 自动搬运。关键点是 DMA 传输长度要动态计算,根据 LSR 寄存器里的数据个数来决定搬多少字节。STM32F2 的 DMA 支持单次传输,配置好外设地址和内存地址,使能通道就行。5.3 用 shell 命令验证每个通道调通之后,我习惯在工程里加一个简单的 shell,通过主串口输入命令来测试 4 个扩展通道。比如:// 测试命令:wk2114 send <ch> <string> void Cmd_WK2114_Send(int ch, char *str) { while (*str) { while (!(WK2114_ReadReg(WK2114_REG_LSR(ch)) & 0x20)); WK2114_WriteReg(WK2114_REG_THR(ch), *str++); } }逻辑说明:LSR的 bit5 是发送保持寄存器空标志,等待它置位再写下一个字节。这样可以在不接外部设备的情况下,用回环线把 TX 和 RX 短接,自己发自己收,验证通道是否正常。参数说明:ch范围 0~3,str是要发送的字符串。回环测试时把对应通道的 TX 和 RX 用杜邦线短接,发什么就应该收到什么。5.4 一个我踩过的坑早期调试时,我把 WK2114 的 IRQ 接在了 STM32F2 的 PB0 上,但 EXTI0 的中断优先级设得比 SysTick 还低,结果主循环里HAL_Delay一执行,中断就被挂起,数据丢了一大半。后来把 EXTI0 的抢占优先级调到比 SysTick 高,问题消失。从那以后我每次配中断优先级都强制走一遍优先级分组检查,确保串口中断不会被系统滴答打断。希望帮到你。本文还有配套的精品资源,点击获取