开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验

先楫半导体HPMicro 2026-09-02 09:35
最近我在 HPMicro 的公开仓库中看到了 CircuitPython 相关代码,于是拿 HPM5300EVK 做了一次实际体验:

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

开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图1

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.I2Cpwmio.PWMOutrotaryio.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.SCKboard.SDAboard.LED 等对象,底层会自动找到对应的 HPM 外设和引脚。不同 HPM 芯片之间的差异也由适配层处理,Python 程序不需要因为更换 SoC 就重新编写一套外设代码。

我能直接使用哪些功能

拿到固件后,我可以直接使用下面这些 CircuitPython 模块:

模块
功能
digitalio
GPIO 输入、输出和上下拉
busio.UART
UART 通信和 USB REPL
busio.I2C
I2C 主机通信
busio.SPI
SPI 主机通信,支持 mode 0 到 3
i2ctarget
I2C 从机地址、读写和事务处理
spitarget
SPI 从机中断收发和硬件 CE0
canio
HPM MCAN 通信
pwmio
GPTMR PWM 输出、频率和占空比控制
frequencyio
GPTMR 输入捕获和频率测量
countio
GPIO 边沿计数
rotaryio
QEI、QEIV2 或 GPIO 方式的旋转编码器
analogio.AnalogIn
ADC 模拟输入
analogio.AnalogOut
DAC 模拟输出
microcontroller.watchdog
硬件看门狗
microcontroller.cpu.temperature
CPU 温度读取
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

ST7789 信号
HPM5300EVK
SCK
PA27,P1-23,‎board.SCK
SDA/MOSI
PA29,P1-19,‎board.MOSI
MISO
PA28,P1-21,‎board.MISO
CS
PA26,P1-24,‎board.CE0
RES
PA12,P1-11,‎board.GPIO0
DC
PA30,P1-15,‎board.GPIO3
BLK
PA31,P1-13,‎board.GPIO2
VCC
3.3 V
GND
GND

EC11

EC11 信号
HPM5300EVK
A
PA10,‎board.QEIV2_A
B
PA11,‎board.QEIV2_B
PUSH
PB01,P1-29,‎board.GPIO21
C
GND

另外,demo 使用 board.GPIO26 读取 K0,使用 board.LED 控制板载 LED。K0 和板载 LED 均按当前开发板的低电平有效逻辑处理。

运行方法

可以打开 USB CDC REPL,直接复制运行完整脚本。具体操作如下:

  1. 连接开发板的 USB CDC 串口,进入 CircuitPython REPL。
  2. 按 ‎Ctrl+E 进入粘贴模式。
  3. 粘贴完整的 ‎hpm5300evk_st7789_rotary_demo.py 内容。
  4. 按 ‎Ctrl+D 执行脚本。
开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图2

开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图3

也可以把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.

程序会一直运行。

开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图4


这个 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 的 DCCSRES 和 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

开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图5


开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图6
开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图7
开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图8
开发者分享 | 用 Python 开发 RISC-V MCU:CircuitPython 实战体验图9

以上内容来自先楫开发者的原创分享。

我们始终相信开发者共创的力量。先楫社区坚持开源共享、互惠互利,贴近每一个开发者,一步一个脚印,一点一滴积累,为成为更好的我们而不断努力。


心之所向,锐意进取,星辰大海,恣意成长。


MCU生态建设需要您的贡献与支持!欢迎广大爱好者和开发者踊跃投稿,供稿请联系marketing@hpmicro.com。



关于科技区角:国内科技展会垂直内容策划服务商,提供从论坛内容全案策划、会展市场化IP打造到精准专业观众一站式邀约服务,以产业内容吸引高质量B端人群,打通展会从议题设计、演讲嘉宾邀约、宣传预热、精准邀观到供需对接全链路。
声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。
MCU RISC-V
more
宇航领域新突破:中国电科58所成功研制RISC-V高可靠微处理器
时擎智能PT153S芯片量产破百万,RISC-V架构赋能国产USB网卡突围
RISC-V挺进服务器“深水区”:蓝芯算力LX500以“智通融合”架构交出量产答卷
江苏首家RISC-V生态实验室获得RVI国际基金会认证,全国仅两家
进迭时空K3芯片亮相,中国RISC-V实现全球领跑
【RVEI】8月新一批RISC-V团体标准正式发布
2026 RISC-V城市行 | 技术沙龙@长沙站【议程更新】(倒计时一周)
正式揭晓|RISC-V 中国峰会2026钻石赞助商名单公布
【RVEI】聚焦架构创新与标准协同: RISC-V AI扩展指令集研讨会成功召开
RISC-V能不能跑机器人?——基于VisionFive 2、YOLOE、视觉闭环、Agent API与MCP的机械臂工程实践(含核心代码)
Copyright © 2025-成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号