串口与 USB 通信
本文档介绍 AW33N BLE SDK 中串口(UART/LPUART)与 USB(设备/主机)两大通信子系统的实现:从硬件寄存器抽象、公共串口控制模块、端点 DMA 缓冲与中断处理,到低功耗唤醒、挂起/恢复与典型使用方式。
Purpose and Scope
本页覆盖以下内容:
- 串口(UART):公共串口控制模块
common_uart_control(数据接收、回调注册、低功耗唤醒)、BD57 系列 UART 硬件寄存器抽象(uart_hal.h)、低功耗串口 LPUART、调试串口uart_debug以及外设演示(uart_demo/lpuart_demo)。 - USB:设备控制器配置
usb_device_config.c(端点 DMA 缓冲分配、中断表、usb_isr)、USB 设备栈(usb_stack.h)、挂起/恢复(usb_suspend_resume)、USB 主机(usb_host.h/usb_storage.h)与物理层(usb_phy.h)。
以下主题属于其他目录页面的范围,本页仅在必要时引用:
- 通过串口进行固件升级(
testbox_uart_update/uart_update)属于“固件升级”类页面; - AT 命令透传(
at_char_uart)、FindMy 传感器串口(ble_fmy_sensor_uart)、HID 鼠标(mouse_usb)等应用示例属于各自的应用页面; - 低功耗睡眠管理本身属于电源管理页面,本页只说明串口与睡眠的交互点。
概述
AW33N BLE SDK 面向低功耗蓝牙 SoC,通信外设被广泛用于调试、透传、升级与 USB 复合设备(HID/音频/存储)等场景。串口与 USB 在该 SDK 中的角色如下:
- 串口是系统最基础的调试与数据通道:
uart_debug提供日志输出;common_uart_control提供一个与具体应用解耦的“公共串口”抽象,任何模块(透传、升级、传感器数据)都可以注册接收回调;lpuart则面向低功耗场景,可在睡眠时保持唤醒能力。 - USB 用于将芯片作为设备接入主机(如 PC),常见配置是复合设备:端点 0 用于控制传输(Setup),端点 1 用于大容量存储(MSD),端点 2 用于 HID,端点 3 用于音频(扬声器/麦克风)。SDK 也提供主机侧栈(
usb_host)用于读 U 盘等场景。
理解这两个子系统,关键是先看硬件寄存器抽象(UART 的 JL_UART_TypeDef、USB 的端点缓冲分配),再看软件如何围绕 DMA 缓冲与中断把数据搬运到应用层。
架构
flowchart TD
subgraph sg_App["应用层 (apps)"]
Demo["uart_demo / lpuart_demo<br/>at_char_uart / ble_fmy_sensor_uart"]
Update["testbox_uart_update / uart_update"]
UsbApp["HID mouse / 复合设备应用"]
end
subgraph sg_Uart["串口子系统"]
CommonUart["common_uart_control<br/>(公共串口控制)"]
UartDebug["uart_debug"]
Lpuart["lpuart (低功耗串口)"]
UartHal["UART HAL 寄存器<br/>JL_UART_TypeDef / JL_WL_UART_TypeDef"]
end
subgraph sg_Usb["USB 子系统"]
UsbDevCfg["usb_device_config<br/>(端点缓冲/中断配置)"]
UsbStack["usb_stack / usb_device<br/>(设备栈)"]
UsbSuspend["usb_suspend_resume"]
UsbHost["usb_host / usb_storage<br/>(主机栈)"]
UsbPhy["usb_phy / usb_hw<br/>(物理层)"]
end
subgraph sg_Hw["硬件层"]
HwUart["UART/LPUART 外设"]
HwUsb["USB 控制器 (DMA)"]
end
Demo --> CommonUart
Demo --> UartDebug
Update --> CommonUart
CommonUart --> UartHal
UartDebug --> UartHal
Lpuart --> UartHal
UartHal --> HwUart
UsbApp --> UsbStack
UsbDevCfg --> UsbStack
UsbStack --> UsbSuspend
UsbStack --> UsbPhy
UsbHost --> UsbPhy
UsbPhy --> HwUsb
架构说明:
- 串口路径:应用(透传/升级/传感器)通过
common_uart_control注册接收回调并使用其发送接口;该模块直接操作 HAL 寄存器层(uart_hal.h中的JL_UART_TypeDef/JL_WL_UART_TypeDef),最终落到 UART/LPUART 外设。调试日志则走uart_debug。 - USB 路径:应用配置设备描述符后,由
usb_device_config负责端点 DMA 缓冲与中断资源分配;usb_stack处理枚举与传输;usb_suspend_resume处理挂起/恢复;主机侧通过usb_host/usb_storage访问存储设备。 - 两条路径都依赖硬件 DMA 与中断:串口用 DMA 接收缓冲(
uart_rx_ptr/uart_rx_data),USB 为每个端点预分配对齐 DMA 缓冲,中断服务usb_isr在 IRQ 上下文中搬运数据。
串口子系统详解
公共串口控制模块(common_uart_control)
apps/app/bsp/common/common_uart/common_uart_control.c 是整个 SDK 串口数据通道的核心抽象。它以编译宏 TCFG_COMMON_UART_ENABLE 作为总开关,为上层提供“注册接收回调 + 发送数据”的统一接口,屏蔽了具体串口实例的差异。
模块内部的数据结构与关键设计如下:
#if TCFG_COMMON_UART_ENABLE
NOT_KEEP_RAM
static uint8_t uart_rx_ptr[COMMON_UART_RX_BUF_SIZE] ALIGNED(4);
NOT_KEEP_RAM
u8 uart_rx_data[COMMON_UART_RX_BUF_SIZE];
static void (*common_uart_irq_rx_handler_callback)(uint8_t *packet, uint32_t size) = NULL;
static int common_uart_num = COMMON_UART_INDEX;
Source: common_uart_control.c
设计要点:
- 双缓冲设计:
uart_rx_ptr是 DMA 接收指针缓冲(内部使用,ALIGNED(4)保证 DMA 对齐),uart_rx_data是实际数据缓冲(非static,符号导出,便于调试/抓取)。缓冲大小统一由COMMON_UART_RX_BUF_SIZE决定。 - 回调解耦:接收回调
common_uart_irq_rx_handler_callback是函数指针,签名固定为void (*)(uint8_t *packet, uint32_t size)。这样发送方(透传、升级、传感器)不必关心底层是哪个串口、走 DMA 还是中断,只需注册回调。 - 串口实例选择:
common_uart_num取自COMMON_UART_INDEX,让同一份代码可以编译到不同串口上,不同产品只需在配置头文件里改索引。
回调注册接口(内部调用,供各模块在初始化时绑定):
void common_uart_regiest_receive_callback(void *rx_cb)
{
common_uart_irq_rx_handler_callback = rx_cb;
}
Source: common_uart_control.c
紧随其后(第 78–80 行注释处)是“发送数据到串口”的发送入口,与接收回调配套使用:上层把整包数据交给该模块,由模块负责搬运到硬件发送路径。
低功耗唤醒交互
串口与电源管理的关键交互点也在这个文件中。默认 COMMON_UART_RX_POWERLOW_ENABLE 为 0、LPUART_RX_ENABLE 为 0(低功耗串口接收默认关闭);当开启后,行为如下(源码注释原话):
串口接收到数据后禁止系统进休眠,但第一包数据会丢,需要在发送正式数据前发送一个空包,并在
UART_POWERLOW_TIMEOUT时间内发出后续的有效包。
#define COMMON_UART_RX_POWERLOW_ENABLE 0 //低功耗状态下配置串口唤醒
#define LPUART_RX_ENABLE 0 //低功耗串口使能
#if COMMON_UART_RX_POWERLOW_ENABLE
#include "asm/power/power_wakeup.h"
#define UART_POWERLOW_TIMEOUT 500 //unit: ms
static u16 uart_pl_timer = 0; //用于计时进入休眠
static void uart_rx_wakeup_timeout_callback(void *priv)
{
sleep_run_check_enalbe(1, SLEEP_EN_TYPE_UART);
uart_pl_timer = 0;
}
static void uart_rx_wakeup_callback(P33_IO_WKUP_EDGE edge)
{
sleep_run_check_enalbe(0, SLEEP_EN_TYPE_UART);
if (uart_pl_timer == 0) {
uart_pl_timer = sys_timeout_add(NULL, uart_rx_wakeup_timeout_callback, UART_POWERLOW_TIMEOUT);
} else {
sys_timer_modify(uart_pl_timer, UART_POWERLOW_TIMEOUT);
}
}
static struct _p33_io_wakeup_config uart_rx_wakeup_config = {
.pullup_down_mode = PORT_INPUT_PULLUP_10K,
.gpio = COMMON_UART_RX_PIN,
.edge = FALLING_EDGE,
.filter = PORT_FLT_DISABLE,
.callback = uart_rx_wakeup_callback,
};
#endif
Source: common_uart_control.c
机制说明:
- 串口 RX 引脚(
COMMON_UART_RX_PIN)配置为 P33 端口下降沿唤醒源,空闲时系统可休眠;一旦出现下降沿(起始位),uart_rx_wakeup_callback被调用,通过sleep_run_check_enalbe(0, SLEEP_EN_TYPE_UART)禁止系统进入休眠,防止接收中途掉电。 - 同时启动/刷新一个 500 ms 的超时定时器(
UART_POWERLOW_TIMEOUT):应用须在该窗口内继续发来有效数据并消费完;超时后uart_rx_wakeup_timeout_callback重新允许睡眠,回到低功耗状态。 - 由于唤醒本身要消耗时间,唤醒后的第一包数据会丢失——这是硬件级约束,因此协议设计上要求“先发空包唤醒,再发有效包”,这也是串口低功耗方案的经典 trade-off。
UART 硬件寄存器抽象(uart_hal.h)
apps/include_lib/cpu/bd57/periph/uart_hal.h 定义了 BD57 系列 UART 控制器的寄存器映射,是所有 UART 驱动(含 DMA 收发)直接操作的对象:
typedef struct {
__RW __u16 TX_CON0; /* 发送控制 0 */
__RW __u16 TX_CON1; /* 发送控制 1 */
__RW __u16 RX_CON0; /* 接收控制 0 */
__RW __u16 RX_CON1; /* 接收控制 1 */
__RW __u16 CON2; /* 控制 2 */
__RW __u16 BAUD; /* 波特率 */
__RW __u8 BUF; /* 数据寄存器 */
__RW __u32 OTCNT; /* 溢出计数 */
__RW __u32 TXADR; /* 发送 DMA 地址 */
__WO __u16 TXCNT; /* 发送计数 */
__RW __u32 RXSADR; /* 接收 DMA 起始地址 */
__RW __u32 RXEADR; /* 接收 DMA 结束地址 */
__RW __u32 RXCNT; /* 接收计数 */
__RO __u16 HRXCNT; /* 硬件接收计数 */
__RO __u16 RX_ERR_CNT;/* 接收错误计数 */
} JL_UART_TypeDef;
typedef struct {
__RW __u16 CON0;
__RW __u16 CON1;
__RW __u16 CON2;
__RW __u16 BAUD;
__RW __u8 BUF;
} JL_WL_UART_TypeDef;
Source: uart_hal.h
值得注意的设计:
- 完整 DMA 收发寄存器组:
TXADR/TXCNT是发送 DMA 的地址与计数;RXSADR/RXEADR定义接收 DMA 的环形区间;HRXCNT实时反映硬件已收字节数;OTCNT与RX_ERR_CNT记录溢出与错误,驱动可据此做丢包统计与恢复。这与common_uart_control.c中ALIGNED(4)的 DMA 缓冲是配套的——寄存器层直接操作内存地址,驱动只需维护环形缓冲的起止地址。 - 两种寄存器集:
JL_UART_TypeDef是标准 UART 全功能寄存器集;JL_WL_UART_TypeDef是精简版(无线/低功耗串口变体),仅保留 CON0/CON1/CON2/BAUD/BUF,对应lpuart场景——低功耗串口省去了 DMA 寄存器,由硬件内部完成收发缓冲。
调试串口与演示程序
apps/app/bsp/cpu/uart_debug.c(配套头文件apps/include_lib/cpu/bd57/uart_debug.h):调试串口驱动,负责日志通道的初始化与输出,是LOG_TAG "[common_uart]"等日志宏的底层载体。apps/app/bsp/cpu/periph_demo/uart_demo.c与lpuart_demo.c:外设演示工程,展示了普通 UART 与 LPUART 的初始化、收发与中断用法,是开发者移植到新产品时的最小参考。- 类型与依赖:
apps/include_lib/cpu/uart.h、apps/include_lib/cpu/uart_depend.h、apps/include_lib/cpu/bd57/uart_types.h提供 UART 驱动的公共 API 与依赖注入(如波特率表、引脚配置结构)。
串口接收核心流程
sequenceDiagram
participant Hw as UART 硬件 (DMA)
participant Irq as 串口中断服务
participant Ctl as common_uart_control
participant App as 应用模块 (透传/升级/传感器)
participant Pwr as 电源管理 (可选)
Note over Hw,Pwr: 低功耗模式: RX 下降沿唤醒
Hw->>Pwr: P33 下降沿唤醒 (COMMON_UART_RX_PIN)
Pwr->>Pwr: 禁止休眠, 启动 500ms 超时定时器
Hw->>Irq: DMA 接收完成/数据到达中断
Irq->>Ctl: 搬运 uart_rx_ptr/uart_rx_data
Ctl->>App: 调用 common_uart_irq_rx_handler_callback(packet, size)
App->>App: 解析并处理数据包
App->>Ctl: 调用发送接口回包
Note over Pwr: 超时后重新允许休眠
流程要点:硬件 DMA 把数据写入对齐缓冲 → 中断服务更新指针并回调 → 应用在回调中处理;回包经发送接口写回硬件。低功耗模式下,唤醒、禁止休眠、超时恢复三个动作由电源管理回调链保证数据包在窗口内被完整接收。
USB 子系统详解
设备控制器配置(usb_device_config.c)
apps/app/bsp/common/usb/device/usb_device_config.c 负责 USB 设备控制器的资源规划:为每个端点预分配 DMA 缓冲、维护中断向量表,并提供 usb_isr 中断入口。文件开头的资源定义是整个配置的核心:
#define EP0_DMA_SIZE (64+4)
#define HID_DMA_SIZE (64+4)
#define AUDIO_DMA_SIZE (SPK_FRAME_LEN + MIC_FRAME_LEN + 4 * 2)
#define MSD_DMA_SIZE ((64+4)*2)
#define MAX_EP_TX 5
#define MAX_EP_RX 5
static usb_interrupt usb_interrupt_tx[USB_MAX_HW_NUM][MAX_EP_TX];// SEC(.usb_g_bss);
static usb_interrupt usb_interrupt_rx[USB_MAX_HW_NUM][MAX_EP_RX];// SEC(.usb_h_bss);
static usb_interrupt usb_sof_interrupt_tx[USB_MAX_HW_NUM][MAX_EP_TX];
static usb_interrupt usb_mtx_interrupt[USB_MAX_HW_NUM][MAX_EP_RX];
static usb_interrupt usb_mrx_interrupt[USB_MAX_HW_NUM][MAX_EP_RX];
NOT_KEEP_RAM
static u8 ep0_dma_buffer[EP0_DMA_SIZE] __attribute__((aligned(4)));
NOT_KEEP_RAM
static u8 ep1_msd_dma_buffer[2][MSD_DMA_SIZE] __attribute__((aligned(4)));
NOT_KEEP_RAM
static u8 ep2_hid_dma_buffer[HID_DMA_SIZE] __attribute__((aligned(4)));
NOT_KEEP_RAM
static u8 ep3_spk_dma_buffer[AUDIO_DMA_SIZE] __attribute__((aligned(4)));
NOT_KEEP_RAM
static u8 ep3_mic_dma_buffer[AUDIO_DMA_SIZE] __attribute__((aligned(4)));
Source: usb_device_config.c
设计意图解读:
- 复合设备端点规划:EP0 是控制端点(Setup/枚举,
64+4字节,4 字节为 DMA 描述符开销);EP1 是 MSD(大容量存储,双缓冲2×用于乒乓传输);EP2 是 HID(64+4,标准 64 字节全速中断包);EP3 分扬声器(SPK,OUT)与麦克风(MIC,IN)两个方向,大小由音频帧长SPK_FRAME_LEN/MIC_FRAME_LEN决定。 - 多硬件单元支持:所有中断表与缓冲都带
USB_MAX_HW_NUM维度,说明 SDK 支持多个 USB 控制器实例(如双 USB 口),配置层按索引隔离。 - 中断源细分:除常规 TX/RX 中断外,还单独维护 SOF 中断(
usb_sof_interrupt_tx)与 MTX/MRX(主模式收发,即主机侧)中断向量,设备模式与主机模式共用同一套资源表。 - 内存分段:DMA 缓冲标
NOT_KEEP_RAM(不常驻 RAM,节省低功耗唤醒后的保留内存),中断表放在.usb_g_bss/.usb_h_bss段;__attribute__((aligned(4)))保证 DMA 对齐。
端点缓冲查询接口根据端点号与方向返回对应的 DMA 缓冲,供传输层读写:
__attribute__((always_inline_when_const_args))
void *usb_get_ep_buffer(const usb_dev usb_id, u32 ep)
{
u8 *ep_buffer = NULL;
u32 _ep = ep & 0xf;
if (ep & USB_DIR_IN) {
switch (_ep) {
case 0: ep_buffer = ep0_dma_buffer; break;
case 1: ep_buffer = ep1_msd_dma_buffer[0]; break;
case 2: ep_buffer = ep2_hid_dma_buffer; break;
case 3: ep_buffer = ep3_mic_dma_buffer; break;
}
} else {
switch (_ep) {
case 0: ep_buffer = ep0_dma_buffer; break;
case 1: ep_buffer = ep1_msd_dma_buffer[1]; break;
case 2: ep_buffer = NULL; break;
case 3: ep_buffer = ep3_spk_dma_buffer; break;
}
}
return ep_buffer;
}
Source: usb_device_config.c
要点:usb_id 参数目前仅用于区分硬件单元(缓冲是全局静态的);EP0 双向共用同一缓冲;EP1 按方向使用双缓冲的两个成员(IN 用 [0],OUT 用 [1]);EP2 HID 仅支持 IN 方向(OUT 返回 NULL);EP3 按方向区分 SPK/MIC。该函数标注 always_inline_when_const_args,在编译期参数已知时会被内联,减少传输热路径上的调用开销。
中断处理(usb_isr)
配置层同时提供统一中断入口:
void usb_isr(const usb_dev usb_id)
{
/* JL_PORTA->OUT |= BIT(7); */
u32 intr_usb, intr_usbe;
u32 intr_tx, intr_txe;
u32 intr_rx, intr_rxe;
...
}
Source: usb_device_config.c
usb_isr 按“总中断使能 → TX → RX”分组读取中断状态(intr_usb/intr_usbe、intr_tx/intr_txe、intr_rx/intr_rxe),分别派发到 usb_interrupt_tx/usb_interrupt_rx 向量表,最终回调到设备栈注册的处理函数。它运行在 IRQ 上下文,因此处理函数必须短小、不做阻塞操作。
设备栈、挂起恢复与主机侧
围绕 usb_device_config.c 的外围模块:
- 设备栈:
apps/include_lib/device/usb/device/usb_stack.h与apps/app/bsp/common/usb/device/usb_device.c实现 USB 设备枚举、标准请求(SETUP)处理与描述符管理(usb/device/descriptor.h)。usb_device_config.c顶部包含的usb_stack.h、usb_suspend_resume.h、fusb_pll_trim.h表明设备初始化链路为:usb_device.c→ 配置层分配资源 → 物理层(fusb_hal/usb_hw,含 PLL 校准fusb_pll_trim)。 - 挂起/恢复:
apps/app/bsp/common/usb/device/usb_suspend_resume.c(头文件apps/include_lib/device/usb/device/usb_suspend_resume.h)处理总线 SUSPEND/RESUME 事件,配合usb_config.h中的电源策略,在总线空闲时进入低功耗、检测到恢复信号时重新启动时钟——这与串口的低功耗唤醒是同一套电源管理框架下的两个实例。 - 主机栈:
apps/include_lib/device/usb/host/usb_host.h与usb_storage.h提供主机模式(HOST),可枚举外部设备并访问 U 盘(Mass Storage)。主模式复用了配置层中usb_mtx_interrupt/usb_mrx_interrupt主收发中断向量。 - 公共定义与物理层:
usb_config.h(全局开关与参数)、usb_common_def.h/usb_std_class_def.h(标准 USB 类型与类定义)、usb_phy.h(物理层接口)、apps/include_lib/cpu/bd57/usb_hw.h与fusb_hal.h(芯片 USB 硬件寄存器与 PHY HAL)。
USB 设备枚举与传输流程
sequenceDiagram
participant Host as USB 主机 (PC)
participant Hw as USB 控制器 + PHY
participant Isr as usb_isr (IRQ)
participant Cfg as usb_device_config
participant Stack as usb_stack / usb_device
participant App as 应用 (HID/音频/MSD 回调)
Host->>Hw: 复位/枚举 (SETUP)
Hw->>Isr: 中断 (intr_usb/intr_txe)
Isr->>Cfg: 查询中断向量表
Cfg->>Stack: 派发到 usb_interrupt_tx/rx 处理
Stack->>Stack: 解析 SETUP (usb_setup_t)
Stack->>Cfg: usb_get_ep_buffer(ep) 取缓冲
Stack->>App: 调用类驱动回调 (HID/音频/MSD)
App->>Stack: 填充数据 (如 HID 报告)
Stack->>Cfg: 写端点 DMA 缓冲
Cfg->>Hw: 触发 IN 传输 (TXCNT/TXADR)
Hw-->>Host: 数据上传
要点:枚举阶段的 SETUP 包走 EP0 的 usb_setup_t 结构(usb_config_var_t 成员),传输阶段按端点号经 usb_get_ep_buffer 取缓冲,由类驱动(HID/音频/MSD)填充数据后经 DMA 上传。整个路径在中断上下文完成,配置层只负责“缓冲分配 + 中断分发”,具体协议逻辑全部在 usb_stack 与类驱动中。
配置选项
串口与 USB 的行为主要通过板级/产品配置头文件中的宏控制(app_config.h、usb_config.h 及各产品配置)。以下是从源码中直接确认的关键配置项:
| 配置宏 | 类型 | 默认值/来源 | 说明 |
|---|---|---|---|
TCFG_COMMON_UART_ENABLE | 开关 | 由产品配置决定 | common_uart_control 模块总开关,关闭后整个公共串口代码不编译 |
COMMON_UART_RX_BUF_SIZE | 整数 | 产品配置 | 串口接收 DMA 缓冲大小,同时决定 uart_rx_ptr 与 uart_rx_data 的长度 |
COMMON_UART_INDEX | 整数 | 产品配置 | 使用的串口实例编号(common_uart_num) |
COMMON_UART_RX_PIN | 引脚枚举 | 产品配置 | 串口接收引脚,低功耗唤醒源 |
COMMON_UART_RX_POWERLOW_ENABLE | 开关 | 0 | 是否使能低功耗状态下串口唤醒(含 P33 下降沿唤醒配置) |
LPUART_RX_ENABLE | 开关 | 0 | 是否使能低功耗串口接收(对应 JL_WL_UART_TypeDef 精简寄存器集) |
UART_POWERLOW_TIMEOUT | 整数(ms) | 500 | 唤醒后禁止休眠的超时窗口,超时后恢复允许睡眠 |
USB_MAX_HW_NUM | 整数 | usb_config.h | 支持的 USB 硬件单元数量,决定中断表与缓冲数组的维度 |
MAX_EP_TX / MAX_EP_RX | 整数 | 5 | 每单元最大端点数(TX/RX 各 5) |
EP0_DMA_SIZE | 整数 | 64+4 | 控制端点 DMA 缓冲大小(数据 + 描述符开销) |
HID_DMA_SIZE | 整数 | 64+4 | HID 端点缓冲大小 |
MSD_DMA_SIZE | 整数 | (64+4)*2 | MSD 端点缓冲大小(双缓冲) |
AUDIO_DMA_SIZE | 整数 | SPK_FRAME_LEN + MIC_FRAME_LEN + 4*2 | 音频端点缓冲大小,随音频帧长变化 |
USB_SETUP_SIZE | 整数 | usb_config.h | SETUP 包缓冲大小(usb_config_var_t.usb_setup_buffer) |
API 参考
common_uart_regiest_receive_callback(void *rx_cb)
注册公共串口接收回调。上层模块(透传、升级、传感器)在初始化时调用,之后每次收到完整数据包都会回调。
参数:
rx_cb(void *): 回调函数指针,实际签名为void (*)(uint8_t *packet, uint32_t size),packet指向uart_rx_data中的数据,size为本次包长。
返回: 无。
说明: 源码注释标注“内部调用”,回调直接保存在全局函数指针 common_uart_irq_rx_handler_callback 中,重复注册会覆盖旧回调。
Source: common_uart_control.c
void *usb_get_ep_buffer(const usb_dev usb_id, u32 ep)
按 USB 硬件单元、端点号与方向返回对应的 DMA 缓冲。
参数:
usb_id(usb_dev): USB 硬件单元索引(当前缓冲为全局静态,主要用于区分单元)。ep(u32): 端点号,低 4 位为端点编号,USB_DIR_IN位表示 IN 方向。
返回: 指向对齐 DMA 缓冲的指针;EP2 OUT 方向返回 NULL(HID 仅支持 IN)。
说明: 标记为 always_inline_when_const_args,常量参数时编译期内联,用于传输热路径。
Source: usb_device_config.c
void usb_isr(const usb_dev usb_id)
USB 控制器统一中断入口,读取并派发 USB/TX/RX 中断到 usb_interrupt_tx/usb_interrupt_rx 向量表。运行于 IRQ 上下文。
Source: usb_device_config.c
故障模式、边界情况与并发
- 低功耗首包丢失(已知硬件约束):串口唤醒后第一包数据可能丢失,源码明确要求“发送正式数据前先发送一个空包,并在
UART_POWERLOW_TIMEOUT(500 ms)内发出后续有效包”。协议设计必须容忍首包丢失或采用“空包唤醒”握手。 - 唤醒超时与休眠竞态:
uart_rx_wakeup_callback用sys_timeout_add/sys_timer_modify维护 500 ms 窗口,若应用处理慢于窗口,系统会重新允许休眠,导致后续数据丢失。接收回调中应快速搬走数据,避免长时间占用。 - DMA 缓冲对齐与大小:串口与 USB 的 DMA 缓冲都要求
ALIGNED(4),且大小必须覆盖最大包(USB 端点缓冲含 4 字节 DMA 描述符开销,如64+4)。若产品修改端点或帧长(如加大音频SPK_FRAME_LEN),必须同步调整AUDIO_DMA_SIZE等宏,否则 DMA 越界。 - EP2 OUT 无缓冲:
usb_get_ep_buffer对 EP2 OUT 返回NULL,HID 类驱动若向该方向发送会得到空指针,需在驱动层规避(HID 中断传输本身是 IN-only)。 - IRQ 上下文约束:
usb_isr与串口中断都在中断上下文运行,回调必须短小、禁止阻塞/休眠调用,否则会拉长中断关闭窗口,影响 BLE 协议栈时序。 - 多 USB 硬件单元隔离:中断表按
USB_MAX_HW_NUM分组,但端点缓冲是全局静态的;若同时启用多个单元,需确认缓冲复用策略,避免两个控制器争用同一缓冲。
性能与运维注意事项
- DMA 搬运为主:串口接收用 DMA 环形缓冲(
RXSADR/RXEADR),USB 端点预分配 DMA 缓冲,CPU 仅在中断时搬运/解析,吞吐量受限于外设时钟与总线频率而非 CPU。 - 内联优化热路径:
usb_get_ep_buffer常量参数内联,减少每次传输的调用开销;驱动开发时应保持端点号以常量传入。 - 内存布局:
NOT_KEEP_RAM缓冲不常驻,进入低功耗后由系统回收,唤醒后再初始化;uart_rx_data为非 static 导出符号,便于调试器查看。 - 调试手段:日志宏(
LOG_TAG "[common_uart]"、"[USB]")可开LOG_DUMP_ENABLE/LOG_DEBUG_ENABLE抓取收发数据;uart_debug提供独立调试通道,排查问题时可先确认调试串口是否正常。
扩展点
- 串口接收回调:
common_uart_regiest_receive_callback是串口数据进入应用层的唯一入口,新业务(透传协议、传感器上报)只需注册回调并按包解析,无需改动驱动。 - 串口实例选择:通过
COMMON_UART_INDEX/COMMON_UART_RX_PIN/COMMON_UART_RX_BUF_SIZE可在不改代码的情况下把公共串口挪到任意 UART/LPUART 引脚与实例。 - USB 复合设备:
usb_device_config.c的端点-缓冲映射表是扩展新类别的锚点——新增端点需同步增加 DMA 缓冲、中断向量与usb_get_ep_buffer分支;类协议逻辑在usb_stack之上实现,参考 HID 鼠标示例apps/demo/hid/examples/mouse_dual/mouse_usb.c。 - 主机模式:
usb_host/usb_storage复用同一配置层的主收发中断(usb_mtx_interrupt/usb_mrx_interrupt),可扩展 U 盘以外的设备类。
相关文件
- common_uart_control.c / common_uart_control.h
- uart_hal.h / lpuart.h / uart_debug.c
- usb_device_config.c / usb_device.c / usb_suspend_resume.c
- usb_config.h / usb_stack.h / usb_host.h
相关链接
- 固件升级(串口升级通道
testbox_uart_update):见“固件升级”目录页 - AT 命令透传(
at_char_uart):见“AT 命令”目录页 - FindMy 传感器串口(
ble_fmy_sensor_uart):见“FindMy”目录页 - HID 鼠标与 USB 复合设备应用:见“HID 设备”目录页
- 低功耗睡眠与电源管理:见“电源管理”目录页