📋 实验总览

本系列实验使用同一块 CanMV / K210 开发板,按难度由浅入深排列。建议按编号顺序完成,前一个实验掌握的 API(GPIOFPIOA边沿检测WS2812)会在后续实验中直接复用。

GPIO 数字 IO 协议 / 总线 MaixPy 固件 ≥ v0.6.2 预计总耗时 ≈ 80 分钟
2.1

板载 LED 流水灯

GPIO 输出 · FPIOA 引脚映射 · 循环与延时
⭐ 简单 · 10 分钟

🎯实验目的

  • 掌握 K210 GPIO 输出模式的基本用法
  • 学会使用 fpioa_manager 把内部 GPIO0-7 映射到物理 IO6-IO13
  • 熟悉 for 循环、time.sleep_ms() 延时、列表索引等 MicroPython 基础

🛠️硬件准备

  • CanMV / K210 开发板 × 1(已焊接 8 颗板载 LED)
  • USB Type-C 数据线 × 1(必须支持数据传输)
  • MaixPy 固件 ≥ v0.6.2

💡 不需要外接任何传感器或屏幕,程序运行后立刻可以从板载 LED 上看到现象。

🔌引脚分配

开发板把内部 GPIOHS0~7 通过 FPIOA 映射到板载 LED:

内部 GPIO物理引脚板载 LED
GPIO0IO6LED_A
GPIO1IO7LED_B
GPIO2IO8LED_C
GPIO3IO9LED_D
GPIO4IO10LED_E
GPIO5IO11LED_F
GPIO6IO12LED_G
GPIO7IO13LED_H

💻实验代码 2.1.Water_LED.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 文件名:2.1.Water_LED.py
# 说明:板载LED流水灯实验
import time
from maix import GPIO
from fpioa_manager import fm
from board import board_info

# CanMV 开发板引脚配置
fm.register(6,  fm.fpioa.GPIO0, force=True)
fm.register(7,  fm.fpioa.GPIO1, force=True)
fm.register(8,  fm.fpioa.GPIO2, force=True)
fm.register(9,  fm.fpioa.GPIO3, force=True)
fm.register(10, fm.fpioa.GPIO4, force=True)
fm.register(11, fm.fpioa.GPIO5, force=True)
fm.register(12, fm.fpioa.GPIO6, force=True)
fm.register(13, fm.fpioa.GPIO7, force=True)

# 配置引脚为输出
leds = [GPIO(GPIO.GPIO0, GPIO.OUT), GPIO(GPIO.GPIO1, GPIO.OUT),
        GPIO(GPIO.GPIO2, GPIO.OUT), GPIO(GPIO.GPIO3, GPIO.OUT),
        GPIO(GPIO.GPIO4, GPIO.OUT), GPIO(GPIO.GPIO5, GPIO.OUT),
        GPIO(GPIO.GPIO6, GPIO.OUT), GPIO(GPIO.GPIO7, GPIO.OUT)]

if __name__ == '__main__':
    while True:
        for n in range(0, 8):        # 依次点亮
            leds[n].value(1)
            time.sleep_ms(300)
        for n in range(0, 8):        # 依次熄灭
            leds[n].value(0)
            time.sleep_ms(300)

📖代码说明

  • fm.register(IO, GPIO, force=True):FPIOA(Flexible Pin Multiplexing)允许把任意内部功能映射到任意物理 IO;force=True 表示强制覆盖之前的映射。
  • GPIO(pin, mode):构造一个 GPIO 句柄,GPIO.OUT 表示输出模式。
  • led.value(1 / 0):输出高 / 低电平,由于板载 LED 另一端接 3.3V,所以 value(0) 点亮、value(1) 熄灭。
  • time.sleep_ms(300):阻塞 300 毫秒,控制灯光切换速度。

🎬实验现象

上电运行后,开发板上 8 颗 LED 按照:

A → B → C → D → E → F → G → H → 全灭 → A → ...

循环,呈现出"水流动"的效果,每步间隔 300ms。

⚠️注意事项

  • 务必使用 from board import board_info 提前读取板级信息,避免在某些固件版本上因引脚未初始化而报错。
  • fm.register 调用时建议带 force=True,否则重复烧录程序后第二次运行可能因为引脚已被占用而失败。
  • 如果想改"流速",只需修改 time.sleep_ms(300) 中的数值(建议 50 ~ 1000ms)。
2.2

板载方向按键

GPIO 输入 · 内部上拉 · 边沿消抖 · 串口打印
⭐ 简单 · 15 分钟

🎯实验目的

  • 掌握 K210 GPIO 输入模式 + PULL_UP 内部上拉
  • 理解 按键消抖 思路:前后状态比较避免重复触发
  • 学会用 print() 通过串口(REPL)输出调试信息

🛠️硬件准备

  • CanMV / K210 开发板 × 1(带 4 向方向按键)
  • USB Type-C 数据线 × 1(烧录 + 供电 + 串口通信)

💡 板载按键一端接引脚,另一端接地,因此使用 GPIO.PULL_UP 内部上拉,未按时为高电平 1。

🔌引脚分配

按键物理引脚内部 GPIO触发逻辑
UPIO6GPIO0按下低电平
LEFTIO7GPIO1按下低电平
DOWNIO8GPIO2按下低电平
RIGHTIO10GPIO3按下低电平

💻实验代码 2.2_button.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 文件名:2.2_button.py
# 说明:板载方向按键实验
import time
from maix import GPIO
from fpioa_manager import fm

# CanMV 开发板引脚配置
fm.register(6,  fm.fpioa.GPIO0, force=True)
fm.register(7,  fm.fpioa.GPIO1, force=True)
fm.register(8,  fm.fpioa.GPIO2, force=True)
fm.register(10, fm.fpioa.GPIO3, force=True)

# 配置为输入并启用内部上拉
up_button    = GPIO(GPIO.GPIO0, GPIO.IN, GPIO.PULL_UP)
left_button  = GPIO(GPIO.GPIO1, GPIO.IN, GPIO.PULL_UP)
down_button  = GPIO(GPIO.GPIO2, GPIO.IN, GPIO.PULL_UP)
right_button = GPIO(GPIO.GPIO3, GPIO.IN, GPIO.PULL_UP)

state = ['up', 'left', 'down', 'right']
prev = [1, 1, 1, 1]  # 上一次状态,用于消抖

while True:
    curr = [up_button.value(), left_button.value(),
            down_button.value(), right_button.value()]
    for i in range(4):
        if curr[i] != prev[i]:        # 状态发生变化
            if curr[i] == 0:          # 0 = 按下瞬间
                print(state[i])
            prev[i] = curr[i]
    time.sleep_ms(20)                  # 20ms 轮询周期

📖代码说明

  • GPIO.IN + GPIO.PULL_UP:将引脚配置为输入并启用内部上拉电阻,使空闲时为高电平。
  • 消抖思路:每隔 20ms 采样一次引脚电平,只在前后状态不同且当前为低电平时,认为是一次有效按键动作。
  • print():通过串口(REPL)输出方向字符串,方便调试。

🎬实验现象

打开 CanMV IDE(v2.9.2-2)的 串口监视器,依次按下 4 个按键:

up left down right

💡 长按同一按键时,由于状态未变化,串口不会重复打印——这正是"边沿触发"的效果。

⚠️注意事项

  • 轮询周期不能太短time.sleep_ms(20) 是个常用经验值,太短会增加 CPU 占用,太长会漏掉短按。
  • 长按与短按:本实验不区分,如果需要"单击/双击/长按"等高级事件,请在状态机里增加计时逻辑。
  • 板载按键通常已加硬件消抖(RC 滤波),软件只做边沿判断即可。
2.3

有源蜂鸣器

GPIO 数字输出 · 蜂鸣器驱动 · 函数封装复用
⭐⭐ 中等 · 15 分钟

🎯实验目的

  • 认识 有源蜂鸣器 与无源蜂鸣器的区别(前者内置振荡源,给电就响)
  • 掌握 GPIO 输出高低电平来 驱动蜂鸣器 的方法
  • 学会用函数封装可复用的"蜂鸣一声"动作

🛠️硬件准备

  • CanMV / K210 开发板 × 1(板载有源蜂鸣器,标注 "BUZZER")
  • USB Type-C 数据线 × 1

💡 板载蜂鸣器一般是 低电平触发,请确认你的开发板型号;如果是高电平触发,把 value(0)value(1) 对调即可。

🔌引脚分配

模块物理引脚内部 GPIO
蜂鸣器 BUZZERIO6GPIO0

💻实验代码 2.3.Buzzer.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 文件名:2.3.Buzzer.py
# 说明:有源蜂鸣器实验
import time
from maix import GPIO
from fpioa_manager import fm
from board import board_info

fm.register(6, fm.fpioa.GPIO0, force=True)
buzzer = GPIO(GPIO.GPIO0, GPIO.OUT)

def makerobo_buzzer_on():
    buzzer.value(0)            # 低电平触发

def makerobo_buzzer_off():
    buzzer.value(1)

def makerobo_beep(x):
    makerobo_buzzer_on()
    time.sleep_ms(x)
    makerobo_buzzer_off()
    time.sleep_ms(x)

def loop():
    while True:
        makerobo_beep(500)     # 鸣叫 500ms、静默 500ms

if __name__ == '__main__':
    loop()

📖代码说明

  • makerobo_beep(x):封装"鸣叫 + 静默"为一个完整动作,方便复用。
  • 延时单位time.sleep_ms(500) 即 500 毫秒;如果想鸣叫 1 秒,把参数改成 1000
  • 循环结构:在 loop() 中无限循环,可以把蜂鸣器作为"提示音"嵌入到任何主程序里。

🎬实验现象

上电后蜂鸣器以 500ms 鸣叫 + 500ms 静默 的节奏持续发出:

滴 —— 滴 —— 滴 —— 滴 ——

声音节奏均匀,可清晰分辨"鸣"和"停"两段时间。

⚠️注意事项 & 拓展

  • 不要长时间鸣叫:板载蜂鸣器电流较大(~30mA),持续通电会发烫,建议单次鸣叫不超过 2 秒。
  • 有源 vs 无源:本实验针对 有源 蜂鸣器(自带振荡电路)。如果是无源蜂鸣器,必须用 PWM 输出方波才能响。
  • 三极管驱动:外接更大功率蜂鸣器时,建议加 NPN 三极管(如 S8050)做电流放大。
  • 拓展 ①:把蜂鸣器接到按键事件里,实现"按下时短促响一声"。
  • 拓展 ②:改用 PWM 驱动无源蜂鸣器,演奏简单旋律(参考 machine.PWM API)。
2.4

火焰传感器

数字式传感器 · GPIO 输入 · 边沿检测 · 状态打印
⭐⭐ 中等 · 20 分钟

🎯实验目的

  • 了解 数字式传感器 的工作方式:输出只有 0 / 1 两种状态
  • 掌握 GPIO 输入 + 边沿检测的组合应用
  • 学会把"状态变化"映射为可读的控制台信息

🛠️硬件准备

  • CanMV / K210 开发板 × 1
  • 数字式火焰传感器模块 × 1(3 线制:VCC / GND / DO)
  • 杜邦线 × 3(公对母)
  • USB Type-C 数据线 × 1

💡 火焰传感器对 760nm ~ 1100nm 波段的红外光敏感,能感知打火机、蜡烛等光源。

🔌引脚分配

传感器引脚物理引脚内部 GPIO说明
DO(数字输出)IO6GPIO0状态信号,0/1
VCC3.3V电源
GNDGND

💡 大多数模块的 DO 默认在 无火焰时输出 1有火焰时输出 0;少数型号相反,可通过模块上的电位器调节灵敏度。

💻实验代码 2.4_sensor.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 文件名:2.4_sensor.py
# 说明:传感器实验
import time
from maix import GPIO
from fpioa_manager import fm

fm.register(6, fm.fpioa.GPIO0, force=True)
makerobo_DO = GPIO(GPIO.GPIO0, GPIO.IN)

def makerobo_Print(x):
    if x == 1:
        print('')
        print('   *******************')
        print('   *  Makerobo Safe~ *')
        print('   *******************')
        print('')
    if x == 0:
        print('')
        print('   ******************')
        print('   * Makerobo Fire! *')
        print('   ******************')
        print('')

def makerobo_loop():
    makerobo_status = 1
    while True:
        makerobo_tmp = makerobo_DO.value()
        if makerobo_tmp != makerobo_status:    # 边沿检测
            makerobo_Print(makerobo_tmp)
            makerobo_status = makerobo_tmp
        time.sleep_ms(200)

if __name__ == '__main__':
    makerobo_loop()

📖代码说明

  • 边沿检测if tmp != status: 用来判断传感器状态是否变化,只有"发生变化"才打印一次,避免重复刷新。
  • 轮询延时 200ms:足以感知人手操作的节奏;如果换成更快的传感器(如震动),可缩小到 20-50ms。
  • 可扩展性:把 print 换成蜂鸣器响 / LED 闪 / MQTT 上报,就能形成完整的"火焰报警"产品。

🎬实验现象

打开 CanMV IDE(v2.9.2-2)串口监视器:

  1. 上电后无火焰,串口打印一次 Makerobo Safe~
  2. 用打火机在传感器前方 距离 20cm 内 点火但不烧到模块,串口立即打印 Makerobo Fire!
  3. 移开火焰,串口再次打印 Makerobo Safe~

⚠️注意事项

  • 打火机火焰不要靠太近:温度过高会损坏传感器,建议 10-30cm。
  • 环境光干扰:阳光中的红外成分可能误触发,可通过模块上的电位器调高阈值。
  • 真火警场景:实验仅做"探测-提示"演示;真正产品需要加 声光报警 + 自动灭火 + 远程通知 链路。
2.5

WS2812 RGB 彩灯

单线协议 · NeoPixel 驱动 · 全彩循环动画
⭐⭐⭐ 进阶 · 20 分钟

🎯实验目的

  • 认识 WS2812 / NeoPixel 可寻址 RGB 灯珠的通信协议(单线归零码)
  • 掌握 K210 上的 modules.ws2812 驱动调用方式
  • 学会用循环 + 颜色常量做"彩灯轮播"动画

🛠️硬件准备

  • CanMV / K210 开发板 × 1(板载或外接 4 颗 WS2812 灯珠)
  • USB Type-C 数据线 × 1

💡 WS2812 工作电压 5V。板载灯珠已通过稳压电路接好,直接供电即可;外接时建议用独立 5V 电源(≥ 1A),K210 引脚仅提供数据信号。

🔌引脚分配

模块物理引脚
WS2812 DIN(数据线)IO6

💻实验代码 2.5.WS2812_show.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 文件名:2.5.WS2812_show.py
# 说明:RGBS彩灯实验
from modules import ws2812
import utime

# 颜色常量 (R, G, B)
cmv_RED   = (255, 0,   0)
cmv_GREEN = (0,   255, 0)
cmv_BLUE  = (0,   0,   255)

# 引脚 6,4 颗灯珠
np = ws2812(6, 4)

while True:
    # 红色
    for i in range(4):
        np.set_led(i, cmv_RED)
    np.display()
    utime.sleep(1)

    # 绿色
    for i in range(4):
        np.set_led(i, cmv_GREEN)
    np.display()
    utime.sleep(1)

    # 蓝色
    for i in range(4):
        np.set_led(i, cmv_BLUE)
    np.display()
    utime.sleep(1)

📖代码说明

  • ws2812(pin, num):构造灯带对象,pin 是数据线引脚号(默认接 FPIOA 的 pin),num 是灯珠数量。
  • set_led(index, (r,g,b)):仅修改缓冲区,必须 display() 后才真正发送到灯珠。
  • 颜色顺序:本驱动默认 GRB 顺序写入;通过 set_led 传入 RGB 元组时,驱动会自动转换。
  • utime.sleep(1):每种颜色持续 1 秒;改成 0.1 即可做"流水"效果。

🎬实验现象

上电后,4 颗灯珠同步切换:

  • 1 秒 全红
  • 1 秒 全绿
  • 1 秒 全蓝
  • 循环往复

⚠️注意事项 & 拓展

  • 电平匹配:WS2812 数据线高电平阈值为 0.7 × VDD,K210 输出 3.3V 时大多数灯珠都能识别;若遇不稳定,可在数据线串联 100Ω 电阻。
  • 电流估算:每颗全白 ≈ 60mA,4 颗全亮 ≈ 240mA;请确保电源能稳定供电。
  • 时序敏感:WS2812 对时序非常敏感,中途插入 print() 可能会破坏波形——若发现颜色异常,把调试打印关掉。
  • 数量上限:单条总线建议不超过 500 颗,更长需要加电平转换或缓冲器。
  • 拓展 ①:改成 呼吸灯:用 for 循环改变亮度(0~255)实现 PWM 效果。
  • 拓展 ②:改成 彩虹跑马灯:用 HSV → RGB 转换函数,按位置给每颗灯珠不同色相。
  • 拓展 ③:加入按键控制:方向键切换颜色 / 模式(结合 2.2 的按键读取)。
📚

通用准备 & 常见问题

环境准备 · 运行步骤 · 故障排查

🛠️通用准备

  • 一块 CanMV / K210 开发板(推荐带板载 LED、按键、蜂鸣器、WS2812)
  • USB Type-C 数据线(务必支持数据传输)
  • 烧录好 MaixPy 固件 ≥ v0.6.2
  • CanMV IDE(推荐 v2.9.2-2) 或其他串口工具

🚀运行步骤

  1. CanMV IDE(v2.9.2-2) 打开 2.X.名称.py
  2. 选择正确的串口并连接;
  3. 点击 运行(或按 F5);
  4. 按照 README 中 🎬 实验现象 一节验证效果。
❓ 烧录第二次运行报错 "Pin already registered"?
这是 FPIOA 引脚被前一次程序占用。在 fm.register(..., force=True) 中加上 force=True 参数会强制覆盖,所有本系列示例都已加上该参数,请确认你下载的是最新版。
❓ 串口看不到 print 输出?
① 确认 CanMV IDE (v2.9.2-2) 已点击"连接"且左下角出现 提示符;② 串口监视器的波特率需要与固件匹配(默认 115200);③ 在 IDE 中按 Ctrl+C 中断后再重新运行。
❓ WS2812 颜色异常 / 乱闪?
时序被 print 打断是常见原因。请把所有调试 print 注释掉再观察;其次检查数据线是否过长(建议 < 1m),必要时在 DIN 串联 100Ω 电阻。
❓ 想换板子但 IO 号对不上?
请参考你板子的原理图,把代码里的 fm.register(IO, ...) 第二个参数和 FPIOA 编号改成实际连接的引脚即可,示例中的 6/7/8/10/13 等是 CanMV 板的常用 IO 编号。
❓ 实验完成后下一步学什么?
GPIO 玩熟后建议进入 示例项目 学习摄像头采集、屏幕显示,再进入 课程目录 的实战篇跑 YOLO 人脸检测与语音关键词识别。