1. WK2124芯片与STM32L151驱动方案概述
WK2124是一款SPI转4路UART的串口扩展芯片,每路UART都带有256字节的收发FIFO,最高支持2Mbps波特率。这款芯片特别适合在MCU原生串口资源不足时,扩展多通道异步通信能力。
在嵌入式系统开发中,串口资源紧张是常见问题。以STM32L151为例,这款低功耗MCU通常只提供2-3个UART接口,但在工业控制、智能家居等场景下,往往需要同时连接多个串口设备(如传感器、显示屏、无线模块等)。WK2124的引入,完美解决了这一痛点。
1.1 WK2124核心特性解析
- SPI主接口:最高20MHz时钟频率,支持标准SPI通信协议
- 4路独立UART:全双工工作模式,每路均可独立配置
- 大容量FIFO:每通道Rx/Tx各256字节缓冲,有效减轻MCU中断负担
- 灵活的波特率:单路最高2Mbps,满足大多数高速通信需求
- 工业级设计:工作电压2.5-5.0V,IO耐压5V,温度范围-40℃~+85℃
- 丰富的中断机制:支持Rx满、Tx空、超时、错误等多种中断类型
1.2 硬件连接方案
WK2124采用SSOP20封装,与STM32的连接非常简单:
- SPI接口:SCK、MISO、MOSI、CS
- 中断引脚:INT(下降沿有效)
- 复位引脚:RST(低电平有效)
- 4路UART:TXD1/RXD1至TXD4/RXD4
在实际电路设计中,建议:
- SPI线上串联22Ω电阻,抑制信号反射
- 在靠近WK2124的位置放置0.1μF去耦电容
- 若通信距离较长,UART线上可添加TVS二极管防护
2. 驱动程序设计详解
2.1 寄存器操作基础
WK2124的驱动核心是对其内部寄存器的读写操作。芯片寄存器分为全局寄存器和子串口寄存器两类:
c复制// 全局寄存器写操作
static void write_g_reg(uint8_t reg, uint8_t data) {
GPIO_CS_PIN(true);
HAL_SPI_Transmit(&hspi1, ®, sizeof(uint8_t), SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, &data, sizeof(uint8_t), SPI_TIMEOUT);
GPIO_CS_PIN(false);
}
// 子串口寄存器读操作
static uint8_t read_s_reg(uint8_t port, uint8_t reg) {
uint8_t regist = (0x40 | (port << 4) | reg);
uint8_t value = 0x00;
GPIO_CS_PIN(true);
HAL_SPI_Transmit(&hspi1, ®ist, sizeof(uint8_t), SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, &value, sizeof(uint8_t), SPI_TIMEOUT);
GPIO_CS_PIN(false);
return value;
}
关键点:WK2124的寄存器访问遵循特定格式:
- 写操作:先发送寄存器地址,再发送数据
- 读操作:发送的地址字节最高位需置1(0x40)
- 子串口寄存器访问还需包含端口号(0-3)
2.2 FIFO操作实现
数据收发主要通过FIFO进行,这是提高通信效率的关键:
c复制// FIFO写操作
static uint16_t write_fifo(uint8_t port, uint8_t *data, uint16_t len) {
if (len > 255) len = 255; // FIFO最大256字节
uint8_t buff[256 + 1];
buff[0] = 0x80 | (port << 4); // FIFO写命令
memcpy(buff + 1, data, len);
GPIO_CS_PIN(true);
HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, buff, len + 1, SPI_TIMEOUT);
GPIO_CS_PIN(false);
return (status == HAL_OK) ? len : 0;
}
2.3 波特率计算算法
波特率配置是串口初始化的关键步骤,计算公式如下:
c复制static void calc_baudrate(uint32_t baudrate, uint8_t* BAUD0, uint8_t* BAUD1, uint8_t* PRES) {
uint32_t fosc = WK21XX_CRYSTAL_HZ; // 11.0592MHz
float value = (float)fosc / (float)(baudrate << 4);
*BAUD0 = (((uint32_t)value) - 1) & 0xFF;
*BAUD1 = ((((uint32_t)value) - 1) >> 8) & 0xFF;
*PRES = ((uint32_t)(value * 10)) % 10; // 小数部分
}
技术细节:WK2124的波特率生成采用分频系数+小数预分频的机制,计算公式为:
波特率 = 晶振频率 / (16 × (分频系数 + 小数预分频/10))
其中分频系数=BAUD1:BAUD0,小数预分频=PRES
3. 完整驱动实现
3.1 初始化流程
WK2124的初始化需要按特定顺序配置各寄存器:
c复制void wk21xx_init(void) {
// 硬件复位
wk21xx_reset();
// 初始化各端口队列缓冲区
tk_queue_init(&m_port1_tx_queue, m_port1_tx_buf, sizeof(m_port1_tx_buf), 1, false);
// ...其他端口初始化
// 配置各子串口
port_config(UART_PORT_1, 115200, CHECK_NONE, STOP_1BIT);
// ...其他端口配置
}
static void port_config(uint8_t port, uint32_t baudrate, check_mode_e check_mode, stop_bit_e stop_bit) {
// 1. 使能子串口时钟
uint8_t value = read_g_reg(WK21XX_GENA);
write_g_reg(WK21XX_GENA, value | (1 << port));
// 2. 软件复位子串口
value = read_g_reg(WK21XX_GRST);
write_g_reg(WK21XX_GRST, value | (1 << port));
// 3. 配置FIFO和中断
write_s_reg(port, WK21XX_SIER,
WK21XX_TFTRIG_IEN | WK21XX_TFEMPTY_IEN |
WK21XX_RFTRIG_IEN | WK21XX_RXOUT_IEN);
// 4. 设置波特率(切换到PAGE1)
write_s_reg(port, WK21XX_SPAGE, 1);
uint8_t BAUD0, BAUD1, PRES;
calc_baudrate(baudrate, &BAUD0, &BAUD1, &PRES);
write_s_reg(port, WK21XX_BAUD1, BAUD1);
write_s_reg(port, WK21XX_BAUD0, BAUD0);
write_s_reg(port, WK21XX_PRES, PRES);
// 5. 设置数据格式(切换回PAGE0)
write_s_reg(port, WK21XX_SPAGE, 0);
value = read_s_reg(port, WK21XX_LCR);
// ...配置校验位和停止位
write_s_reg(port, WK21XX_LCR, value);
// 6. 使能收发
value = read_s_reg(port, WK21XX_SCR);
write_s_reg(port, WK21XX_SCR, value | WK21XX_TXEN | WK21XX_RXEN);
}
3.2 数据收发实现
驱动采用双缓冲机制:硬件FIFO+软件队列,确保数据不丢失:
c复制uint16_t wk21xx_send_data(uint8_t port, uint8_t *data, uint16_t len) {
// 数据存入软件队列
uint16_t nsend = tk_queue_push_multi(&m_port1_tx_queue, data, len);
// 触发发送
send_data(port);
// 等待发送完成事件
tk_event_recv(&m_wk21xx_event, PORT_TX_EVENT(port),
TK_EVENT_OPTION_AND|TK_EVENT_OPTION_CLEAR, NULL);
return nsend;
}
static void send_data(uint8_t port) {
uint8_t state = read_s_reg(port, WK21XX_FSR);
if (state & WK21XX_TFULL) return; // FIFO满
uint16_t avail = 256 - read_s_reg(port, WK21XX_TFCNT);
uint8_t buff[256];
// 从软件队列取出数据
uint16_t nsend = tk_queue_pop_multi(&m_port1_tx_queue, buff, avail);
if (nsend > 0) {
write_fifo(port, buff, nsend); // 写入硬件FIFO
}
}
4. 实战经验与问题排查
4.1 常见问题解决方案
问题1:通信不稳定,数据丢失
- 检查SPI时钟是否过高(建议初始使用10MHz)
- 确认WK2124的供电稳定,去耦电容已正确放置
- 检查PCB布线,确保SPI信号线等长,远离高频干扰源
问题2:波特率误差大
- 确认晶振频率准确(11.0592MHz最佳)
- 使用示波器测量实际波特率,调整PRES值
- 对于特殊波特率,可微调晶振负载电容
问题3:中断不触发
- 检查INT引脚连接和GPIO配置(下降沿触发)
- 确认GIER和SIER寄存器已正确配置中断使能
- 在中断服务程序中及时读取GIFR寄存器清除中断标志
4.2 性能优化建议
-
FIFO阈值调整:
c复制write_s_reg(port, WK21XX_RFTL, 100); // 接收FIFO阈值 write_s_reg(port, WK21XX_TFTL, 10); // 发送FIFO阈值- 根据数据包大小调整阈值,减少中断次数
- 大数据量时提高Rx阈值,小数据包时降低Tx阈值
-
DMA传输:
- 将SPI配置为DMA模式,减轻CPU负担
- 建议接收使用DMA循环模式,发送使用普通模式
-
低功耗优化:
c复制// 进入睡眠模式 write_s_reg(port, WK21XX_SCR, read_s_reg(port, WK21XX_SCR) | WK21XX_SLEEPEN);- 无通信时关闭不使用的子串口
- 定期检查线路状态,动态调整功耗模式
5. 扩展应用与进阶技巧
5.1 多设备级联方案
通过片选信号控制,单个SPI接口可连接多个WK2124:
- 为每个WK2124分配独立的CS引脚
- 在驱动中添加设备选择参数
- 软件实现动态设备切换
5.2 自定义协议实现
利用4路UART,可实现灵活的多协议网关:
- UART1:Modbus RTU
- UART2:自定义二进制协议
- UART3:AT指令(无线模块)
- UART4:调试输出
5.3 流量控制实战
WK2124支持硬件流控,配置方法:
c复制// 启用RTS/CTS流控
uint8_t val = read_s_reg(port, WK21XX_SIER);
write_s_reg(port, WK21XX_SIER, val | WK21XX_CTS_IEN | WK21XX_RTS_IEN);
在高速通信或长距离传输时,建议启用硬件流控以避免数据丢失。