1. CircuitPython与MicroPython的核心模块差异第一次接触CircuitPython的开发者往往会惊讶于它与MicroPython在模块设计上的巨大差异。虽然两者都源自Python的嵌入式实现但在实际使用中你会发现从MicroPython迁移项目到CircuitPython时很多基础模块的调用方式完全不同。最明显的区别就是machine模块的消失。在MicroPython中machine模块是硬件操作的核心包含了GPIO控制、定时器、PWM等基础功能。而CircuitPython将其拆分为多个专用模块microcontroller替代machine的基础引脚控制digitalio专门处理数字输入输出analogio模拟信号处理busio总线协议I2C/SPI/UART这种模块化设计带来了更清晰的代码结构但也意味着直接移植的代码几乎无法运行。比如在MicroPython中点亮LED的代码from machine import Pin led Pin(2, Pin.OUT) led.value(1)在CircuitPython中需要改写为import digitalio import microcontroller led digitalio.DigitalInOut(microcontroller.pin.GPIO2) led.direction digitalio.Direction.OUTPUT led.value True2. 硬件接口的兼容层实践2.1 GPIO映射方案由于引脚定义方式完全不同我们需要建立一个映射层。以下是我在实际项目中使用的引脚映射方案import sys CIRCUITPYTHON (sys.implementation.name circuitpython) if CIRCUITPYTHON: from microcontroller import pin as CPin # 建立数字引脚映射表 pin_mapper {} for name in dir(CPin): if name.startswith(GPIO): num int(name[4:]) pin_mapper[num] getattr(CPin, name) else: from machine import Pin使用时通过pin_mapper[引脚编号]获取对应引脚对象。这种方法既保持了MicroPython的数字引脚习惯又兼容了CircuitPython的命名体系。2.2 总线协议适配SPI和I2C的差异尤为明显。MicroPython使用统一的machine模块from machine import SPI spi SPI(1, baudrate5000000, polarity0, phase0)而CircuitPython使用busio模块import busio from microcontroller import pin spi busio.SPI(pin.GPIO10, pin.GPIO11, pin.GPIO12)建议封装一个兼容层def create_spi(instance1, baudrate1000000, polarity0, phase0): if CIRCUITPYTHON: if instance 1: return busio.SPI(pin.GPIO10, pin.GPIO11, pin.GPIO12) else: raise ValueError(Only SPI1 available) else: return SPI(instance, baudratebaudrate, polaritypolarity, phasephase)3. 常用功能模块的兼容实现3.1 时间模块处理时间模块的差异经常被忽视。MicroPython的utime与CircuitPython的time模块在以下方面不同时间戳精度毫秒vs秒本地时间结构8元素vs9元素元组sleep函数的参数单位这是我使用的兼容方案if CIRCUITPYTHON: from time import * def sleep_ms(ms): sleep(ms/1000) def ticks_ms(): return int(time()*1000) else: from utime import * ticks_ms ticks_ms3.2 按键检测实现按键检测是嵌入式开发的常见需求两种环境的实现方式截然不同MicroPython版本依赖中断from machine import Pin btn Pin(2, Pin.IN, Pin.PULL_UP) btn.irq(handlerlambda p:print(Pressed), triggerPin.IRQ_FALLING)CircuitPython使用事件队列import keypad keys keypad.Keys((microcontroller.pin.GPIO2,), value_when_pressedFalse) while True: event keys.events.get() if event and event.pressed: print(Pressed)兼容方案可以抽象为class Button: def __init__(self, pin_num): if CIRCUITPYTHON: self._key keypad.Keys((pin_mapper[pin_num],), value_when_pressedFalse) else: self._btn Pin(pin_num, Pin.IN, Pin.PULL_UP) self._btn.irq(handlerself._callback, triggerPin.IRQ_FALLING) def _callback(self, pin): self._last_press ticks_ms() def is_pressed(self): if CIRCUITPYTHON: event self._key.events.get() return event is not None return ticks_ms() - self._last_press 1004. 高级功能兼容方案4.1 文件系统操作CircuitPython默认以只读模式挂载文件系统这点与MicroPython不同。需要在代码中显式启用写权限if CIRCUITPYTHON: import storage storage.remount(/, readonlyFalse)4.2 网络功能差异WiFi模块的实现也有显著区别。MicroPython使用network模块import network wlan network.WLAN(network.STA_IF) wlan.connect(SSID,PASSWORD)CircuitPython使用wifi模块import wifi wifi.radio.connect(SSID,PASSWORD)建议的兼容层实现def wifi_connect(ssid, password): if CIRCUITPYTHON: wifi.radio.connect(ssid, password) return wifi.radio.ipv4_address else: wlan network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) return wlan.ifconfig()[0]4.3 图形显示处理图形显示方面framebuf模块的差异尤为明显。CircuitPython的framebufferio是MicroPython framebuf的子集缺少blit等关键功能。解决方案是使用兼容版本try: from framebufferio import FrameBuffer except ImportError: from framebuf import FrameBuffer对于需要高性能图形处理的场景建议直接使用CircuitPython的displayio模块它虽然学习曲线较陡但提供了更现代的图形架构。
CircuitPython与MicroPython的模块差异与兼容性实践
发布时间:2026/6/30 15:50:02
1. CircuitPython与MicroPython的核心模块差异第一次接触CircuitPython的开发者往往会惊讶于它与MicroPython在模块设计上的巨大差异。虽然两者都源自Python的嵌入式实现但在实际使用中你会发现从MicroPython迁移项目到CircuitPython时很多基础模块的调用方式完全不同。最明显的区别就是machine模块的消失。在MicroPython中machine模块是硬件操作的核心包含了GPIO控制、定时器、PWM等基础功能。而CircuitPython将其拆分为多个专用模块microcontroller替代machine的基础引脚控制digitalio专门处理数字输入输出analogio模拟信号处理busio总线协议I2C/SPI/UART这种模块化设计带来了更清晰的代码结构但也意味着直接移植的代码几乎无法运行。比如在MicroPython中点亮LED的代码from machine import Pin led Pin(2, Pin.OUT) led.value(1)在CircuitPython中需要改写为import digitalio import microcontroller led digitalio.DigitalInOut(microcontroller.pin.GPIO2) led.direction digitalio.Direction.OUTPUT led.value True2. 硬件接口的兼容层实践2.1 GPIO映射方案由于引脚定义方式完全不同我们需要建立一个映射层。以下是我在实际项目中使用的引脚映射方案import sys CIRCUITPYTHON (sys.implementation.name circuitpython) if CIRCUITPYTHON: from microcontroller import pin as CPin # 建立数字引脚映射表 pin_mapper {} for name in dir(CPin): if name.startswith(GPIO): num int(name[4:]) pin_mapper[num] getattr(CPin, name) else: from machine import Pin使用时通过pin_mapper[引脚编号]获取对应引脚对象。这种方法既保持了MicroPython的数字引脚习惯又兼容了CircuitPython的命名体系。2.2 总线协议适配SPI和I2C的差异尤为明显。MicroPython使用统一的machine模块from machine import SPI spi SPI(1, baudrate5000000, polarity0, phase0)而CircuitPython使用busio模块import busio from microcontroller import pin spi busio.SPI(pin.GPIO10, pin.GPIO11, pin.GPIO12)建议封装一个兼容层def create_spi(instance1, baudrate1000000, polarity0, phase0): if CIRCUITPYTHON: if instance 1: return busio.SPI(pin.GPIO10, pin.GPIO11, pin.GPIO12) else: raise ValueError(Only SPI1 available) else: return SPI(instance, baudratebaudrate, polaritypolarity, phasephase)3. 常用功能模块的兼容实现3.1 时间模块处理时间模块的差异经常被忽视。MicroPython的utime与CircuitPython的time模块在以下方面不同时间戳精度毫秒vs秒本地时间结构8元素vs9元素元组sleep函数的参数单位这是我使用的兼容方案if CIRCUITPYTHON: from time import * def sleep_ms(ms): sleep(ms/1000) def ticks_ms(): return int(time()*1000) else: from utime import * ticks_ms ticks_ms3.2 按键检测实现按键检测是嵌入式开发的常见需求两种环境的实现方式截然不同MicroPython版本依赖中断from machine import Pin btn Pin(2, Pin.IN, Pin.PULL_UP) btn.irq(handlerlambda p:print(Pressed), triggerPin.IRQ_FALLING)CircuitPython使用事件队列import keypad keys keypad.Keys((microcontroller.pin.GPIO2,), value_when_pressedFalse) while True: event keys.events.get() if event and event.pressed: print(Pressed)兼容方案可以抽象为class Button: def __init__(self, pin_num): if CIRCUITPYTHON: self._key keypad.Keys((pin_mapper[pin_num],), value_when_pressedFalse) else: self._btn Pin(pin_num, Pin.IN, Pin.PULL_UP) self._btn.irq(handlerself._callback, triggerPin.IRQ_FALLING) def _callback(self, pin): self._last_press ticks_ms() def is_pressed(self): if CIRCUITPYTHON: event self._key.events.get() return event is not None return ticks_ms() - self._last_press 1004. 高级功能兼容方案4.1 文件系统操作CircuitPython默认以只读模式挂载文件系统这点与MicroPython不同。需要在代码中显式启用写权限if CIRCUITPYTHON: import storage storage.remount(/, readonlyFalse)4.2 网络功能差异WiFi模块的实现也有显著区别。MicroPython使用network模块import network wlan network.WLAN(network.STA_IF) wlan.connect(SSID,PASSWORD)CircuitPython使用wifi模块import wifi wifi.radio.connect(SSID,PASSWORD)建议的兼容层实现def wifi_connect(ssid, password): if CIRCUITPYTHON: wifi.radio.connect(ssid, password) return wifi.radio.ipv4_address else: wlan network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) return wlan.ifconfig()[0]4.3 图形显示处理图形显示方面framebuf模块的差异尤为明显。CircuitPython的framebufferio是MicroPython framebuf的子集缺少blit等关键功能。解决方案是使用兼容版本try: from framebufferio import FrameBuffer except ImportError: from framebuf import FrameBuffer对于需要高性能图形处理的场景建议直接使用CircuitPython的displayio模块它虽然学习曲线较陡但提供了更现代的图形架构。