73 usb-USB主机/从机控制
作者:黄何 | 最后修改:2026-10-27
一、概述
usb 是 LuatOS 的 USB 控制器操作核心库,用来选择 USB 工作在从机(DEVICE)、主机(HOST)还是 OTG 协商模式,以及在从机模式下组合出需要的设备类,在主机模式下接收外设的接入和断开事件。
对从机模式来说,本库负责“设备对外长什么样”:可以先清除已有的设备类,再按需添加 CDC-ACM 虚拟串口、自定义 HID 或标准键盘 HID、MSC 大容量存储(U 盘),并可以自定义 VID、PID。
对主机模式来说,本库负责“模组怎么接外设”:把 USB 切换成主机,枚举插入的 U 盘、USB 摄像头、HUB、RNDIS/ECM 网卡,枚举完成(usb.EV_CONNECT)后通过回调把设备类和 app_id 交给上层业务。
使用时有两条必须遵守的规则:
-
所有会改动 USB 配置的接口,包括 usb.mode、usb.add_class、usb.clear_all_class、usb.vid、usb.pid,都必须在 USB 外设掉电状态下调用,也就是先用
pm.power(pm.USB, false)断电,改完再用pm.power(pm.USB, true)上电;USB 正在工作时调用会直接返回 false。 -
从机模式下,只有 HID 类(usb.HID_CM、usb.HID_KB)需要用本库的 usb.tx、usb.rx 收发数据;CDC-ACM 虚拟串口在 Lua 层就是一条串口,直接用 uart 库操作 uart.VUART_0 即可,不需要也不能用 usb.tx 发送。
--从机功能
pm.power(pm.USB, false) --确保USB外设是掉电状态
usb.clear_all_class(0) --清除掉之前配置的设备类
usb.mode(0, usb.DEVICE) --usb设置成从机模式
usb.add_class(0, usb.CDC_ACM, 1) --使用1个CDC-ACM虚拟串口功能
pm.power(pm.USB, true) --USB上电初始化开始工作
--主机功能
pm.power(pm.USB, false) --确保USB外设是掉电状态
usb.mode(0, usb.HOST) --usb设置成主机模式
pm.power(pm.USB, true) --USB上电初始化开始工作
二、核心示例
1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的 demo,请参考 LuatOS 仓库 中的 olddemo/demo/usb(主机模式枚举 U 盘)、olddemo/demo/usb_net(RNDIS/ECM 网卡)、olddemo/demo/usb_uart(CDC-ACM 虚拟串口)目录。
2.1 从机模式:CDC-ACM 虚拟串口
-- 先断电再配置,配置完再上电,这是本库所有配置接口的固定顺序
pm.power(pm.USB, false)
usb.mode(0, usb.DEVICE) -- 从机模式
usb.clear_all_class(0) -- 清掉之前配置的设备类
usb.add_class(0, usb.CDC_ACM, 1) -- 1 路虚拟串口,最多 3 路
pm.power(pm.USB, true) -- 上电后 PC 侧出现一个虚拟串口
local uartid = uart.VUART_0 -- USB 虚拟串口固定用这个 id
uart.setup(uartid, 115200, 8, 1) -- 纯虚拟串口,波特率实际不起作用
uart.on(uartid, "receive", function(id, len)
local s = uart.read(id, len)
if s and #s > 0 then
uart.write(id, s) -- 收到什么回什么
end
end)
-- 对端(PC 串口工具)打开/关闭虚拟串口的通知
sys.subscribe("VUART_STATE", function(vuart_id, state)
log.info("vuart", vuart_id, "is connect", state)
end)
2.2 主机模式:枚举 U 盘并挂载文件系统
local u_disk_app_id
local u_disk_ready = false
usb.on(0, function(usb_id, class, app_id, event, hub_addr, hub_port, dev_addr)
if class == usb.MSC and event == usb.EV_CONNECT then
u_disk_app_id = app_id
u_disk_ready = true
log.info("usb", "U盘接入", hub_addr, hub_port, dev_addr)
elseif class == usb.MSC and event == usb.EV_DISCONNECT then
u_disk_ready = false
u_disk_app_id = nil
end
end)
pm.power(pm.USB, false)
usb.mode(0, usb.HOST) -- 主机模式
pm.power(pm.USB, true)
sys.taskInit(function()
while true do
if u_disk_ready then
-- app_id 由回调给出,挂载到 /udisk 后按普通文件操作即可
fatfs.mount(fatfs.USB, "/udisk", u_disk_app_id)
log.info("udisk", json.encode(fatfs.getfree("/udisk")))
fatfs.unmount("/udisk")
end
sys.wait(1000)
end
end)
三、常量详解
核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;
每个常量对应的常量取值仅做日志打印时查询使用,不要将这个常量取值用做具体的业务逻辑判断,因为LuatOS内核固件可能会变更每个常量对应的常量取值;
如果用做具体的业务逻辑判断,一旦常量取值发生改变,业务逻辑就会出错;
usb.DEVICE
常量含义:USB 从机模式,模组作为设备被 PC、主控或 USB 主机枚举;
数据类型:number;
示例代码:usb.mode(0, usb.DEVICE);
usb.HOST
常量含义:USB 主机模式,模组作为主机去枚举 U 盘、摄像头、HUB、RNDIS/ECM 网卡等外设;
数据类型:number;
示例代码:usb.mode(0, usb.HOST);
usb.OTG
常量含义:USB OTG 协商模式,由硬件 ID 脚或对端角色决定当前是主机还是从机;
数据类型:number;
示例代码:usb.mode(0, usb.OTG);
usb.HUB
常量含义:集线器类,主机模式插入 HUB 时在 usb.on 回调中上报;
数据类型:number;
示例代码:if class == usb.HUB then ... end;
usb.CAMERA
常量含义:摄像头类,主机模式插入 USB 摄像头时在 usb.on 回调中上报,后续交给 camera 库使用;
数据类型:number;
示例代码:if class == usb.CAMERA then ... end;
usb.CDC_ACM
常量含义:虚拟串口类,从机模式可通过 usb.add_class 配置,Lua 侧用 uart.VUART_0 收发;
数据类型:number;
示例代码:usb.add_class(0, usb.CDC_ACM, 1);
usb.MSC
常量含义:大容量存储类,也就是 U 盘、TF 卡;主机模式插入 U 盘时在 usb.on 回调中上报,可用 fatfs 库挂载;
数据类型:number;
示例代码:if class == usb.MSC then ... end;
usb.CDC_ECM
常量含义:虚拟网卡类,主机模式插入 ECM 网卡时在 usb.on 回调中上报,后续交给 netdrv 库使用;
数据类型:number;
示例代码:if class == usb.CDC_ECM then ... end;
usb.RNDIS
常量含义:虚拟网卡类,主机模式插入 RNDIS 网卡时在 usb.on 回调中上报,后续交给 netdrv 库使用;
数据类型:number;
示例代码:if class == usb.RNDIS then ... end;
usb.AUDIO
常量含义:audio 音频类,用于标识 USB 音频设备;
数据类型:number;
示例代码:if class == usb.AUDIO then ... end;
usb.HID_CM
常量含义:HID 设备类中的自定义类型,用于透传数据,不能和标准键盘 usb.HID_KB 同时使用;
数据类型:number;
示例代码:usb.add_class(0, usb.HID_CM, 1);
usb.HID_KB
常量含义:HID 设备类中的标准键盘,常见扫码枪就属于这一类,不能和自定义类型 usb.HID_CM 同时使用;
数据类型:number;
示例代码:usb.add_class(0, usb.HID_KB, 1);
usb.EV_CONNECT
常量含义:连接事件;从机模式表示设备已经被主机枚举成功,主机模式表示外设枚举完成,此时回调的 param1、param2、param3 依次为 hub 地址、设备所在的 hub port 序号、设备自身地址;
数据类型:number;
示例代码:if event == usb.EV_CONNECT then ... end;
usb.EV_DISCONNECT
常量含义:断开事件,参数含义与 usb.EV_CONNECT 相同;
数据类型:number;
示例代码:if event == usb.EV_DISCONNECT then ... end;
usb.EV_NEW_RX
常量含义:有新的数据到来;
数据类型:number;
示例代码:if event == usb.EV_NEW_RX then ... end;
usb.EV_TX_DONE
常量含义:数据已经全部发送完成;
数据类型:number;
示例代码:if event == usb.EV_TX_DONE then ... end;
usb.EV_RX_ERROR
常量含义:接收出错;
数据类型:number;
示例代码:if event == usb.EV_RX_ERROR then ... end;
usb.EV_TX_ERROR
常量含义:发送出错;
数据类型:number;
示例代码:if event == usb.EV_TX_ERROR then ... end;
usb.EV_SUSPEND
常量含义:usb 从机挂起,通常是主机侧不再供电或进入休眠;
数据类型:number;
示例代码:if event == usb.EV_SUSPEND then ... end;
usb.EV_RESUME
常量含义:usb 从机从挂起状态恢复;
数据类型:number;
示例代码:if event == usb.EV_RESUME then ... end;
usb.EV_ERR_STOP
常量含义:usb 从机遇到故障停止工作,可以调用 usb.reset_device 尝试恢复;
数据类型:number;
示例代码:if event == usb.EV_ERR_STOP then ... end;
四、函数详解
usb.mode(id, mode)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
设置 USB 的工作模式,必须在 USB 外设掉电不工作时进行设置。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;芯片只有一条 USB 线时填 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
mode
参数含义:工作模式;
数据类型:number;
取值范围:usb.HOST 主机模式、usb.DEVICE 从机模式、usb.OTG 协商模式;
是否必选:可选,不填按 usb.DEVICE 处理;
注意事项:必须在 pm.power(pm.USB, false) 之后、pm.power(pm.USB, true) 之前调用,USB 正在工作时会返回 false;
参数示例:usb.HOST,usb.DEVICE,usb.OTG;
返回值
local result = usb.mode(id, mode)
result
含义说明:表示返回结果,成功返回 true,失败返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:总线 id 填错、所选模式芯片不支持、USB 外设正在工作时返回 false;
返回示例:true;
示例
pm.power(pm.USB, false)
usb.mode(0, usb.DEVICE)
pm.power(pm.USB, true)
usb.add_class(id, class, num)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
设置从机模式下 USB 支持的设备类和数量,必须在 USB 外设掉电不工作时进行设置。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
class
参数含义:设备类;
数据类型:number;
取值范围:从机模式支持 usb.CDC_ACM、usb.HID_CM、usb.HID_KB、usb.MSC;主机模式不需要配置;
是否必选:可选,不填按 usb.CDC_ACM 处理;
注意事项:usb.HID_CM 和 usb.HID_KB 不能同时使用;选错设备类或端点数量超过芯片允许的最大值时返回 false;
参数示例:usb.CDC_ACM,usb.HID_CM,usb.HID_KB,usb.MSC;
num
参数含义:该设备类的数量;
数据类型:number;
取值范围:只有从机的 usb.CDC_ACM 允许至多 3 个,其他设备类只允许 1 个,超过时会强制改成所允许的最大值;
是否必选:可选,不填按 1 处理;
注意事项:可先用 usb.get_free_ep_num 查看剩余端点数;
参数示例:1,2,3;
返回值
local result = usb.add_class(id, class, num)
result
含义说明:表示返回结果,成功返回 true,失败返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:总线 id 填错、所选设备类不支持、端点数量超过芯片允许的最大值、USB 外设正在工作时返回 false;
返回示例:true;
示例
pm.power(pm.USB, false)
usb.add_class(0, usb.CDC_ACM, 3) --使用3个CDC-ACM虚拟串口功能
usb.add_class(0, usb.HID_CM, 1) --使用1个自定义HID功能
pm.power(pm.USB, true)
usb.clear_all_class(id)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
清除掉当前配置的设备类,必须在 USB 外设掉电不工作时进行设置;一般和 usb.add_class 配合使用,先清空再按需添加,避免残留上一次的配置。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
返回值
local result = usb.clear_all_class(id)
result
含义说明:表示返回结果,成功返回 true,失败返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:总线 id 填错、USB 外设正在工作时返回 false;
返回示例:true;
示例
pm.power(pm.USB, false)
usb.clear_all_class(0) --清除掉之前配置的设备类
usb.add_class(0, usb.CDC_ACM, 3) --使用3个CDC-ACM虚拟串口功能
usb.add_class(0, usb.HID_KB, 1) --使用1个标准键盘功能
pm.power(pm.USB, true)
usb.get_free_ep_num(id)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
返回当前剩余的端点数,配置设备类之前可以先查一下还能不能加。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
返回值
local num = usb.get_free_ep_num(id)
num
含义说明:剩余的端点数;
数据类型:number;
取值范围:总线 id 填错时直接返回 0;
注意事项:返回值只表示剩余端点数量,具体设备类能否添加还受芯片支持情况限制;
返回示例:4;
示例
log.info(usb.get_free_ep_num(0))
usb.vid(id, vid)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
设置或获取 USB 的 VID,设置必须在 USB 外设掉电不工作时进行,获取没有限制。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
vid
参数含义:想要设置的 VID 值;
数据类型:number;
取值范围:16 位数值,通常写成十六进制;
是否必选:可选,留空则只做获取、不做设置;
注意事项:设置时必须在 pm.power(pm.USB, false) 之后调用;
参数示例:0x1234;
返回值
local result, vid = usb.vid(id, vid)
result
含义说明:设置成功或者获取 VID 时返回 true,否则返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:总线 id 填错、芯片不支持设置、USB 外设正在工作等情况下返回 false;
返回示例:true;
vid
含义说明:当前 VID 值;
数据类型:number;
取值范围:16 位数值;
注意事项:暂无;
返回示例:0x1234;
示例
pm.power(pm.USB, false)
usb.vid(0, 0x1234)
pm.power(pm.USB, true)
-- 只读取当前 VID
local ok, vid = usb.vid(0)
log.info("usb", "vid", string.format("0x%04X", vid))
usb.pid(id, pid)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
设置或获取 USB 的 PID,设置必须在 USB 外设掉电不工作时进行,获取没有限制。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
pid
参数含义:想要设置的 PID 值;
数据类型:number;
取值范围:16 位数值,通常写成十六进制;
是否必选:可选,留空则只做获取、不做设置;
注意事项:设置时必须在 pm.power(pm.USB, false) 之后调用;
参数示例:0x5678;
返回值
local result, pid = usb.pid(id, pid)
result
含义说明:设置成功或者获取 PID 时返回 true,否则返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:总线 id 填错、芯片不支持设置、USB 外设正在工作等情况下返回 false;
返回示例:true;
pid
含义说明:当前 PID 值;
数据类型:number;
取值范围:16 位数值;
注意事项:暂无;
返回示例:0x5678;
示例
pm.power(pm.USB, false)
usb.pid(0, 0x5678)
pm.power(pm.USB, true)
usb.on(id, func)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
注册 USB 事件回调,主机模式下必须在 usb.mode 切到主机、USB 上电之前注册好,否则可能漏掉第一个设备的接入事件。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:同一路 USB 重复注册会覆盖上一个回调;
参数示例:0;
func
参数含义:回调方法;
数据类型:function;
取值范围:形参依次为 usb_id、class、app_id、event、param1、param2、param3,共 7 个;
是否必选:传入 function 时注册,传入其他值相当于只清除回调;
注意事项:只有 usb.EV_CONNECT、usb.EV_DISCONNECT 两个事件的 param1、param2、param3 有值,其余事件这三个参数为 nil;回调的 7 个参数是 2026-04-06 才加的,更早的固件(EC7xx 的 V2020~V2030)回调只有 3 个参数:usb_id、app_id、class;
参数示例:见下方示例;
返回值
nil
示例
usb.on(0, function(usb_id, class, app_id, event, param1, param2, param3)
log.info("usb", usb_id, class, app_id, event)
-- param1 为 hub 地址,param2 为设备所在的 hub port 序号,param3 为设备自身地址
if event == usb.EV_CONNECT then
log.info("usb", "设备接入", class, app_id, param1, param2, param3)
elseif event == usb.EV_DISCONNECT then
log.info("usb", "设备断开", class, app_id, param1, param2, param3)
end
end)
usb.tx(id, data, app_id)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
USB 发送数据,目前仅限 HID 设备使用;CDC-ACM 虚拟串口直接使用 uart 库操作,不能用本接口发送。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
data
参数含义:需要发送的数据;
数据类型:string 或 zbuff;
取值范围:string 按其实际长度发送,zbuff 按其已使用长度发送;
是否必选:必须传入此参数;
注意事项:HID 报文长度不足时底层会补 0;
参数示例:"1234";
app_id
参数含义:设备应用 id;
数据类型:number;
取值范围:由 usb.on 回调给出;
是否必选:必须传入此参数;
注意事项:注意本接口的参数顺序是 (id, data, app_id),app_id 在最后;
参数示例:1;
返回值
local result = usb.tx(id, data, app_id)
result
含义说明:成功返回 true,否则返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:总线 id 填错、所填设备类不支持直接发送数据等情况下返回失败;
返回示例:true;
示例
-- HID上传数据
usb.tx(0, "1234", 1) -- usb hid上传0x31 0x32 0x33 0x34 + N个0
usb.rx(id, buff, app_id)
适用固件:2025-12-15 及以后的固件(EC7xx 从 V2020 / 2025.12.31 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
以 zbuff 形式读取接收到的数据,一次读出全部缓存数据存入 buff,buff 空间不够时会自动扩展。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
buff
参数含义:接收数据用的 zbuff 对象;
数据类型:zbuff;
取值范围:任意已创建的 zbuff,空间不足会自动扩容;
是否必选:必须传入此参数;
注意事项:传入非 zbuff 对象时直接返回 0;
参数示例:zbuff.create(64);
app_id
参数含义:设备应用 id;
数据类型:number;
取值范围:由 usb.on 回调给出;
是否必选:必须传入此参数;
注意事项:注意本接口的参数顺序是 (id, buff, app_id),app_id 在最后;
参数示例:1;
返回值
local len = usb.rx(id, buff, app_id)
len
含义说明:返回读到的长度,并把 zbuff 的写指针后移;
数据类型:number;
取值范围:0 或正数,0 表示当前没有缓存数据;
注意事项:源码注释标注该接口当前仍处于“暂时无法使用”状态,正式项目使用前请先在目标固件上实测;
返回示例:4;
示例
local buff = zbuff.create(64)
local len = usb.rx(0, buff, 1)
log.info("usb", "rx", len)
usb.reset_device(id, app_id)
适用固件:2026-04-07 及以后的固件(EC7xx 从 V2032 / 2026.04.09 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
USB 主机重新枚举从机,常用于设备异常停止(usb.EV_ERR_STOP)或接触不良导致通讯中断后的快速恢复。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:只适用于主机模式;
参数示例:0;
app_id
参数含义:设备应用 id;
数据类型:number;
取值范围:从 usb.on 回调中拿到;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:2;
返回值
local result = usb.reset_device(id, app_id)
result
含义说明:true 表示已经开始重新枚举,false 表示无法枚举;
数据类型:boolean;
取值范围:true 或 false;
注意事项:暂无;
返回示例:true;
示例
usb.reset_device(0, 2)
usb.debug(id, on_off)
适用固件:2026-02-05 及以后的固件(EC7xx 从 V2026 / 2026.02.28 起,Air1601/1602/1603 从 V1024 / 2026.07.07 起),详见第六章
功能
配置 USB 调试信息输出开关,排查枚举失败、端点不足等问题时打开。
参数
id
参数含义:usb 总线 id;
数据类型:number;
取值范围:默认 0;
是否必选:可选,不填按 0 处理;
注意事项:暂无;
参数示例:0;
on_off
参数含义:调试信息开关;
数据类型:boolean;
取值范围:true 开,false 关;
是否必选:可选,不填按 false 处理;
注意事项:调试信息量较大,正式发布时建议关闭;
参数示例:true;
返回值
nil
示例
usb.debug(0, true) --开启调试信息输出
usb.debug(0, false) --关闭调试信息输出
五、产品支持说明
usb 核心库由固件编译开关 LUAT_USE_USB 控制,不同型号、不同固件号默认是否带该库并不一致,使用前请先确认当前固件是否支持,脚本里也可以这样自检:
if usb then
log.info("usb", "当前固件支持 usb 核心库")
else
log.info("usb", "当前固件不支持 usb 核心库")
end
不同产品的 LuatOS 固件对 usb 核心库的支持情况,请以对应固件版本页的核心库支持列表为准:
Air780EX2/Air700ECP/Air780EPM/Air780EGP固件支持列表
Air700ECH/Air780EHM/EHV/EGH/EGG/EHU/EHN固件支持列表
如果对应固件版本页的核心库列表中没有 usb,说明当前固件没有编译这个库,需要换用支持的固件,或者联系合宙确认。
从固件更新记录看,Air1601/Air1602 系列固件已经支持 usb 库,HID 键盘鼠标、U 盘挂载、ECM/RNDIS 虚拟网卡、UAC 音频等能力是随固件版本逐步加入的,具体以对应版本页的更新说明为准。
硬件要求
- 模组需要具备 USB 控制器,并且 USB 的 D+、D- 已正常引出;
- 主机模式下需要给 USB 外设供电,部分开发板用 GPIO 控制 USB 供电使能,例如 Air1601 开发板需要先把 GPIO12 拉高、Air8101 开发板需要先把 GPIO28 拉高,否则枚举不到外设;
- 端点数量有限,CDC-ACM 最多 3 路,其他从机设备类各 1 路,配置前可用 usb.get_free_ep_num 查询。
使用限制
- 从机模式下只有 HID 类需要用 usb.tx、usb.rx 收发;CDC-ACM 虚拟串口统一走 uart.VUART_0;
- usb.HID_CM 与 usb.HID_KB 不能同时使用;
- 所有改配置的接口都必须在 USB 外设掉电时调用,上电后再改会返回 false;
- 主机模式枚举到的 U 盘、摄像头、网卡,需要分别交给 fatfs、camera、netdrv 库继续处理,usb 库只负责枚举和事件通知。
六、接口与固件版本对应关系
usb 库第一次提交是 2025-12-15 的 a58335b93 add:usb控制api,之后每个接口是分批加进来的,并不是所有固件都有全部接口。下表按 gitee 提交记录整理,可以直接判断某个固件版本能不能用某个接口。
「最早可用固件」的取法是:该接口引入日期之后发布的第一个固件版本。两条产品线的版本号不通用,EC7xx 系是 V20xx,Air1601/Air1602/Air1603 是 V10xx。
| 接口 / 常量 | gitee 提交 | EC7xx(Air780EHM/EHV/EGH/EGG/EHU/EHN、Air780EPM/EGP、Air700ECP/ECH、Air8000 系列) | Air1601 / Air1602 / Air1603 |
|---|---|---|---|
| usb.mode、usb.vid、usb.pid、usb.add_class、usb.clear_all_class、usb.get_free_ep_num usb.HOST、usb.DEVICE、usb.OTG、usb.CDC_ACM、usb.MSC、usb.CAMERA、usb.AUDIO |
a58335b93(2025-12-15) |
V2020(2025.12.31)起 | V1024(2026.07.07)起 ① |
| usb.tx、usb.rx、usb.on usb.EV_NEW_RX、usb.EV_TX_DONE、usb.EV_CONNECT、usb.EV_DISCONNECT、usb.EV_SUSPEND、usb.EV_RESUME |
5abbbcef4(2025-12-15) |
V2020(2025.12.31)起 | V1024(2026.07.07)起 ① |
| usb.HID_CM、usb.HID_KB(HID 拆成「自定义」和「标准键盘」两个常量,之前只有一个 HID) | 3fe91babf(2026-01-07) |
V2022(2026.01.16)起 | V1024(2026.07.07)起 ① |
| usb.debug | b47da2cdc(2026-02-05) |
V2026(2026.02.28)起 | V1024(2026.07.07)起 ① |
| usb.on 回调升级为 7 个参数(connect/disconnect 带 hub 地址、hub 端口序号、设备地址),之前只有 3 个参数 | b0226b711(2026-04-06) |
V2032(2026.04.09)起 | V1024(2026.07.07)起 ① |
| usb.reset_device usb.EV_RX_ERROR、usb.EV_TX_ERROR、usb.EV_ERR_STOP |
ec1ecb337(2026-04-07) |
V2032(2026.04.09)起 | V1024(2026.07.07)起 ① |
| usb.HUB、usb.CDC_ECM | ef6a178dc(2026-07-31) |
V2048(2026.08.03)起 | V1026(2026.08.28)起 |
| usb.RNDIS | d7620b506(2026-08-19) |
V2050(2026.08.21)起 | V1026(2026.08.28)起 |
① Air1601/Air1602/Air1603 的固件版本页目前只保留 V1024、V1026、V1028 三个版本,V1024 之前的版本没有列出来,所以这五行只能确认「V1024 及以后可用」,更早的固件是否支持没有核对。如果拿到更老的 1601 固件,建议先用 if usb then 自检。
关于固件编号
同一版本号下不同编号的固件,核心库是同一套,区别只在分区。以 Air1601 为例,101 号固件是 1024KB 脚本区 + 3008KB 文件系统,102 号固件是 4096KB 脚本区 + 1536KB 文件系统,usb 库在两个编号里都能用。
所以判断 usb 库能不能用,看的是版本号(V10xx / V20xx),不是编号(101/102 号)。个别能力是随版本号做进来的固件功能,而不是接口本身,例如 UAC 音频、ECM/RNDIS 网卡:常量 usb.AUDIO、usb.CDC_ECM、usb.RNDIS 出现得早,但真正能工作的固件版本要按上表后两行走。
常见版本组合
- 只做 CDC-ACM 虚拟串口或 HID 透传:EC7xx 用 V2020(2025.12.31)及以后的固件即可,1601 用 V1024 及以后;
- 要做 USB 主机枚举 U 盘、摄像头:EC7xx 建议 V2032(2026.04.09)及以后,此时
usb.on才能拿到 hub 地址和端口;接触不良需要usb.reset_device恢复的,也必须 V2032 及以后; - 要做 USB 网卡(RNDIS/ECM):EC7xx 必须 V2050(2026.08.21)及以后,1601/1602/1603 必须 V1026(2026.08.28)及以后。