1. 项目概述打造你的移动代码工作站在嵌入式开发或者创客项目中我们常常会遇到一个场景设备已经部署在某个角落或者你正带着它进行户外原型测试这时突然发现代码里有个小bug需要紧急修改或者想临时记录一些传感器数据。难道每次都要跑回电脑前连接数据线打开IDE吗这太不“极客”了。今天分享的这个项目就是为了解决这个痛点——一个基于CircuitPython的、完全自包含的便携式文本编辑器。这个项目的核心是让一块搭载了CircuitPython的微控制器板我们用的是Adafruit Feather RP2040 with USB Host变成一台可以独立进行文本编辑的微型工作站。它接上一块3.5英寸的TFT显示屏作为输出一个USB键盘作为输入再配上一块锂电池就能让你在任何地方打开、编辑并保存存储在板载闪存中的文件。它的操作体验借鉴了经典的Linux终端编辑器Nano通过方向键移动光标进行基础的文本插入、删除和保存。对于经常折腾树莓派Pico、ESP32或者各种Feather板卡的开发者来说这相当于给你的开发板配了一个“随身急救包”极大地提升了现场调试和开发的灵活性。2. 硬件选型与核心设计思路2.1 为什么是Feather RP2040 with USB Host硬件是项目的骨架选型直接决定了项目的可行性和体验。这个项目的核心需求很明确运行CircuitPython、驱动显示屏、连接标准USB键盘。市面上能满足这些条件的板卡不少但Feather RP2040 with USB Host版本有几个独特的优势让它成为这个项目的“天选之子”。首先双USB接口的设计是关键。一个Micro-USB口用于供电和编程作为USB设备连接电脑另一个Type-A Host口则专门用于连接USB外设比如我们的键盘。这意味着键盘可以即插即用无需额外的USB Host Shield扩展板大大简化了硬件连接和软件驱动复杂度。其次RP2040芯片本身性能足够强劲双核Arm Cortex-M0处理器和264KB的SRAM足以流畅运行CircuitPython解释器、图形库并处理键盘输入事件。最后Adafruit的Feather生态提供了丰富的“Wing”扩展板选择特别是显示屏扩展板可以像积木一样堆叠避免了复杂的飞线。注意如果你手头只有普通的RP2040开发板如Raspberry Pi Pico没有USB Host功能这个项目将无法直接运行。你需要额外添加一个USB Host模块如MAX3421E芯片的方案并且需要自行处理相关的底层驱动复杂度会高很多。2.2 显示与输入设备的考量显示屏选择了Adafruit的3.5英寸TFT FeatherWing。480x320的分辨率在微控制器上算是“高清”了足以清晰显示多行文本。更重要的是它通过Feather标准的引脚与主板连接无需焊接即插即用。Adafruit为其提供了完善的displayio驱动库在CircuitPython中只需几行代码就能初始化并显示内容这是我们能快速构建文本界面的基础。当然如果你有其他尺寸的FeatherWing显示屏比如2.4英寸代码只需稍作修改主要是初始化对象和调整旋转参数即可适配。键盘的选择体现了项目的实用性导向任何标准USB HID键盘都可以。项目作者用了Adafruit那款小巧的迷你键盘但理论上你从抽屉里翻出来的任何一个USB键盘都能工作。这得益于CircuitPython内置的USB HID主机支持。当键盘插入Feather RP2040的Host口时系统会自动识别并将其作为标准输入设备我们的程序可以直接读取按键事件无需处理复杂的USB协议。这种“它只管工作™”的特性极大地降低了开发门槛。2.3 供电与便携性设计真正的“便携”意味着脱离固定电源。项目可以通过两种方式供电一是常见的USB充电宝通过板子的Micro-USB口供电二是通过板载的JST PH 2-pin接口连接一块3.7V锂电池。后者是实现极致便携的关键一块1000mAh的锂电池可以轻松提供数小时的续航。整个系统主板、显示屏、键盘的功耗需要估算RP2040全速运行约100mATFT显示屏背光全开可能在150-200mA键盘功耗极低。总计约300-400mA的电流意味着一块2000mAh的电池可以支持5小时以上的连续使用足以应对大多数移动编辑场景。3. 软件架构与核心模块解析3.1 项目文件结构与职责划分将下载的项目包解压后你会看到CIRCUITPY驱动盘上出现几个关键文件和文件夹它们共同构成了编辑器的软件生态。boot.py: 这是CircuitPython启动时首先执行的文件。我们在这里做了两件重要的事一是启用USB CDC通信设备类的数据端口usb_cdc.enable(dataTrue)为调试信息输出开辟独立通道避免干扰主显示二是通过一个连接到D9引脚的按钮实现存储设备读写模式的动态切换。按住按钮启动存储盘以读写模式挂载可以保存文件否则以只读模式挂载防止误操作。code.py: 这是主程序入口相当于传统编程中的main函数。它负责初始化显示、创建自定义显示组、管理可见光标并启动一个循环先调用文件选择器picker让用户选文件再启动编辑器editor进行编辑。lib/目录: 存放项目依赖的第三方库主要是adafruit_featherwing用于驱动显示屏和adafruit_display_text用于绘制文本标签。adafruit_editor/目录: 这是本项目的核心代码库包含四个模块util.py: 工具函数如判断当前存储是否为只读模式。dang.py: 一个精简版的“curses”库实现负责管理屏幕、处理键盘输入和输出到终端。它是编辑器交互逻辑的底层框架。picker.py: 文件选择器模块。它列出CIRCUITPY根目录下的文件用户可以用上下箭头选择按回车键打开或按CtrlN创建新文件。editor.py: 文本编辑器本体。它打开指定文件显示内容处理光标移动、文本插入删除等编辑操作并响应CtrlX保存退出和CtrlC不保存退出等命令。这种模块化设计使得代码清晰、易于维护和扩展。例如如果你想修改文件选择器的界面只需专注于picker.py如果想增加新的编辑快捷键则主要修改editor.py。3.2 Displayio与“终端”的魔法CircuitPython的displayio框架是图形显示的核心。它采用一种“组”Group和“图块”TileGrid的层级结构来管理屏幕上的所有元素。本项目最巧妙的一点是它并没有从头开始绘制一个文本界面而是**“借用”了系统内置的终端显示层**。在CircuitPython中当你通过串口REPL与板子交互时其输出默认会显示在连接的屏幕上这个显示层就是displayio.CIRCUITPYTHON_TERMINAL。它本质上是一个特殊的Group对象。我们的代码做了以下操作创建一个新的、空的Groupcustomized_console_group。将这个新Group设置为显示的根组display.root_group这会覆盖默认的终端显示。将系统内置的终端GroupCIRCUITPYTHON_TERMINAL作为子元素追加到我们自己的customized_console_group中。这样一来我们既保留了系统终端的所有文本显示功能文件内容、选择器列表都是通过print语句输出到这个终端实现的又获得了在其之上叠加其他图形元素比如我们的自定义光标的能力。这是一种非常高效的重用策略避免了直接操作像素的复杂性。实操心得务必记得在程序退出前将CIRCUITPYTHON_TERMINAL从你的自定义组中移除customized_console_group.remove(...)。如果忘记这一步系统在尝试回收控制权时可能会因为显示层状态混乱而导致崩溃。项目代码中用了一个大的try...except块来确保任何异常包括用户按CtrlC触发时都能执行这步清理操作。3.3 实现“看得见”的光标系统终端有光标位置的概念但在displayio框架下它没有一个实体的、可见的光标图形。为了让编辑体验更直观我们需要自己造一个。项目的解决方案既简单又聪明用一个反色显示的文本标签Label来模拟光标。在code.py中我们创建了一个Label对象初始文本为一个空格前景色设为黑色背景色设为浅灰色形成反显效果。然后将其hidden属性设为True初始隐藏并添加到之前创建的customized_console_group中。由于添加顺序在终端之后这个光标标签会显示在终端文本的上层。在editor.py内部随着用户移动光标通过方向键程序会实时计算光标在屏幕上的像素坐标x,y然后更新这个Label对象的位置visible_cursor.x,visible_cursor.y并将其显示出来。当光标移动到一个新字符上时还会将这个Label的文本.text属性设置为该位置的字符从而实现“覆盖高亮”的效果。这种将显示逻辑与业务逻辑分离的设计主程序创建光标对象编辑器模块操作它使得代码耦合度更低。4. 从零开始详细搭建与配置步骤4.1 硬件连接与组装堆叠扩展板将3.5英寸TFT FeatherWing显示屏直接插到Feather RP2040主板的Feather接口上。确保引脚对齐轻轻按压直至完全贴合。这是最简单的物理连接。连接模式切换按钮取一个 tactile 按钮将其一端用杜邦线连接至主板的D9引脚另一端连接至GND接地。这个按钮用于启动时选择存储模式。你可以用热熔胶或胶带将其固定在板子侧面方便操作。连接USB键盘将USB键盘直接插入Feather RP2040主板上的USB Type-A Host端口。供电选择以下任一方式USB供电用一根USB线连接主板的Micro-USB口到电脑、充电宝或USB充电器。电池供电将一块3.7V锂电池的JST插头连接到主板上的JST PH 2-pin 电池接口。主板会自动管理电池充电当通过USB供电时和供电切换。4.2 软件环境准备与固件更新安装CircuitPython如果你的Feather RP2040还没有安装CircuitPython需要先进行这一步。访问CircuitPython官网找到“Adafruit Feather RP2040”的页面下载最新的.uf2固件文件。按住主板上的BOOT或BooTsel按钮同时将主板通过Micro-USB连接到电脑。然后释放按钮。电脑上会出现一个名为RPI-RP2的U盘。将下载的.uf2文件拖入这个U盘。板子会自动重启之后你会看到一个名为CIRCUITPY的新U盘这表明CircuitPython已安装成功。安装必要的库CircuitPython的核心是丰富的库生态。我们需要将项目依赖的库文件复制到板子上。从项目包或Adafruit的CircuitPython库包中找到以下库文件夹并将其复制到CIRCUITPY盘下的lib/目录中如果lib目录不存在就新建一个adafruit_featherwing/adafruit_display_text/adafruit_bus_device/(通常是其他库的依赖)adafruit_register/(通常是其他库的依赖)4.3 部署项目代码与关键配置配置boot.py以实现读写模式切换这是保证文件可保存的关键。将项目提供的boot.py文件内容完整复制到CIRCUITPY盘的根目录。其核心逻辑是检测D9引脚的电平通过按钮接地拉低# boot.py 关键部分 import digitalio import storage import board write_mode_btn digitalio.DigitalInOut(board.D9) write_mode_btn.direction digitalio.Direction.INPUT write_mode_btn.pull digitalio.Pull.UP # 启用内部上拉电阻 # 如果按钮被按下value为False即接地则以读写模式挂载否则只读。 storage.remount(/, readonlywrite_mode_btn.value)内部上拉电阻pulldigitalio.Pull.UP这行代码非常重要。它让D9引脚在按钮未按下时保持高电平True。当按钮按下引脚连接到GND电平被拉低False。这样我们就不需要外接一个物理的上拉电阻了。storage.remount这个函数动态改变了整个CIRCUITPY盘在电脑和CircuitPython程序眼中的访问权限。readonlyFalse意味着可以写入。部署核心项目文件将项目包中的adafruit_editor/整个文件夹复制到CIRCUITPY盘根目录。将项目包中的code.py复制到CIRCUITPY盘根目录覆盖原有的如果有。检查最终的文件结构应该类似于CIRCUITPY/ ├── boot.py ├── code.py ├── lib/ │ ├── adafruit_featherwing/ │ └── adafruit_display_text/ └── adafruit_editor/ ├── __init__.py ├── dang.py ├── editor.py ├── picker.py └── util.py首次运行与测试确保键盘已插入USB Host口。如果不需保存文件直接按一下复位键RST启动。屏幕会亮起显示文件列表。如果需要创建或保存文件按住连接在D9上的按钮然后按一下复位键。等待1-2秒屏幕亮起后再松开按钮。此时系统应以读写模式启动。使用键盘的上下箭头键在文件列表中移动回车键选中文件进行编辑。在编辑器中方向键移动光标直接打字输入CtrlX保存退出CtrlC放弃保存退出。5. 深度开发调试技巧与功能扩展5.1 利用USB数据端口进行调试在开发或修改编辑器代码时我们经常需要打印一些调试信息。但普通的print()语句会直接输出到主显示终端破坏编辑器界面。这就是为什么我们在boot.py中启用了usb_cdc.data。项目中的code.py重写了print函数将其指向了这个数据端口。要查看这些调试信息你需要使用一个串口工具如PuTTY、CoolTerm、screen或tio命令去连接第二个串口。在Windows设备管理器中你可能会看到两个COM口通常第二个就是数据端口。在Linux/macOS上通常是/dev/ttyACM1或/dev/ttyUSB1。操作流程用Mu编辑器或一个串口终端连接主串口ttyACM0或第一个COM口以观察主程序运行和REPL。用另一个串口终端连接数据串口ttyACM1或第二个COM口。在这个窗口里你将看到所有由项目代码中print()语句输出的信息例如文件操作日志、异常堆栈跟踪等而完全不影响主屏幕的显示。5.2 常见问题排查速查表在搭建和使用过程中你可能会遇到以下典型问题。这里提供一个快速排查指南问题现象可能原因解决方案屏幕一片空白或显示混乱1. 显示屏初始化失败。2. 库文件缺失或版本不匹配。3. 显示旋转设置不当。1. 检查lib/目录下adafruit_featherwing等库是否存在。2. 确认code.py中tft_featherwing_35.TFTFeatherWing35V2()型号与你硬件版本一致V1或V2。3. 尝试注释或调整display.rotation 180这行代码0, 90, 180, 270。键盘按键无反应1. 键盘未正确识别。2. 键盘需要较大启动电流。3. USB Host端口接触不良。1.先拔下键盘再重新插入这是最常见有效的办法。2. 确保供电充足尝试使用USB电源而非电池供电。3. 检查USB口是否有异物或损坏。无法创建或保存文件1. 存储模式为只读。2. 文件系统已满。3.boot.py未正确配置或执行。1.确保在启动时按住模式切换按钮直到屏幕亮起。2. 连接电脑检查CIRCUITPY盘剩余空间。3. 检查boot.py文件是否在根目录且代码无误。可以尝试在REL中手动执行import storage; storage.remount(/, False)测试。程序崩溃后屏幕卡死或无法操作程序异常退出未清理CIRCUITPYTHON_TERMINAL。按硬件复位键RST重启板子。这是最直接的恢复方法。光标显示位置不准或闪烁光标位置计算与终端字体/尺寸不匹配。检查adafruit_editor/editor.py中计算光标像素坐标的逻辑可能与终端字体宽度、行高有关。可以尝试在数据端口打印光标坐标进行调试。5.3 项目扩展思路与实践建议这个项目是一个优秀的起点你可以基于它进行各种有趣的扩展支持更多文件操作目前的picker.py只支持基本列表。可以增加创建文件夹、删除文件、重命名文件等功能。这需要修改picker.py的交互逻辑并增加对应的文件系统操作os模块。语法高亮为editor.py增加简单的语法高亮功能比如针对Python文件的关键字、字符串用不同颜色显示。这需要解析当前行文本并在渲染时使用不同颜色的Label或自定义的显示对象。多标签页编辑在内存中维护多个文件缓冲区通过快捷键如CtrlTab切换。这需要对editor.py的架构进行较大调整引入状态管理。集成REPL或简单Shell在编辑器中增加一个模式可以直接输入并执行单行CircuitPython代码就像一个小型REPL。这需要调用eval()或exec()函数并妥善处理安全性和错误。更换显示驱动或输入设备如果你想用其他非FeatherWing的显示屏需要根据其驱动库修改code.py中的初始化部分。理论上任何支持displayio的屏幕都可以。输入方面除了键盘也可以尝试集成旋转编码器或按键矩阵来作为方向控制和功能键。最后一点个人体会这个项目最让我欣赏的是它“站在巨人肩膀上”的设计哲学。它没有重复造轮子而是巧妙地利用了CircuitPython已有的displayio.CIRCUITPYTHON_TERMINAL和USB HID主机支持将重心放在了应用逻辑的整合上。在嵌入式开发中资源总是有限的如何最大化利用现有框架和库的特性往往是项目成败和开发效率的关键。当你成功让这个编辑器跑起来并在一个小屏幕上用键盘修改自己的代码时那种自给自足的成就感正是硬件创客乐趣的重要组成部分。