https://github.com/hpmicro/circuitpython.git
这意味着,HPMicro 开发板除了使用 C 语言和 HPM SDK 开发之外,也可以使用 CircuitPython 直接运行 Python 程序。修改代码、复制文件、打开 REPL,就可以快速验证一个外设或做出一个简单的交互应用。
这篇文章记录我实际使用 HPM5300EVK 的过程:先在 Linux 或 Windows 上构建固件,再通过板载 FT2232 烧录,最后运行一个带 ST7789 彩屏、EC11 旋转编码器、K0 按键和板载 LED 的 python demo,该demo在仓库的ports\hpmicro\tests\hpm5300evk_st7789_rotary_demo.py

CircuitPython 是什么
CircuitPython 是面向微控制器的 Python 运行环境。它把 GPIO、UART、I2C、SPI、PWM 等硬件功能封装成简单的 Python 模块,用户不需要为每次功能验证重新创建 C 工程。
例如,控制板载 LED 只需要几行代码:
import board
import digitalio
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
led.value = False
程序可以直接从 USB REPL 运行,也可以保存为 code.py 放到开发板的 CIRCUITPY 盘中。开发板重新上电后,CircuitPython 会自动执行这个文件。
构建编译对用户尽量无感
从使用者的角度看,构建 HPMicro CircuitPython 固件并不需要了解底层实现。选择开发板后,执行一条命令即可:
make -C ports/hpmicro BOARD=hpm5300evk
我不需要手动选择 HPM5361 使用哪一个驱动文件,也不需要自己填写芯片型号、Flash 地址或外设版本。构建系统会根据 BOARD 自动关联对应的 HPM SDK board,读取芯片信息,选择适合的外设实现,并生成最终固件。
Linux 第一次构建时会自动下载匹配的 RISC-V 工具链;Windows 直接使用 sdk_env 中已有的工具链和构建工具。准备好系统的基础工具后,日常构建只需要更换 BOARD 名称即可。
构建完成后,主要文件会出现在 ports/hpmicro/build/:
firmware.elf 用于调试和烧录
firmware.bin 固件镜像
firmware.elf.map 内存和符号信息
内存较小的开发板可以直接使用 board 的默认配置;HPM5300EVK 如果运行较大的 Python UI,可以使用文中的 ILM heap/stack 参数构建。对普通 Python 用户来说,这只是构建命令上的一个可选配置,不会改变 Python 程序的写法。
HPMicro 适配框架
对于 HPMicro 平台,HPM SDK 继续负责芯片启动、时钟、引脚复用和底层驱动,CircuitPython 负责提供统一的 Python API。这样既保留了 HPM SDK 的硬件能力,也能获得 CircuitPython 简洁、快速的开发体验。
从使用者的角度看,整个适配框架可以简单理解为三层:
CircuitPython Python API
|
HPMicro 开发板的引脚和外设映射
|
HPM SDK 和芯片硬件
这三层分别解决不同的问题。
第一层:CircuitPython 公共 API
最上层就是用户看到的 Python 模块,例如:
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA, frequency=400000)
这一层使用的是 CircuitPython 统一接口。busio.I2C、pwmio.PWMOut、rotaryio.IncrementalEncoder 等模块不需要知道 HPM5361 的寄存器地址,也不需要知道底层使用的是 GPTMR、QEIV2 还是 GPIO 中断。
第二层:HPMicro board 资源映射
board 模块把开发板上的真实引脚和外设名称提供给 Python 程序:
board.SCK -> HPM5300EVK 的 SPI1 SCK
board.MOSI -> HPM5300EVK 的 SPI1 MOSI
board.SDA -> HPM5300EVK 的 I2C0 SDA
board.GPTMR_PWM0 -> GPTMR0 的 PWM 通道
board.QEIV2_A -> QEIV2 的 A 相输入
board.LED -> 板载 LED
不同开发板的引脚可能完全不同,但 Python 程序只使用当前 board 提供的名称。增加新 board 时,主要工作就是补充板卡的引脚表、外设映射和功能开关。
第三层:HPMicro common-hal 和外设后端
这一层把 CircuitPython 的对象操作转换成 HPM SDK 调用。例如:
pwmio.PWMOut.frequency
-> GPTMR 时钟和 reload/compare 配置
rotaryio.IncrementalEncoder.position
-> QEIV2 位置计数器,或 GPIO AB 状态解码
analogio.AnalogOut.value
-> DAC 输出值
busio.SPI.write()
-> HPM SPI FIFO、传输状态和片选控制
HPM 不同系列的硬件 IP 可能不一样,所以这一层会根据目标 SoC 选择相应实现。比如 watchdog 可能使用 EWDG 或 WDG,GPTMR 也有不同版本;这些差异在固件构建和底层适配中处理,应用层仍然使用统一的 CircuitPython API。
HPM SDK 负责底层硬件
HPM SDK 继续负责芯片启动、时钟树、pinmux、寄存器定义、异常入口和底层驱动。CircuitPython HPMicro port 在上面增加适配,而不是重新复制一套启动代码。
这样做之后,一条外设调用大致经过下面的路径:
Python 程序
-> CircuitPython 公共 API
-> HPMicro common-hal
-> board 外设和引脚描述
-> HPM SDK 驱动
-> HPM MCU 外设寄存器
一个具体例子:EC11 是如何工作的
在 Python 侧,EC11 只需要这样创建:
encoder = rotaryio.IncrementalEncoder(
board.QEIV2_A,
board.QEIV2_B,
divisor=4,
)
print(encoder.position)
在 HPM5300EVK 上,board.QEIV2_A 和 board.QEIV2_B 对应 PA10 和 PA11,底层优先使用 HPM5361 的 QEIV2 硬件计数器。对于没有对应 QEI 外设的 SoC,适配层可以使用 GPIO 中断读取 A/B 两相状态,Python 侧的调用方式保持不变。
这就是适配框架的意义:用户看到的是统一的 IncrementalEncoder,底层则根据开发板和 SoC 的实际能力选择最合适的硬件实现。
我只需要按照开发板提供的名称使用 board.SCK、board.SDA、board.LED 等对象,底层会自动找到对应的 HPM 外设和引脚。不同 HPM 芯片之间的差异也由适配层处理,Python 程序不需要因为更换 SoC 就重新编写一套外设代码。
我能直接使用哪些功能
拿到固件后,我可以直接使用下面这些 CircuitPython 模块:
digitalio | |
busio.UART | |
busio.I2C | |
busio.SPI | |
i2ctarget | |
spitarget | |
canio | |
pwmio | |
frequencyio | |
countio | |
rotaryio | |
analogio.AnalogIn | |
analogio.AnalogOut | |
microcontroller.watchdog | |
microcontroller.cpu.temperature | |
os.urandom() |
不同 HPM SoC 的外设并不完全相同,所以每块 board 能使用的模块会有所区别。当前仓库中目前看到有HPM5300EVK、HPM5301EVKLite board,其他board估计后续也会陆陆续续增加。
获取源码
我使用下面的命令获取源码并初始化子模块:
git clone https://github.com/hpmicro/circuitpython.git
git submodule update --init --recursive data/nvm.toml lib/tlsf tools/huffman ports/hpmicro/sdk_env
cd circuitpython
HPMicro port 位于:
ports/hpmicro/
其中 sdk_env 是 HPM SDK 子模块,构建 HPMicro CircuitPython 固件时会使用这里的 SDK 配置。
Linux 构建
安装主机工具
Ubuntu 或 Debian 系统可以安装以下工具:
sudo apt update
sudo apt install -y \
git make cmake ninja-build openocd \
python3 python3-pip python3-venv
CMake 和 Ninja 是 HPM SDK 子构建所需的工具,Linux 下使用系统版本,不会由 Makefile 自动安装。
自动下载 RISC-V 工具链
Linux 第一次构建 HPMicro port 时,会自动从 HPMicro release 下载原生 RISC-V 工具链,并解压到:
ports/hpmicro/toolchains/rv32imac_zicsr_zifencei_multilib_b_ext-linux/
开始构建
在仓库根目录执行:
make -C ports/hpmicro BOARD=hpm5300evk
构建产物位于:
ports/hpmicro/build/firmware.elf
ports/hpmicro/build/firmware.bin
ports/hpmicro/build/firmware.elf.map
如果使用的是本机已有的 HPMicro 工具链,可以显式指定路径:
make -C ports/hpmicro BOARD=hpm5300evk HPM_TOOLCHAIN_PATH=/opt/hpm/toolchain HPM_CMAKE=cmake HPM_NINJA=ninja
Windows 构建
Windows 下建议使用 Git Bash。Git for Windows 提供了运行 CircuitPython Makefile 所需的 POSIX shell,同时还包含 Git 工具。
第一次使用时,可以在 Git Bash 中运行:
python ports/hpmicro/tools/setup_sdk.py
这个脚本会初始化相关子模块,并准备 HPM SDK 自带的构建环境。Windows 使用 sdk_env 中的 RISC-V GCC、CMake 和 Ninja,不需要另外下载 Linux 工具链。
然后执行:
make -C ports/hpmicro BOARD=hpm5300evk -j8
如果 Make 找不到 shell,可以指定 Git Bash 的路径:
make -C ports/hpmicro BOARD=hpm5300evk SHELL='C:/Program Files/Git/bin/sh.exe'
Windows 版本的构建命令对长参数使用了 response file,因此 SDK 头文件较多时也可以正常完成编译。
HPM5300EVK 的内存配置
HPM5300EVK 的 RAM 比较紧张,建议使用 ILM 存放 CircuitPython 的 heap 和 stack。以 HPM5300EVK 为例,可以使用:
make -C ports/hpmicro BOARD=hpm5300evk -j8 HPM_ILM_DATA_SECTION=.fast HPM_ILM_HEAP_SIZE=0x10000 HPM_ILM_STACK_SIZE=0x4000 HPM_DLM_HEAP_SIZE=0
参数含义如下:
HPM_ILM_DATA_SECTION 数据使用的 linker section,HPM5300EVK 为 .fast
HPM_ILM_HEAP_SIZE CircuitPython GC heap 大小
HPM_ILM_STACK_SIZE CircuitPython 运行时 stack 大小
HPM_DLM_HEAP_SIZE DLM 中保留的 heap 大小,0 表示使用 ILM
不同开发板的内存容量不同,HPM5300EVK 的参数不一定适用于其他 board。修改内存参数后,可以先清理旧的构建目录:
make -C ports/hpmicro BOARD=hpm5300evk clean
使用jlink或者 FT2232 烧录
这里主要介绍板载的调试器使用,对于jlink更加简单,这里不做阐述。
HPM5300EVK 使用板载 FT2232 调试器。先进入 HPMicro port 目录:
cd ports/hpmicro
启动 OpenOCD
Linux 下如果 openocd 已经在 PATH 中,可以执行:
openocd \
-f sdk_env/hpm_sdk/boards/openocd/probes/ft2232.cfg \
-f sdk_env/hpm_sdk/boards/openocd/soc/hpm5300.cfg \
-f sdk_env/hpm_sdk/boards/openocd/boards/hpm5300evk.cfg
Windows 下可以使用 sdk_env 自带的 OpenOCD:
sdk_env/tools/openocd/openocd.exe \
-f sdk_env/hpm_sdk/boards/openocd/probes/ft2232.cfg \
-f sdk_env/hpm_sdk/boards/openocd/soc/hpm5300.cfg \
-f sdk_env/hpm_sdk/boards/openocd/boards/hpm5300evk.cfg
OpenOCD 启动后会在本机 TCP 3333 端口等待 GDB 连接。
使用 GDB 下载
Linux 使用自动下载的工具链:
toolchains/rv32imac_zicsr_zifencei_multilib_b_ext-linux/bin/\
riscv32-unknown-elf-gdb build/firmware.elf
Windows 使用 sdk_env 中的 GDB:
sdk_env/toolchains/rv32imac_zicsr_zifencei_multilib_b_ext-win/bin/
riscv32-unknown-elf-gdb.exe build/firmware.elf
进入 GDB 后输入:
target extended-remote localhost:3333
monitor reset halt
load
monitor reset run
烧录完成后,开发板会重新运行新的 CircuitPython 固件。
HPM5300EVK 的 ST7789 + EC11 Demo
为了把这些功能串起来,我使用了一个更加完整的演示程序:
ports/hpmicro/tests/hpm5300evk_st7789_rotary_demo.py
这个 demo 使用 320x240 的 ST7789 SPI 彩屏显示一个小型状态面板,同时读取 EC11 旋转编码器。旋转 EC11 时,屏幕上的位置、方向、变化量和标尺会实时更新;按下 EC11 会将位置清零;按下 K0 会切换板载 LED。
硬件连接
ST7789
board.SCK | |
board.MOSI | |
board.MISO | |
board.CE0 | |
board.GPIO0 | |
board.GPIO3 | |
board.GPIO2 | |
EC11
board.QEIV2_A | |
board.QEIV2_B | |
board.GPIO21 | |
另外,demo 使用 board.GPIO26 读取 K0,使用 board.LED 控制板载 LED。K0 和板载 LED 均按当前开发板的低电平有效逻辑处理。
运行方法
可以打开 USB CDC REPL,直接复制运行完整脚本。具体操作如下:
连接开发板的 USB CDC 串口,进入 CircuitPython REPL。 按 Ctrl+E进入粘贴模式。粘贴完整的 hpm5300evk_st7789_rotary_demo.py内容。按 Ctrl+D执行脚本。


也可以把demo 复制到 CIRCUITPY 盘,并重命名为:
code.py
启动后会看到类似输出:
HPM5300EVK ST7789 + EC11 dashboard demo
LCD DC=PA30/P1-15; EC11 A=PA10/P1-12, B=PA11/P1-16, PUSH=PB01/P1-29
K0=board.GPIO26; onboard LED=PA23, both active low
Rotate: value, direction, delta, and scale update.
EC11 push: reset position. K0: toggle the onboard LED.
The display uses one scanline buffer, not a full framebuffer.
Display initialized; running continuously.
程序会一直运行。

这个 demo 做了什么
使用普通 SPI 驱动 ST7789
显示屏使用 CircuitPython 的 busio.SPI:
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
while not spi.try_lock():
pass
spi.configure(baudrate=40000000, polarity=0, phase=0)
ST7789 的 DC、CS、RES 和 BLK 使用 digitalio.DigitalInOut 控制。这样可以在不依赖额外显示库的情况下,直接完成屏幕初始化、窗口设置和像素刷新。
使用小行缓冲降低 RAM 占用
320x240 的 RGB565 全屏缓存需要:
320 x 240 x 2 = 153600 bytes
HPM5300EVK 不适合为这个 demo 分配完整 framebuffer,因此程序只使用十行 RGB565 缓冲:
line = bytearray(WIDTH * 10)
绘制矩形时,程序先生成十行像素,再重复发送到屏幕。这样可以保留较低的内存占用,同时完成状态面板、数字、标尺和指示灯的显示。
使用 QEIV2 读取 EC11
HPM5300EVK 的 EC11 A/B 连接到 QEIV2:
encoder = rotaryio.IncrementalEncoder(
board.QEIV2_A,
board.QEIV2_B,
divisor=4,
)
rotaryio 返回编码器当前位置,demo 根据位置变化更新屏幕内容。按下 EC11 后将当前位置设置为 0:
encoder.position = 0
K0 控制板载 LED
K0 通过普通 GPIO 输入读取,按下沿切换 LED:
k0 = digitalio.DigitalInOut(board.GPIO26)
k0.direction = digitalio.Direction.INPUT
k0.pull = digitalio.Pull.UP
这样一个 demo 同时展示了 SPI 显示、硬件编码器、GPIO 按键和板载 LED 的协同使用。
常用验证脚本
仓库中还提供了多个可以直接复制到 REPL 或 CIRCUITPY 盘运行的测试脚本:
ports/hpmicro/tests/hpmicro_pwm_test.py
ports/hpmicro/tests/hpmicro_frequencyio_test.py
ports/hpmicro/tests/hpmicro_countio_test.py
ports/hpmicro/tests/hpmicro_rotaryio_test.py
ports/hpmicro/tests/hpmicro_i2ctarget_test.py
ports/hpmicro/tests/hpmicro_spitarget_test.py
ports/hpmicro/tests/hpmicro_watchdog_test.py
ports/hpmicro/tests/hpmicro_rng_test.py
ports/hpmicro/tests/hpmicro_analogout_test.py
ports/hpmicro/tests/hpm5300evk_st7789_color_test.py
我的做法是先运行单功能脚本确认外设,再运行 ST7789 + EC11 综合 demo。
总结
CircuitPython 让 HPM RISC-V MCU 的开发流程变得更加直接:底层仍然使用 HPM SDK,应用层则可以用 Python 快速修改和验证。
这套方案的重点不只是增加几个 Python 模块,而是把 RISC-V MCU 的底层能力连接到更轻量的开发流程:固件负责提供运行环境,后续通过 `code.py` 或 USB REPL 就可以快速修改和迭代应用。
源码和后续更新可以在这里查看,需要什么模块或者bug也可以提issue反馈
https://github.com/hpmicro/circuitpython.git


以上内容来自先楫开发者的原创分享。
我们始终相信开发者共创的力量。先楫社区坚持开源共享、互惠互利,贴近每一个开发者,一步一个脚印,一点一滴积累,为成为更好的我们而不断努力。
心之所向,锐意进取,星辰大海,恣意成长。
MCU生态建设需要您的贡献与支持!欢迎广大爱好者和开发者踊跃投稿,供稿请联系marketing@hpmicro.com。


