news 2026/9/20 22:34:36

MicroPython pyboard 化身 USB 鼠标:从 boot.py 配置到加速度计控制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MicroPython pyboard 化身 USB 鼠标:从 boot.py 配置到加速度计控制实战

MicroPython pyboard 化身 USB 鼠标:从 boot.py 配置到加速度计控制实战

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

本教程基于仓库中的 docs/pyboard/tutorial/usb_mouse.rst 展开,讲解如何把 pyboard 从默认的 U 盘模式切换为 USB 鼠标(HID 设备)模式:先通过boot.py中的pyb.usb_mode('VCP+HID')完成模式切换,再借助pyb.USB_HID().send()手动发送鼠标事件,最后用板载加速度计实现"倾斜即移动"的体感鼠标。读完本文,你将掌握 pyboard USB 设备接口的配置原理、HID 报告的数据格式,以及安全模式(safe mode)这一在 HID 模式下救急编辑文件的关键技巧,并能举一反三地自定义 HID 键盘等设备。

pyboard 的 USB 设备本质:接口(interface)而非单一实体

pyboard 本身就是一个 USB 设备,默认配置为"虚拟串口 + 大容量存储(U 盘)"。在 ports/stm32/usb.c 的源码注释中,作者明确阐述了其设计哲学:

MicroPython bindings for USB:pyb.usb_mode(...)configures the USB on the board. The USB itself is not an entity, rather the interfaces are, and can be accessed by creating objects, such aspyb.USB_VCP()andpyb.USB_HID().

也就是说,USB 本身不是一个对象,而是多个**接口(interface)**的组合,每个接口可以通过独立的 Python 对象访问:

  • pyb.USB_VCP()—— 虚拟串口接口,用于读写数据、承载 REPL;
  • pyb.USB_HID()—— 人机交互设备接口,用于发送/接收 HID 报告(本例中即鼠标事件);
  • MSC 接口 —— 大容量存储,即你看到的 U 盘。

这套接口组合在 ports/stm32/modpyb.c 中注册到pyb模块:pyb.usb_modepyb.USB_HIDpyb.USB_VCPpyb.hid_mousepyb.hid_keyboard(其中后两者仅在MICROPY_HW_USB_HID使能时编译,旧版遗留的全局发送函数已标记为 deprecated,推荐使用USB_HID.send)。

第一步:编辑 boot.py,开启 VCP+HID 模式

boot.py是 pyboard 上电后最先执行的脚本,负责配置 USB 和文件系统(应用代码放在main.py)。全新的boot.py大致如下:

# boot.py -- run on boot to configure USB and filesystem # Put app code in main.py import pyb #pyb.main('main.py') # main script to run after this one #pyb.usb_mode('VCP+MSC') # act as a serial and a storage device #pyb.usb_mode('VCP+HID') # act as a serial device and a mouse

启用鼠标模式只需取消最后一行注释:

pyb.usb_mode('VCP+HID') # act as a serial device and a mouse

如果你已经改过boot.py,最小的可用写法只有两行:

import pyb pyb.usb_mode('VCP+HID')

这行代码告诉 pyboard 在上电时把自己配置为同时具备 VCP(Virtual COM Port,虚拟串口)和 HID(Human Interface Device,本例为鼠标)两种 USB 接口的设备。

注意:此时板子同时还是 VCP 虚拟串口,因此你依然可以用串口程序连接 REPL 交互(这也是后续"手动发送鼠标事件"的前提)。

保存文件后,弹出/卸载 pyboard 的 U 盘,再按 RST 复位键。此时电脑应能识别出一个新的鼠标设备——恭喜,你的 pyboard 已经"变成"鼠标了。

pyb.usb_mode 支持的全部模式

在 ports/stm32/usb.c 中定义了完整的模式表(pyb_usb_mode_table),pyb.usb_mode接受的第一个参数即表中字符串:

模式字符串含义默认 PID 宏
'VCP'仅虚拟串口MICROPY_HW_USB_PID_CDC
'MSC'仅大容量存储MICROPY_HW_USB_PID_MSC
'VCP+MSC'串口 + U 盘(默认)MICROPY_HW_USB_PID_CDC_MSC
'VCP+HID'串口 + HID(鼠标/键盘)MICROPY_HW_USB_PID_CDC_HID
'VCP+MSC+HID'串口 + U 盘 + HID 三合一MICROPY_HW_USB_PID_CDC_MSC_HID

旧名称(如'CDC''CDC+MSC''CDC+HID')出于向后兼容仍然支持(对应源码中的deprecated_str字段)。此外,pyb.usb_mode()不带参数调用时返回当前模式字符串;传入None则禁用 USB。部分板卡还支持双串口('2xVCP'等),取决于MICROPY_HW_USB_CDC_NUM的编译配置。

第二步:手动发送鼠标事件,理解 HID 报告格式

用串口程序连接 REPL,依次输入以下命令:

>>> hid = pyb.USB_HID() >>> hid.send((0, 100, 0, 0)) # (button status, x-direction, y-direction, scroll)

鼠标应当向右移动 100 像素。send()接收的元组包含 4 个字节,即一份标准的 4 字节鼠标 HID 报告:

  • button status(按键状态):bit0=左键、bit1=右键、bit2=中键,按下对应的位就置 1;
  • x-direction(x 方向位移):正数向右、负数向左(有符号 8 位整数);
  • y-direction(y 方向位移):正数向下、负数向上(注意坐标系与屏幕显示习惯相反);
  • scroll(滚轮):滚动量,正负对应滚轮方向。

100表示在 x 方向移动了 100 像素。报告是"相对位移"语义:每次send发送的是一个增量,多次发送即累积移动。

让鼠标左右摆动

>>> import math >>> def osc(n, d): ... for i in range(n): ... hid.send((0, int(20 * math.sin(i / 10)), 0, 0)) ... pyb.delay(d) ... >>> osc(100, 50)

osc的第一个参数n是发送的鼠标事件次数,第二个参数d是两次事件之间的延时(毫秒)。正弦函数把 x 位移变成平滑的正负交替,鼠标便来回摆动。可以尝试修改幅度(如 20)、频率(如i/10)和延时(如 50)来感受参数对运动轨迹的影响。

练习题:利用math.cosmath.sin组合 x、y 分量,让鼠标画出一个圆。

USB_HID.send 的底层实现与边界

pyb.USB_HID()在 ports/stm32/usb.c 中实现为pyb_usb_hid_send。其核心逻辑有两点值得注意:

  1. 两种入参形式:既可以直接传bytes/bytearray,也可以传元组或整数列表——元组/列表会被逐个转换成uint8_t字节(这正是本教程hid.send((0, 100, 0, 0))可用的原因);
  2. 长度上限:当入参是元组/列表时,内部使用 8 字节临时缓冲(byte temp_buf[8]),超过 8 字节会抛出ValueError: tuple/list too large for HID report; use bytearray instead——自定义更大的报告(如键盘 8 字节报告、多媒体按键报告)时,应改用bytearray传入;
  3. 返回值send成功后返回发送的字节数;若底层USBD_HID_SendReport失败则返回0,可用于检测链路状态。

send配套的还有USB_HID.recv(data, timeout=5000)方法(见 ports/stm32/usb.c),用于接收主机发来的 HID 报告——在双向 HID 通信场景(如模拟游戏手柄)中会用到。

第三步:用加速度计做一个"体感鼠标"

让鼠标跟随板子的倾斜角度移动,核心代码如下(可临时在 REPL 中逐行输入验证):

import pyb switch = pyb.Switch() accel = pyb.Accel() hid = pyb.USB_HID() while not switch(): hid.send((0, accel.x(), accel.y(), 0)) pyb.delay(20)

但问题来了:板子当前是 HID 模式,没有 U 盘,你无法挂载文件系统去编辑main.py,也无法改回boot.py……此时就需要进入**安全模式(safe mode)**来"救场"。

进入安全模式的完整步骤

安全模式的完整说明见 reset tutorial,这里直接给出操作步骤:

  1. 按住USR 按键
  2. 保持按住 USR,按下并松开RST 按键
  3. 此时 LED 会循环显示:绿 → 橙 → 绿+橙 → 回到绿……;
  4. 持续按住 USR,直到只有橙色 LED 亮起,然后松开 USR;
  5. 橙色 LED 会快速闪烁 4 次后熄灭;
  6. 现在你已处于安全模式。

安全模式的源码级原理

安全模式为什么能让你重新访问 U 盘?从 ports/stm32/boardctrl.c 可以看到,boot.pymain.py的执行都受复位模式(reset_mode)控制:

// boardctrl_run_boot_py bool run_boot_py = state->reset_mode != BOARDCTRL_RESET_MODE_SAFE_MODE; // boardctrl_run_main_py bool run_main_py = state->reset_mode != BOARDCTRL_RESET_MODE_SAFE_MODE && pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL;

安全模式(BOARDCTRL_RESET_MODE_SAFE_MODE,见 ports/stm32/boardctrl.h)下两者都不执行,于是板子以默认 USB 配置启动——U 盘回来了,你可以正常编辑boot.pymain.py。注意此时boot.py保持不变,因为我们编辑完main.py后还要回到 HID 模式。

复位模式的选择逻辑在 ports/stm32/boardctrl.c 的update_reset_mode中:上电时检测 USR 开关是否按下,若按下则 LED 以不同组合循环指示可选的复位模式,通过 LED 组合编码选择。教程中的"绿 → 橙 → 绿+橙"循环正是这一 LED 状态机在只有开关的 pyboard 上的呈现;模式选定后 LED 会闪烁对应次数加以确认。

提示:安全模式下boot.py不执行,USB 恢复正常默认配置,因此 U 盘可见、串口可用,方便你修改文件或调试。

保存并运行

把上述代码保存为main.pyboot.py保持pyb.usb_mode('VCP+HID')不动),弹出 U 盘并复位。板子现在就是一只"倾斜控制"的鼠标:

  • 倾斜角度映射为 x/y 位移,板子角度变化会带动鼠标移动;
  • USR 按键即可停止鼠标运动(while not switch()退出循环);
  • 你会发现y 轴方向是反的——这是加速度计坐标轴方向决定的,修复方法很简单:在hid.send()的 y 分量前加负号:
hid.send((0, accel.x(), -accel.y(), 0))

试试看能不能让鼠标纹丝不动地停在原地(那说明你握板的手法极其稳定)。

第四步:恢复正常模式

如果放任不管,pyboard 每次插入都会以鼠标模式运行。恢复正常模式的流程:

  1. 先进入安全模式(步骤同上);
  2. 编辑boot.py,把VCP+HID那一行注释掉:
#pyb.usb_mode('VCP+HID') # act as a serial device and a mouse
  1. 保存文件,弹出 U 盘,复位板子。

此时boot.py不再切换 USB 模式,pyboard 恢复为默认的"串口 + U 盘"形态。

进阶:鼠标协议之外——键盘模式与自定义报告描述符

pyb.usb_mode在 ports/stm32/usb.c 中暴露了若干可选关键字参数,其中hid参数决定了 HID 设备的协议描述:

  • 默认值即pyb.hid_mousesubclass=1即 boot 子类、protocol=2即鼠标协议、轮询间隔 8ms、内置鼠标报告描述符,见 ports/stm32/usb.c);
  • pyb.hid_keyboard即可把 pyboard 变成 USB 键盘(protocol=1、8ms 轮询,见 ports/stm32/usb.c);
  • 也可以传自定义 5 元组(subclass, protocol, max_packet_len, polling_interval, report_desc),其中report_desc是一份完整的 HID 报告描述符(bytes),源码会将其复制保存以防被 GC 回收(ports/stm32/usb.c)。

示例:指定 VID/PID 的鼠标模式:

pyb.usb_mode('VCP+HID', vid=0xf055, pid=0x9800) # 自定义厂商/产品 ID pyb.usb_mode('VCP+HID', hid=pyb.hid_keyboard) # 变成键盘

除此之外,pyb.usb_mode还支持port(选择 USB 端口,多 USB 的板卡可用)、msc(指定大容量存储逻辑单元,如pyb.Flash()或 SD 卡)等关键字参数。所有这些配置最终汇聚到 ports/stm32/usb.c 的pyb_usb_dev_init,由 STM32 USB 设备栈(usbd_cdc_msc_hid,见 ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h)统一配置 VID/PID、选择接口组合并初始化各接口。

总结

本文完整走通了 pyboard 变身 USB 鼠标的四个阶段:boot.py 配置模式 → REPL 手动验证 → 加速度计体感控制(含安全模式自救)→ 恢复正常模式。关键要点回顾:

  1. pyb.usb_mode('VCP+HID')是切换鼠标模式的唯一入口,VCP 接口保留使 REPL 仍然可用;
  2. USB_HID.send((btn, x, y, scroll))发送 4 字节相对位移报告,元组会被自动转为字节,超 8 字节需用bytearray
  3. 加速度计方案的核心是while not switch(): hid.send((0, accel.x(), -accel.y(), 0))循环,注意 y 轴取反;
  4. HID 模式下无法挂载 U 盘,通过"按住 USR + 复位"进入安全模式即可重新编辑文件;
  5. 修改hid参数即可把同一套机制复用到 USB 键盘或自定义 HID 设备。

如果想继续深入,可以阅读 pyb.USB_HID 参考文档(文中未列,实际接口见 modpyb.c)以及 pyb.usb_mode 相关文档,并结合 ports/stm32/usbd_hid_interface.c 了解报告收发的中断级实现。

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 22:33:00

本地AI技能调度中枢:OpenClaw+Hermes架构原理与实战

1. 项目概述:这不是一个“AI工具合集”,而是一套可落地的本地化技能调度中枢“龙虾 Skill 技能库|OpenClawHermes 全集成 一键调用所有 AI 技能”——这个标题里没有一个词是虚的,但每一个词背后都藏着容易被忽略的工程现实。我从…

作者头像 李华
网站建设 2026/9/20 22:32:54

哈工大AI课程资料使用指南:从机器学习到强化学习的实战路径

简介:面向哈尔滨工业大学人工智能专业学子的课程学习与项目实践资料合集,覆盖机器学习、深度学习、自然语言处理、计算机视觉、强化学习等核心方向,适合本科日常自学、期末复习、考研复试准备以及课程设计/毕业设计参考。资源共348个文件&…

作者头像 李华
网站建设 2026/9/20 22:32:18

高斯模糊 RenderScript 效率低?Codex 走 TaoToken 对照 handleBit 排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 22:28:34

STM32F407移植Zephyr:设备树配置与定时器触发DAC实战

当年我从 Keil 切到 Zephyr 的时候,第一反应是“这也太复杂了吧”。一个简单的串口打印,FreeRTOS 工程里配个库调用就行,Zephyr 里要折腾 west、SDK、设备树、Kconfig,还没跑通就先被工具链劝退。但等我把整个流程走通之后&#x…

作者头像 李华