一 本节介绍
📝本节我们将学习如何通过控制开发板的 GPIO 引脚,实现对 RGB 灯和按键的控制。
🏆学习目标
1️⃣如何将GPIO引脚配置为输出模式,通过引脚电平来控制RGB灯的颜色变化。
2️⃣如何将GPIO引脚配置为输入模式,来检测按键是否按下或松开,去改变RGB灯的亮灭。
3️⃣知道立创·庐山派K230-CanMV开发板和立创·庐山派Lite-K230D-CanMV开发板 在板载 RGB 灯、用户按键引脚和点亮电平上的差异。
二 软硬件准备
| 项目 | 说明 |
|---|---|
| 开发板 | 立创·庐山派K230-CanMV开发板 或 立创·庐山派Lite-K230D-CanMV开发板 |
| 固件 | 与自己开发板型号匹配的 CanMV MicroPython 固件 |
| IDE | CanMV IDE K230 |
| 外设 | 本节使用板载 RGB 灯和板载用户按键,不需要额外接线 |
三 双板兼容说明
本节默认同时面向以下两款开发板:
| 项目 | 立创·庐山派K230-CanMV开发板 | 立创·庐山派Lite-K230D-CanMV开发板 |
|---|---|---|
| 是否支持本节实验 | 支持 | 支持 |
| 红色 LED | GPIO62,低电平点亮 | GPIO65,高电平点亮 |
| 绿色 LED | GPIO20,低电平点亮 | GPIO66,高电平点亮 |
| 蓝色 LED | GPIO63,低电平点亮 | GPIO71,高电平点亮 |
| 用户按键 | GPIO53,下拉输入,按下为高电平 | GPIO64,下拉输入,按下为高电平 |
NOTE
两块开发板的 CanMV MicroPython API 基本一致,本节主要差异来自板载硬件连接的 GPIO 不同。为了让两块开发板的读者都有同样的学习体验,下面在涉及板载 RGB 灯和用户按键的实验中,会分别给出两块开发板的完整例程。
四 名词解释
| 名词 | 说明 |
|---|---|
| GPIO | General Purpose Input Output (通用输入/输出) |
| FPIOA | Field Programmable Input and Output Array(现场可编程 IO 阵列) |
| iomux | Pin multiplexing(管脚功能选择) |
| Pin 模块 | 在MicroPython 中用于控制设备的GPIO 引脚。 |
INFO
- GPIO就是我们常说的IO引脚,它直接从芯片上引出,可以被设置为输入或输出信号。没有额外功能的GPIO主要用来控制简单的设备,比如点亮LED灯或者读取按键的状态。
- FPIOA是一种灵活的输入输出阵列,允许芯片对IO功能进行重新配置,类似用STM32上的复用引脚,也就是说不同的引脚可以根据需求重新定义。
- iomux就是引脚复用了,主要是配置物理PAD(管脚)的功能,由于soc功能多管脚(pads)少,多个功能共享同一个I/O管脚(pads),但是一个pads同一时间只能使用其中一个功能,所以需要IOMUX进行功能选择。IOMUX也叫FPIOA,Pin multiplexing,管脚功能选择等,在K230中,FPIOA就是iomux。
- 我们使用 MicroPython 语法来控制K230的引脚,
machine.Pin模块用于控制微控制器的 GPIO 引脚。Pin类提供了访问和控制硬件引脚的功能,主要包括配置引脚模式,读写引脚状态等。
五 开发板引脚介绍
5.1 兼容树莓派40Pin排针引脚
【立创·庐山派K230-CanMV开发板 排针脚示意图】 
【立创·庐山派Lite-K230D-CanMV开发板 排针脚示意图】

上图可以看到这两个开发板的排针脚部分IO是不一样的,这是因为K230D相较于K230少了很多引脚,为了凑这完整的兼容树莓派引脚,也是不得不这么做了,不少引脚和板载功能是同一个IO,用之前需要先查询下原理图。 排针处的GPIO还可复用为IIC,SPI,PWM,PDM,IIS等功能,这些IO电平都是3.3V的。
需要注意的是,这里的排针都没有ADC功能,也就是没有模拟功能,这里主要有两个考虑点:
- 考虑到K230的ADC仅支持最高1.8V的输入电压。为了避免用户在使用过程中意外将超过1.8V的电压输入ADC,从而导致芯片损坏,这里并未将ADC接口直接引出到标准的GPIO排针上
- K230的ADC引脚只能作为模拟输入,不能当做普通GPIO来使用。
结合以上两点本开发板采用了FPC排线座来引出ADC引脚,需要用到的时候用一个fpc转出来就可以了,立创·庐山派K230-CanMV开发板 引出来4个ADC通道,可以连接两个摇杆;立创·庐山派Lite-K230D-CanMV开发板 由于所用的K230D芯片只有3个ADC,所以只引出了3个ADC通道。
5.2 GH1.25座和大焊盘触点引出
立创·庐山派K230-CanMV开发板

左边是立创·庐山派-K230-CanMV开发板的正面图,右边是立创·庐山派-K230-CanMV开发板的背面图,分别用3个GH1.25-4P带锁接口和3组2.54mm间距的大触点焊盘接口对GPIO进行了引出。它们既可以被当做普通GPIO来控制,也可以被复用为串口(UART)来和外部设备进行通讯,其中串口2还可以被复用为IIC2。
立创·庐山派Lite-K230D-CanMV开发板

左边是立创·庐山派Lite-K230D-CanMV开发板的正面图,右边是立创·庐山派Lite-K230D-CanMV开发板的背面图,分别用3个GH1.25-4P带锁接口和3组2.54mm间距的大触点焊盘接口对GPIO进行了引出。它们既可以被当做普通GPIO来控制,也可以被复用为串口(UART)来和外部设备进行通讯,其中串口2还可以被复用为IIC2。
串口0:
UART0_TXD:GPIO38UART0_RXD:GPIO39串口2:
UART2_TXD:GPIO11UART2_RXD:GPIO12串口3【要注意两个板子的引脚是不一样的哦】:
- 立创·庐山派K230-CanMV开发板
UART3_TXD:GPIO50UART3_RXD:GPIO51- 立创·庐山派Lite-K230D-CanMV开发板
UART3_TXD:GPIO32UART3_RXD:GPIO33
⚠️注意!
串口0默认被系统大核RT-Smart占用,作为调试串口使用,所以以上三个串口我们在运行CanMV固件时能用的只有串口2和串口3。如果使用 立创·庐山派Lite-K230D-CanMV开发板,还要额外留意 40Pin 排针与摄像头、LCD、HDMI、功放、风扇等板载功能的复用关系。
5.3 各引脚可复用功能
使用以下程序即可打印出各GPIO的复用功能,把程序复制到CanMV IDE K230里面然后执行就可以从串行终端中得到所有引脚的复用信息了,为了方便大家查阅,这里也一并以表格的形式放出来。
from machine import FPIOA
# 实例化FPIOA
fpioa = FPIOA()
# 打印所有引脚配置
fpioa.help()2
3
4
5

查看全部引脚的可复用功能
| pin | cur func | can be func |
|---|---|---|
| 0 | GPIO0 | GPIO0/BOOT0/RESV/ |
| 1 | GPIO1 | GPIO1/BOOT1/RESV/ |
| 2 | JTAG_TCK | GPIO2/JTAG_TCK/PULSE_CNTR0/RESV/ |
| 3 | JTAG_TDI | GPIO3/JTAG_TDI/PULSE_CNTR1/UART1_TXD/RESV/ |
| 4 | JTAG_TDO | GPIO4/JTAG_TDO/PULSE_CNTR2/UART1_RXD/RESV/ |
| 5 | UART2_TXD | GPIO5/JTAG_TMS/PULSE_CNTR3/UART2_TXD/RESV/ |
| 6 | UART2_RXD | GPIO6/JTAG_RST/PULSE_CNTR4/UART2_RXD/RESV/ |
| 7 | PWM2 | GPIO7/PWM2/IIC4_SCL/RESV/RESV/ |
| 8 | PWM3 | GPIO8/PWM3/IIC4_SDA/RESV/RESV/ |
| 9 | PWM4 | GPIO9/PWM4/UART1_TXD/IIC1_SCL/RESV/ |
| 10 | CTRL_IN_3D | GPIO10/CTRL_IN_3D/UART1_RXD/IIC1_SDA/RESV/ |
| 11 | CTRL_O1_3D | GPIO11/CTRL_O1_3D/UART2_TXD/IIC2_SCL/RESV/ |
| 12 | CTRL_O2_3D | GPIO12/CTRL_O2_3D/UART2_RXD/IIC2_SDA/RESV/ |
| 13 | M_CLK1 | GPIO13/M_CLK1/RESV/ |
| 14 | OSPI_CS | GPIO14/OSPI_CS/RESV/QSPI0_CS0/RESV/ |
| 15 | OSPI_CLK | GPIO15/OSPI_CLK/RESV/QSPI0_CLK/RESV/ |
| 16 | OSPI_D0 | GPIO16/OSPI_D0/QSPI1_CS4/QSPI0_D0/RESV/ |
| 17 | OSPI_D1 | GPIO17/OSPI_D1/QSPI1_CS3/QSPI0_D1/RESV/ |
| 18 | OSPI_D2 | GPIO18/OSPI_D2/QSPI1_CS2/QSPI0_D2/RESV/ |
| 19 | OSPI_D3 | GPIO19/OSPI_D3/QSPI1_CS1/QSPI0_D3/RESV/ |
| 20 | GPIO20 | GPIO20/OSPI_D4/QSPI1_CS0/PULSE_CNTR0/RESV/ |
| 21 | OSPI_D5 | GPIO21/OSPI_D5/QSPI1_CLK/PULSE_CNTR1/RESV/ |
| 22 | OSPI_D6 | GPIO22/OSPI_D6/QSPI1_D0/PULSE_CNTR2/RESV/ |
| 23 | GPIO23 | GPIO23/OSPI_D7/QSPI1_D1/PULSE_CNTR3/RESV/ |
| 24 | GPIO24 | GPIO24/OSPI_DQS/QSPI1_D2/PULSE_CNTR4/RESV/ |
| 25 | GPIO25 | GPIO25/PWM5/QSPI1_D3/PULSE_CNTR5/RESV/ |
| 26 | PDM_CLK | GPIO26/MMC1_CLK/RESV/PDM_CLK/ |
| 27 | GPIO27 | GPIO27/MMC1_CMD/PULSE_CNTR5/PDM_IN0/RESV/ |
| 28 | GPIO28 | GPIO28/MMC1_D0/UART3_TXD/PDM_IN1/RESV/ |
| 29 | GPIO29 | GPIO29/MMC1_D1/UART3_RXD/CTRL_IN_3D/RESV/ |
| 30 | GPIO30 | GPIO30/MMC1_D2/UART3_RTS/CTRL_O1_3D/RESV/ |
| 31 | GPIO31 | GPIO31/MMC1_D3/UART3_CTS/CTRL_O2_3D/RESV/ |
| 32 | IIS_CLK | GPIO32/IIC0_SCL/IIS_CLK/UART3_TXD/RESV/ |
| 33 | IIS_WS | GPIO33/IIC0_SDA/IIS_WS/UART3_RXD/RESV/ |
| 34 | IIS_D_IN0 | GPIO34/IIC1_SCL/IIS_D_IN0/PDM_IN3/UART3_RTS/ |
| 35 | IIS_D_OUT0 | GPIO35/IIC1_SDA/IIS_D_OUT0/PDM_IN1/UART3_CTS/ |
| 36 | GPIO36 | GPIO36/IIC3_SCL/IIS_D_IN1/PDM_IN2/UART4_TXD/ |
| 37 | GPIO37 | GPIO37/IIC3_SDA/IIS_D_OUT1/PDM_IN0/UART4_RXD/ |
| 38 | UART0_TXD | GPIO38/UART0_TXD/RESV/QSPI1_CS0/HSYNC0/ |
| 39 | UART0_RXD | GPIO39/UART0_RXD/RESV/QSPI1_CLK/VSYNC0/ |
| 40 | IIC1_SCL | GPIO40/UART1_TXD/IIC1_SCL/QSPI1_D0/RESV/ |
| 41 | IIC1_SDA | GPIO41/UART1_RXD/IIC1_SDA/QSPI1_D1/RESV/ |
| 42 | GPIO42 | GPIO42/UART1_RTS/PWM0/QSPI1_D2/RESV/ |
| 43 | GPIO43 | GPIO43/UART1_CTS/PWM1/QSPI1_D3/RESV/ |
| 44 | IIC3_SCL | GPIO44/UART2_TXD/IIC3_SCL/RESV/SPI2AXI_CK/ |
| 45 | IIC3_SDA | GPIO45/UART2_RXD/IIC3_SDA/RESV/SPI2AXI_CS/ |
| 46 | IIC4_SCL | GPIO46/UART2_RTS/PWM2/IIC4_SCL/RESV/ |
| 47 | IIC4_SDA | GPIO47/UART2_CTS/PWM3/IIC4_SDA/RESV/ |
| 48 | IIC0_SCL | GPIO48/UART4_TXD/RESV/IIC0_SCL/SPI2AXI_DI/ |
| 49 | IIC0_SDA | GPIO49/UART4_RXD/RESV/IIC0_SDA/SPI2AXI_DO/ |
| 50 | UART3_TXD | GPIO50/UART3_TXD/IIC2_SCL/QSPI0_CS4/RESV/ |
| 51 | UART3_RXD | GPIO51/UART3_RXD/IIC2_SDA/QSPI0_CS3/RESV/ |
| 52 | GPIO52 | GPIO52/UART3_RTS/PWM4/IIC3_SCL/RESV/ |
| 53 | GPIO53 | GPIO53/UART3_CTS/PWM5/IIC3_SDA/ |
| 54 | MMC1_CMD | GPIO54/QSPI0_CS0/MMC1_CMD/PWM0/RESV/ |
| 55 | MMC1_CLK | GPIO55/QSPI0_CLK/MMC1_CLK/PWM1/RESV/ |
| 56 | MMC1_D0 | GPIO56/QSPI0_D0/MMC1_D0/PWM2/RESV/ |
| 57 | MMC1_D1 | GPIO57/QSPI0_D1/MMC1_D1/PWM3/RESV/ |
| 58 | MMC1_D2 | GPIO58/QSPI0_D2/MMC1_D2/PWM4/RESV/ |
| 59 | MMC1_D3 | GPIO59/QSPI0_D3/MMC1_D3/PWM5/ |
| 60 | GPIO60 | GPIO60/PWM0/IIC0_SCL/QSPI0_CS2/HSYNC1/ |
| 61 | GPIO61 | GPIO61/PWM1/IIC0_SDA/QSPI0_CS1/VSYNC1/ |
| 62 | M_CLK2 | GPIO62/M_CLK2/UART3_DE/RESV/ |
| 63 | M_CLK3 | GPIO63/M_CLK3/UART3_RE/RESV/ |
六 GPIO 使用指南
6.1 使用FPIOA
FPIOA(灵活外设输入/输出阵列)是 K230 CanMV 系列微处理器提供的功能,允许用户灵活配置引脚来连接不同的外设。通过这个模块,我们可以轻松管理各种硬件接口。FPIOA 可以允许用户根据需要,将特定的引脚分配给不同的功能(比如 SPI、I2C、UART 等),每个引脚在同一时刻只能激活一种功能。使用 FPIOA,可以简化引脚管理,提升芯片的灵活性。
TIP
FPIOA 类位于 machine 模块中,可以像使用其他 Python 模块一样进行操作。如from machine import FPIOA。
6.1.1 构造函数
fpioa = FPIOA()6.1.2 设置引脚功能
fpioa.set_function(pin, func, ie=-1, oe=-1, pu=-1, pd=-1, st=-1, sl=-1, ds=-1)参数:
pin: 要配置的引脚编号,范围:[0, 63]func: 要分配给引脚的功能代码- 普通GPIO:
FPIOA.GPIO0,FPIOA.GPIO1,FPIOA.GPIO2等,范围【0,63】 - 串口:
FPIOA.UART0_TXD,FPIOA.UART0_RXD,FPIOA.UART1_RXD等。 - IIC:
FPIOA.IIC0_SCL,FPIOA.IIC0_SDA,FPIOA.IIC1_SCL,FPIOA.IIC1_SDA等。 - PWM:
FPIOA.PWM0,FPIOA.PWM1,FPIOA.PWM2等。 - 以及其他功能,更多详情请参考上方 3.3章 的 各引脚可复用功能。
- 普通GPIO:
set_function其他可选参数
ie: 输入使能,可选参数(-1为默认值,0为不使能,1为使能)oe: 输出使能,可选参数(-1为默认值,0为不使能,1为使能)pu: 上拉使能,可选参数(-1为默认值,0为不使能,1为使能)pd: 下拉使能,可选参数(-1为默认值,0为不使能,1为使能)st: st 使能,可选参数(-1为默认值,0为不使能,1为使能)- 输入施密特触发器控制使能,使能后提高信号的干扰抵抗能力和改善信号的完整性,简单来说,施密特触发器是一种具有滞回特性的电子电路,其输出只在输入信号超过设定的正向或负向阈值时改变,提高了对噪声的抗干扰能力。
sl: sl 使能,可选参数(-1为默认值,0为不使能,1为使能)- 目前已经不再使用,建议直接设置为-1.
ds: 驱动电流能力,可选参数(-1为默认值)- 默认值为
7,范围0-15,数值越大IO的驱动能力越强,除了boot 0 1其他引脚都可以设置最大15。
- 默认值为
更多关于K230 IO引脚的信息,设计最大电压,施密特触发器高低阈值,上下拉电阻阻值,各IO驱动电流能力。

6.1.3 引脚功能读取获取
fpioa.get_pin_func(pin)参数:
pin: 要配置的引脚编号,范围:[0, 63]。
返回值:
返回引脚当前的功能号。
示例
当前返回的功能号还不是字符串,是标号,大家根据获取到的序号来比对下方的序号确定。 例如以下代码,获取38号引脚的当前功能:
from machine import FPIOA
print(fpioa.get_pin_func(38))2
3
其运行结果打印出来的数值为166,结合下方表格,可知166代表当前38号引脚的功能为 UART0_TXD,串口0被RT SMART占用,主要用来调试。
| 序号 | 功能名 |
|---|---|
| 0 | GPIO0 |
| 1 | GPIO1 |
| 2 | GPIO2 |
| 3 | GPIO3 |
| 4 | GPIO4 |
| 5 | GPIO5 |
| 6 | GPIO6 |
| 7 | GPIO7 |
| 8 | GPIO8 |
| 9 | GPIO9 |
| 10 | GPIO10 |
| 11 | GPIO11 |
| 12 | GPIO12 |
| 13 | GPIO13 |
| 14 | GPIO14 |
| 15 | GPIO15 |
| 16 | GPIO16 |
| 17 | GPIO17 |
| 18 | GPIO18 |
| 19 | GPIO19 |
| 20 | GPIO20 |
| 21 | GPIO21 |
| 22 | GPIO22 |
| 23 | GPIO23 |
| 24 | GPIO24 |
| 25 | GPIO25 |
| 26 | GPIO26 |
| 27 | GPIO27 |
| 28 | GPIO28 |
| 29 | GPIO29 |
| 30 | GPIO30 |
| 31 | GPIO31 |
| 32 | GPIO32 |
| 33 | GPIO33 |
| 34 | GPIO34 |
| 35 | GPIO35 |
| 36 | GPIO36 |
| 37 | GPIO37 |
| 38 | GPIO38 |
| 39 | GPIO39 |
| 40 | GPIO40 |
| 41 | GPIO41 |
| 42 | GPIO42 |
| 43 | GPIO43 |
| 44 | GPIO44 |
| 45 | GPIO45 |
| 46 | GPIO46 |
| 47 | GPIO47 |
| 48 | GPIO48 |
| 49 | GPIO49 |
| 50 | GPIO50 |
| 51 | GPIO51 |
| 52 | GPIO52 |
| 53 | GPIO53 |
| 54 | GPIO54 |
| 55 | GPIO55 |
| 56 | GPIO56 |
| 57 | GPIO57 |
| 58 | GPIO58 |
| 59 | GPIO59 |
| 60 | GPIO60 |
| 61 | GPIO61 |
| 62 | GPIO62 |
| 63 | GPIO63 |
| 64 | BOOT0 |
| 65 | BOOT1 |
| 66 | CI0 |
| 67 | CI1 |
| 68 | CI2 |
| 69 | CI3 |
| 70 | CO0 |
| 71 | CO1 |
| 72 | CO2 |
| 73 | CO3 |
| 74 | DI0 |
| 75 | DI1 |
| 76 | DI2 |
| 77 | DI3 |
| 78 | DO0 |
| 79 | DO1 |
| 80 | DO2 |
| 81 | DO3 |
| 82 | HSYNC0 |
| 83 | HSYNC1 |
| 84 | IIC0_SCL |
| 85 | IIC0_SDA |
| 86 | IIC1_SCL |
| 87 | IIC1_SDA |
| 88 | IIC2_SCL |
| 89 | IIC2_SDA |
| 90 | IIC3_SCL |
| 91 | IIC3_SDA |
| 92 | IIC4_SCL |
| 93 | IIC4_SDA |
| 94 | IIS_CLK |
| 95 | IIS_D_IN0_PDM_IN3 |
| 96 | IIS_D_IN1_PDM_IN2 |
| 97 | IIS_D_OUT0_PDM_IN1 |
| 98 | IIS_D_OUT1_PDM_IN0 |
| 99 | IIS_WS |
| 100 | JTAG_RST |
| 101 | JTAG_TCK |
| 102 | JTAG_TDI |
| 103 | JTAG_TDO |
| 104 | JTAG_TMS |
| 105 | M_CLK1 |
| 106 | M_CLK2 |
| 107 | M_CLK3 |
| 108 | MMC1_CLK |
| 109 | MMC1_CMD |
| 110 | MMC1_D0 |
| 111 | MMC1_D1 |
| 112 | MMC1_D2 |
| 113 | MMC1_D3 |
| 114 | OSPI_CLK |
| 115 | OSPI_CS |
| 116 | OSPI_D0 |
| 117 | OSPI_D1 |
| 118 | OSPI_D2 |
| 119 | OSPI_D3 |
| 120 | OSPI_D4 |
| 121 | OSPI_D5 |
| 122 | OSPI_D6 |
| 123 | OSPI_D7 |
| 124 | OSPI_DQS |
| 125 | PDM_IN0 |
| 126 | PDM_IN1 |
| 127 | PDM_IN2 |
| 128 | PDM_IN3 |
| 129 | PULSE_CNTR0 |
| 130 | PULSE_CNTR1 |
| 131 | PULSE_CNTR2 |
| 132 | PULSE_CNTR3 |
| 133 | PULSE_CNTR4 |
| 134 | PULSE_CNTR5 |
| 135 | PWM0 |
| 136 | PWM1 |
| 137 | PWM2 |
| 138 | PWM3 |
| 139 | PWM4 |
| 140 | PWM5 |
| 141 | QSPI0_CLK |
| 142 | QSPI0_CS0 |
| 143 | QSPI0_CS1 |
| 144 | QSPI0_CS2 |
| 145 | QSPI0_CS3 |
| 146 | QSPI0_CS4 |
| 147 | QSPI0_D0 |
| 148 | QSPI0_D1 |
| 149 | QSPI0_D2 |
| 150 | QSPI0_D3 |
| 151 | QSPI1_CLK |
| 152 | QSPI1_CS0 |
| 153 | QSPI1_CS1 |
| 154 | QSPI1_CS2 |
| 155 | QSPI1_CS3 |
| 156 | QSPI1_CS4 |
| 157 | QSPI1_D0 |
| 158 | QSPI1_D1 |
| 159 | QSPI1_D2 |
| 160 | QSPI1_D3 |
| 161 | SPI2AXI_CK |
| 162 | SPI2AXI_CS |
| 163 | SPI2AXI_DI |
| 164 | SPI2AXI_DO |
| 165 | UART0_RXD |
| 166 | UART0_TXD |
| 167 | UART1_CTS |
| 168 | UART1_RTS |
| 169 | UART1_RXD |
| 170 | UART1_TXD |
| 171 | UART2_CTS |
| 172 | UART2_RTS |
| 173 | UART2_RXD |
| 174 | UART2_TXD |
| 175 | UART3_CTS |
| 176 | UART3_DE |
| 177 | UART3_RE |
| 178 | UART3_RTS |
| 179 | UART3_RXD |
| 180 | UART3_TXD |
| 181 | UART4_RXD |
| 182 | UART4_TXD |
| 183 | PDM_CLK |
| 184 | VSYNC0 |
| 185 | VSYNC1 |
| 186 | CTRL_IN_3D |
| 187 | CTRL_O1_3D |
| 188 | CTRL_O2_3D |
| 189 | TEST_PIN0 |
| 190 | TEST_PIN1 |
| 191 | TEST_PIN2 |
| 192 | TEST_PIN3 |
| 193 | TEST_PIN4 |
| 194 | TEST_PIN5 |
| 195 | TEST_PIN6 |
| 196 | TEST_PIN7 |
| 197 | TEST_PIN8 |
| 198 | TEST_PIN9 |
| 199 | TEST_PIN10 |
| 200 | TEST_PIN11 |
| 201 | TEST_PIN12 |
| 202 | TEST_PIN13 |
| 203 | TEST_PIN14 |
| 204 | TEST_PIN15 |
| 205 | TEST_PIN16 |
| 206 | TEST_PIN17 |
| 207 | TEST_PIN18 |
| 208 | TEST_PIN19 |
| 209 | TEST_PIN20 |
| 210 | TEST_PIN21 |
| 211 | TEST_PIN22 |
| 212 | TEST_PIN23 |
| 213 | TEST_PIN24 |
| 214 | TEST_PIN25 |
| 215 | TEST_PIN26 |
| 216 | TEST_PIN27 |
| 217 | TEST_PIN28 |
| 218 | TEST_PIN29 |
| 219 | TEST_PIN30 |
| 220 | TEST_PIN31 |
| 221 | FUNC_MAX |
TIP
使用以上函数可以查询指定引脚的当前功能。
fpioa.get_pin_num(func)获取指定功能当前所在的引脚。
参数
func: 功能号
返回值
返回引脚号,如果未找到相应功能则返回None 。
示例
例如以下代码,获取38号引脚的当前功能:
from machine import FPIOA
print(fpioa.get_pin_num(FPIOA.UART0_TXD))2
3
其运行结果打印出来为38,标明当前UART0_TXD功能正在被38引脚使用。
TIP
使用以上函数可以查找想要功能的当前引脚。
6.1.4 帮助方法
fpioa.help([number, func=False])打印引脚配置提示信息。
参数
umber`: 引脚号或功能号, 可选参数
func: 是否启用功能号查询,默认为False
返回值
可能为以下三种:
- 所有引脚的配置信息(未设置 umber`)
- 指定引脚的详细配置信息(设置了 umber
,未设置func或设置为False`) - 指定功能的所有可配置引脚号(设置了 umber
,并将func设置为True`)
示例
例如以下代码,获取38号引脚的当前功能:
from machine import FPIOA
print("---run fpioa.help(38) result:")
fpioa.help(38)
print("---run fpioa.help(38) result:")
fpioa.help(FPIOA.UART0_TXD,func=True)2
3
4
5
6
其运行结果打印如下:
---run fpioa.help(38) result:
|pin num |38 |
|current config |UART0_TXD,ie:0,oe:1,pd:0,pu:0,msc:0-3.3v,ds:7,st:1,sl:0,di:0|
|can be function |GPIO38/UART0_TXD/RESV/QSPI1_CS0/HSYNC0/ |
---run fpioa.help(FPIOA.UART0_TXD,func=True) result:
UART0_TXD function can be set to PIN382
3
4
5
6
6.2 使用 machine.Pin
machine.Pin 类是 MicroPython 中用于控制输入/输出引脚的核心模块。通过该模块,我们可以轻松地管理微控制器上的 GPIO 引脚,进行基础的输入输出操作。
要使用 machine.Pin,首先需要导入该模块:
from machine import Pin6.2.1 构造函数
pin = Pin(index, mode, pull=Pin.PULL_NONE, drive=7)用于构造pin对象,可同时对引脚进行初始化。
参数
index: 引脚编号,范围为 [0, 63]。mode: 引脚的模式,支持输入模式或输出模式。Pin.OUTPin.IN
pull: 上下拉配置(可选),默认为Pin.PULL_NONE。Pin.PULL_NONEPin.PULL_UPPin.PULL_DOWN
drive: 驱动能力配置(可选),默认值为 7。- 默认值为
7,范围0-15,数值越大IO的驱动能力越强,除了boot 0 1其他引脚都可以设置最大15。
- 默认值为
⚠️注意!
K230的普通IO无法被配置为模拟输入输出功能,ADC功能有固定的引脚且最高只能输入1.8V,在板子上的物理引出形式为一个6P的FPC座子。
更多关于K230 IO引脚的信息,设计最大电压,施密特触发器高低阈值,上下拉电阻阻值,各IO驱动电流能力等。

示例:
# 实例化Pin2为输出
pin = Pin(2, Pin.OUT, pull=Pin.PULL_NONE, drive=7)2
6.2.2 初始化引脚
pin.init(mode, pull=Pin.PULL_NONE, drive=7)用于初始化引脚的模式、上下拉配置及驱动能力。
参数
mode: 引脚的模式(输入或输出)。Pin.OUTPin.IN
pull: 上下拉配置(可选),默认值为Pin.PULL_NONE。Pin.PULL_NONEPin.PULL_UPPin.PULL_DOWN
drive: 驱动能力(可选),默认值为 7。- 默认值为
7,范围0-15,数值越大IO的驱动能力越强,除了boot 0 1其他引脚都可以设置最大15。
- 默认值为
示例:
# 初始化pin为输入
pin.init(Pin.IN, pull=Pin.PULL_UP, drive=7)2
6.2.3 控制 PIN
value 方法
pin.value([value])获取引脚的输入电平值或设置引脚的输出电平。
参数
value: 输出值(可选),如果传递该参数则设置引脚输出为指定值。如果不传参则返回引脚的当前输入电平值。- 0:输出低电平。
- 1:输出高电平。
返回值
返回空或当前引脚的输入电平值。
- 0:当前引脚输入为低电平
- 1:当前引脚输入为高电平
示例:
# 设置输出为高
pin.value(1)
# 设置输出为低
pin.value(0)
# 获取引脚输入电平
print(pin.value())2
3
4
5
6
mode 方法
pin.mode([mode])主要用来获取或设置引脚的模式。
参数
mode: 引脚模式(输入或输出),如果不传参则返回当前引脚的模式。Pin.OUTPin.IN
返回值
返回空或当前引脚模式。
示例:
# 设置引脚为输入模式
pin.mode(Pin.IN)
# 设置引脚为输出模式
pin.mode(Pin.OUT)2
3
4
pull 方法
pin.pull([pull])获取或设置引脚的上下拉配置。
参数
pull: 上下拉配置(可选),如果不传参则返回当前上下拉配置。
返回值
返回空或当前引脚的上下拉配置。
drive 方法
pin.drive([drive])获取或设置引脚的驱动能力。
参数
drive: 驱动能力(可选),如果不传参则返回当前驱动能力。
返回值
返回空或当前引脚的驱动能力。
on 方法
pin.on()将引脚输出设置为高电平。
off 方法
pin.off()将引脚输出设置为低电平。
high 方法
pin.high()将引脚输出设置为高电平。
low 方法
pin.low()将引脚输出设置为低电平。
6.2.4 中断模式
⚠️注意!
当前的CanMV固件还不支持将引脚配置为中断模式,请等待后续更新。
PIN综合示例
from machine import Pin
from machine import FPIOA
# 实例化FPIOA
fpioa = FPIOA()
# 设置Pin2为GPIO2
fpioa.set_function(2, FPIOA.GPIO2)
# 实例化Pin2为输出
pin = Pin(2, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
# 设置输出为高
pin.value(1)
# pin.on()
# pin.high()
# 设置输出为低
pin.value(0)
# pin.off()
# pin.low()
# 初始化Pin2为输入
pin.init(Pin.IN, pull=Pin.PULL_UP, drive=7)
# 获取输入
print(pin.value())
# 设置模式
pin.mode(Pin.IN)
# 获取模式
print(pin.mode())
# 设置上下拉
pin.pull(Pin.PULL_NONE)
# 获取上下拉
print(pin.pull())
# 设置驱动能力
pin.drive(7)
# 获取驱动能力
print(pin.drive())2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
七 控制 RGB 灯
如果说学C语言的第一个程序是Hello world!,那么我们学习嵌入式板子的第一个程序就应该是点亮一个LED灯。RGB灯其实就是一种由三个LED灯组成的灯,由三个基本颜色的 LED 组成:红色、绿色和蓝色。通过调整这三种颜色的亮度,我们可以混合出几乎任何颜色,不过庐山派CanMV开发板的这个RGB灯为了节约多功能的IO,并没有连接到具有PWM功能的IO上,所以只能控制个LED灯的亮灭而改变不了亮度。初学时我们可以只点亮其中的一个灯。
7.1 LED 灯结构组成
LED 灯(发光二极管)是一种半导体光源,主要结构包括以下部分:
- 外壳:通常由塑料或玻璃制成,用于保护内部元件。
- 发光材料:LED 最核心的部分,由特殊半导体材料制成,例如:常见的 InGaN(氮化铟镓)或 AlInGaP(铝铟镓磷)。
- 芯片:用于产生光的发光二极管芯片。
- 引线:提供电连接的金属引线。
- 焊点:将LED 芯片与引线连接在一起的焊接点。
- 电极:负责连接半导体材料与外部电路,通常由金属制成。
- 反射腔:用于增强发光效果的一个结构,将发出的光反射到正面。
7.2 LED 灯发光原理
LED(发光二极管)发光原理基于半导体特性。在半导体中,存在着两类载流子:电子(n型半导体)和空穴(p型半导体)。当n型与p型半导体材料接触时,会在交界处形成一个层结。当施加适当的电压时,层结中空穴和电子可重组并释放能量。这个能量以光子的形式释放出来,产生光。
7.3 LED 灯驱动原理
LED 驱动指的是通过稳定的电源为 LED 提供合适的电流和电压,使其正常工作点亮。LED 驱动方式主要有恒流和恒压两种。限定电流的恒流驱动是最常见的方式,因为 LED 灯对电流敏感,电流大于其额定值可能导致损坏。恒流驱动保证了稳定的电流,从而确保了 LED 安全。 LED 灯的驱动比较简单,只需要给将对应的正负极接到单片机的正负极即可驱动。LED的接法也分有两种,灌入电流和输出电流。
TIP
在立创·庐山派K230-CanMV开发板中,我们使用的为共阳级的 RGB 灯,详情请看后面的原理图。立创·庐山派Lite-K230D-CanMV开发板的 RGB 灯是高电平点亮。
- 灌入电流指的是LED的供电电流是由外部提供电流,将电流灌入我们的MCU。
- 输出电流指的是由MCU提供电压电流,将电流输出给LED;如果使用 MCU的GPIO 直接驱动 LED,则驱动能力较弱,可能无法提供足够的电流驱动 LED。
- 需要注意的 是 LED 灯的颜色不同,对应的电压也不同。电流不可过大,通常需要接入220欧姆到10K欧姆左右的限流电阻,限流电阻的阻值越大,LED的亮度越暗。
7.4 RGB灯原理图
结合之前介绍,我们已经知道了LED(发光二极管)灯通常只有一个发光二极管,它能产生单一颜色的光。比如,一个红色 LED 只会发出红光,而一个蓝色 LED 只会发出蓝光。而本庐山派开发板和泰山派一样,板载了一个RGB灯,他是红色、绿色和蓝色 LED 灯的组合,可以单独控制每个 LED 的亮灭。
那么我们先来看一下开发板上的RGB灯分别连接的是哪些引脚,看一下原理图:
立创·庐山派K230-CanMV开发板 的 RGB灯原理图

立创·庐山派Lite-K230D-CanMV开发板 的 RGB灯原理图

这里的LED1就是我们需要控制的用户指示灯,结合左上角的短接符我们可以了解到以下信息:
- 这是一个共阳级的RGB灯,当对应颜色引脚为低电平时对应引脚就会亮。
- 这个RGB灯内部有三个不同颜色的灯珠。
- 立创·庐山派K230-CanMV开发板 上,红灯、绿灯、蓝灯分别接到了 GPIO62、GPIO20 和 GPIO63 上面。
- 立创·庐山派Lite-K230D-CanMV开发板的板载 RGB 灯连接不同:红灯 GPIO65、绿灯 GPIO66、蓝灯 GPIO71,并且是高电平点亮。这其实是因为K230D引脚受限,到最后已经没有3.3V的IO可以用了,只能用这几个1.8V电平的IO了,这个RGB灯本身是3.3V供电的,里面的绿灯、蓝灯的正向压降较高,1.8V 基本无法直接点亮,所以这里加入了三个 MOS 管,从而可以用 1.8V GPIO 控制 3.3V LED 电流,从而间接点亮这个RGB灯。
7.5 基础点灯试验
接下来我们实现一个让 RGB 灯里面的红灯每 0.5 秒闪烁一次的程序。两块开发板的实验现象相同,但红灯引脚和点亮电平不同,所以这里分别给出完整例程。
7.5.1 立创·庐山派K230-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 0 # 立创·庐山派K230-CanMV开发板:低电平点亮
LED_OFF_LEVEL = 1
fpioa = FPIOA()
fpioa.set_function(62, FPIOA.GPIO62) # 红灯
fpioa.set_function(20, FPIOA.GPIO20) # 绿灯
fpioa.set_function(63, FPIOA.GPIO63) # 蓝灯
LED_R = Pin(62, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_G = Pin(20, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_B = Pin(63, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_R.value(LED_OFF_LEVEL)
LED_G.value(LED_OFF_LEVEL)
LED_B.value(LED_OFF_LEVEL)
LED = LED_R # 当前控制红色 LED
try:
while True:
os.exitpoint()
LED.value(LED_ON_LEVEL)
time.sleep(0.5)
LED.value(LED_OFF_LEVEL)
time.sleep(0.5)
except Exception as e:
print("程序退出:", e)
finally:
LED_R.value(LED_OFF_LEVEL)
LED_G.value(LED_OFF_LEVEL)
LED_B.value(LED_OFF_LEVEL)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
7.5.2 立创·庐山派Lite-K230D-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 1 # 立创·庐山派Lite-K230D-CanMV开发板:高电平点亮
LED_OFF_LEVEL = 0
fpioa = FPIOA()
fpioa.set_function(65, FPIOA.GPIO65) # 红灯
fpioa.set_function(66, FPIOA.GPIO66) # 绿灯
fpioa.set_function(71, FPIOA.GPIO71) # 蓝灯
LED_R = Pin(65, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_G = Pin(66, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_B = Pin(71, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_R.value(LED_OFF_LEVEL)
LED_G.value(LED_OFF_LEVEL)
LED_B.value(LED_OFF_LEVEL)
LED = LED_R # 当前控制红色 LED
try:
while True:
os.exitpoint()
LED.value(LED_ON_LEVEL)
time.sleep(0.5)
LED.value(LED_OFF_LEVEL)
time.sleep(0.5)
except Exception as e:
print("程序退出:", e)
finally:
LED_R.value(LED_OFF_LEVEL)
LED_G.value(LED_OFF_LEVEL)
LED_B.value(LED_OFF_LEVEL)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
在上述两段程序中,首先从 machine 导入了 Pin 和 FPIOA 模块,用来初始化和控制 GPIO 引脚;也导入了 time 模块,用来调用时间延迟函数。
两块开发板的代码逻辑完全一致,主要差异只有两处:
- 板载 RGB 灯连接的 GPIO 不同。
- 立创·庐山派K230-CanMV开发板是低电平点亮,立创·庐山派Lite-K230D-CanMV开发板是高电平点亮。
代码里用 LED_ON_LEVEL 和 LED_OFF_LEVEL 把点亮电平集中起来,后面控制 LED 时只需要写 LED.value(LED_ON_LEVEL) 或 LED.value(LED_OFF_LEVEL),初学者看起来会更清楚。
如果要改成闪烁绿色 LED,可以把:
LED = LED_R改成:
LED = LED_G如果要改成闪烁蓝色 LED,可以改成:
LED = LED_B7.6 点亮7种不同颜色的RGB灯
接下来我们实现一个让 RGB 灯组合成七种不同颜色并循环闪烁的程序。两块开发板仍然分别给出完整例程,方便各位直接复制运行。
7.6.1 立创·庐山派K230-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 0
LED_OFF_LEVEL = 1
fpioa = FPIOA()
fpioa.set_function(62, FPIOA.GPIO62) # 红灯
fpioa.set_function(20, FPIOA.GPIO20) # 绿灯
fpioa.set_function(63, FPIOA.GPIO63) # 蓝灯
LED_R = Pin(62, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_G = Pin(20, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_B = Pin(63, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
def set_led(pin, on):
pin.value(LED_ON_LEVEL if on else LED_OFF_LEVEL)
def set_color(r_on, g_on, b_on):
set_led(LED_R, r_on)
set_led(LED_G, g_on)
set_led(LED_B, b_on)
def blink_color(r_on, g_on, b_on, delay):
set_color(r_on, g_on, b_on)
time.sleep(delay)
set_color(False, False, False)
time.sleep(delay)
try:
set_color(False, False, False)
while True:
os.exitpoint()
blink_color(True, False, False, 0.5) # 红色
blink_color(False, True, False, 0.5) # 绿色
blink_color(False, False, True, 0.5) # 蓝色
blink_color(True, True, False, 0.5) # 黄色
blink_color(True, False, True, 0.5) # 紫色
blink_color(False, True, True, 0.5) # 青色
blink_color(True, True, True, 0.5) # 白色
except Exception as e:
print("程序退出:", e)
finally:
set_color(False, False, False)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
7.6.2 立创·庐山派Lite-K230D-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 1
LED_OFF_LEVEL = 0
fpioa = FPIOA()
fpioa.set_function(65, FPIOA.GPIO65) # 红灯
fpioa.set_function(66, FPIOA.GPIO66) # 绿灯
fpioa.set_function(71, FPIOA.GPIO71) # 蓝灯
LED_R = Pin(65, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_G = Pin(66, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
LED_B = Pin(71, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
def set_led(pin, on):
pin.value(LED_ON_LEVEL if on else LED_OFF_LEVEL)
def set_color(r_on, g_on, b_on):
set_led(LED_R, r_on)
set_led(LED_G, g_on)
set_led(LED_B, b_on)
def blink_color(r_on, g_on, b_on, delay):
set_color(r_on, g_on, b_on)
time.sleep(delay)
set_color(False, False, False)
time.sleep(delay)
try:
set_color(False, False, False)
while True:
os.exitpoint()
blink_color(True, False, False, 0.5) # 红色
blink_color(False, True, False, 0.5) # 绿色
blink_color(False, False, True, 0.5) # 蓝色
blink_color(True, True, False, 0.5) # 黄色
blink_color(True, False, True, 0.5) # 紫色
blink_color(False, True, True, 0.5) # 青色
blink_color(True, True, True, 0.5) # 白色
except Exception as e:
print("程序退出:", e)
finally:
set_color(False, False, False)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
这个例程的核心是 set_color(r_on, g_on, b_on) 函数,它分别控制红、绿、蓝三个通道。这里的 True 表示点亮某个颜色通道,False 表示关闭某个颜色通道。
两块开发板的颜色组合逻辑完全一致,差异仍然集中在 GPIO 编号和 LED_ON_LEVEL / LED_OFF_LEVEL。这样写的好处是:后面调用 blink_color(True, False, False, 0.5) 时,不需要再关心当前开发板到底是高电平点亮还是低电平点亮。
八 使用用户按键
立创·庐山派K230-CanMV开发板与立创·庐山派Lite-K230D-CanMV开发板的用户按键位置入下图所示。

8.1 独立按键是什么
独立按键是一种简单的输入设备,广泛应用于各种电子设备中,用于实现基本的用户交互。它们的工作原理通常基于一个简单的机械开关,当按下按键时触发某些操作。独立按键可以有多种尺寸、形状和颜色,便于用户辨识和使用。常见按键展示:

8.2 独立按键结构组成
独立按键的主要结构组成包括:按钮、外壳、弹簧、触点、导电片和引脚。由一个弹性体(如弹簧或金属片)和一个按键帽组成。当按键被用户按下时,弹性体会缩短,使按键帽压缩,使按钮顶部变得接近或触摸基底。当用户松开按钮时,弹性体恢复原状,按键返回初始位置。所以当按键未被按下时,通常触点是分开的,电路是断开的。当按下按键时,导电片触碰到触点,从而形成一个闭合电路。常见按键原理图示意如下: 
8.3 独立按键驱动原理
独立按键驱动是为了让微控制器能识别按键的状态,而微控制器正好可以识别高电平和低电平,所以大多数的按键都是通过给按键的一端接入高电平,一端接入GPIO;或者是给按键的一端接入低电平,一端接入GPIO。通过检测连接按键的引脚有没有发生电平变化,就可以知道按键是否按下。
8.4 消抖措施
我们通常用的按键内部都是机械弹性开关,当它按下或者弹起的时候,机械触点会因为弹性作用而在闭合和断开的瞬间伴随着一连串的抖动。这种抖动会导致输入信号在高低电位之间弹跳,产生不正确的输入。这就是按键抖动现象。消抖措施主要分为软件消抖和硬件消抖:
- 软件消抖:主要是通过编程的方法,设定一个延迟或计时器,确保不读取按键在抖动状态时的电平,避免抖动对程序的影响。
- 硬件RC消抖:在按键电路中加入元器件如电阻、电容组成的RC滤波器,对按键信号进行平滑处理,降低抖动的影响。注意硬件消抖只能改善而不能消除抖动(除非你用比较大的电容,但那样会导致上升沿太缓,也会造成按钮反应时间太长)。同时,如果你只用一个电容直接并联在按键两端,当按键按下时,按键相当于短路了这个电容,但是按钮的电阻又很小,就会有很大的放电电流,造成按钮机械接触点加速老化,从而导致按钮寿命极具减少。所以这个电路的设计需要下一些功夫,一般来说即使你的硬件电路中做了硬件消抖,也是需要软件消抖来配合的。
- 硬件RS触发器消抖:成本极高,每个按钮都需要单独的RS电路,这里不过多介绍,想详细理解请从逻辑门电路开始。

TIP
在本庐山派开发板中,按键部分的电路很简单(如下面的原理图所示),没有使用硬件消抖,所以在需要使用按钮来进行状态切换功能时需要进行软件消抖处理。
8.5 板载独立按键原理图

立创·庐山派-K230-CanMV开发板的原理图中,将按键一端(1号引脚)通过电阻R78接到3.3V的高电平上,另一端(2号引脚)接到K230芯片的引脚GPIO53上,3号引脚和4号引脚是我们板载侧按按钮的固定角,没有电气作用,只是用来固定按键的。这样当按键按下时,1号引脚和2号引脚就会导通,GPIO53的电平就会变为3.3V。

立创·庐山派Lite-K230D-CanMV开发板的用户按键连接到 GPIO64,同样是下拉输入,按下为高电平,只不过因为这个IO的电平的1.8V的。
这里面电阻(R78/R84)的作用是限流(害怕初学者不小心给设置成推挽输出了)。
在这里要注意的是要在芯片内部将该GPIO(GPIO53/64)设置为下拉输入模式,这样当按钮没被按下时,引脚为默认的低电平状态。
8.6 按键控制板载RGB灯亮灭
当用户按下用户按键时关闭红色 LED,松开按键时点亮红色 LED。两块开发板的按键电平逻辑一致,都是按下为高电平,但按键 GPIO 和红灯 GPIO 不同。
8.6.1 立创·庐山派K230-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 0
LED_OFF_LEVEL = 1
BUTTON_PRESS_LEVEL = 1
fpioa = FPIOA()
fpioa.set_function(62, FPIOA.GPIO62) # 红灯
fpioa.set_function(53, FPIOA.GPIO53) # 用户按键
LED_R = Pin(62, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
button = Pin(53, Pin.IN, Pin.PULL_DOWN)
try:
LED_R.value(LED_OFF_LEVEL)
while True:
os.exitpoint()
if button.value() == BUTTON_PRESS_LEVEL:
LED_R.value(LED_OFF_LEVEL) # 按下按键,关闭红灯
else:
LED_R.value(LED_ON_LEVEL) # 松开按键,点亮红灯
time.sleep_ms(10)
except Exception as e:
print("程序退出:", e)
finally:
LED_R.value(LED_OFF_LEVEL)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
8.6.2 立创·庐山派Lite-K230D-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 1
LED_OFF_LEVEL = 0
BUTTON_PRESS_LEVEL = 1
fpioa = FPIOA()
fpioa.set_function(65, FPIOA.GPIO65) # 红灯
fpioa.set_function(64, FPIOA.GPIO64) # 用户按键
LED_R = Pin(65, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
button = Pin(64, Pin.IN, Pin.PULL_DOWN)
try:
LED_R.value(LED_OFF_LEVEL)
while True:
os.exitpoint()
if button.value() == BUTTON_PRESS_LEVEL:
LED_R.value(LED_OFF_LEVEL) # 按下按键,关闭红灯
else:
LED_R.value(LED_ON_LEVEL) # 松开按键,点亮红灯
time.sleep_ms(10)
except Exception as e:
print("程序退出:", e)
finally:
LED_R.value(LED_OFF_LEVEL)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
前半部分和我们在前面学习的点亮 RGB 灯是一样的,都是先通过 FPIOA 把对应引脚配置为 GPIO 功能,再用 Pin 创建控制对象。不同的是,这里又增加了一个用户按键输入引脚。
两块开发板的按键都是下拉输入,按下时 button.value() 读到 1,松开时读到 0。本例程关注的是“按键当前是否按下”,不关注“按键状态有没有发生变化”,所以这里暂时不做消抖处理。
8.7 用按键切换RGB灯状态
如果希望“按一下切换一次 LED 状态”,就不能只看按键当前是否按下,还要判断按键是否刚刚从松开变为按下,并加入软件消抖。
流程图如下:
flowchart TD
A[开始] --> B[创建 FPIOA 对象]
B --> C[设置 GPIO 引脚功能]
C --> D[实例化 LED 和按键引脚]
D --> E[初始化 LED 状态并设置消抖时间]
E --> F[进入主循环]
F --> G[获取按键状态和当前时间]
G --> H{按键从 0 变为 1?}
H -- 是 --> I{时间间隔 > 消抖时间?}
I -- 是 --> J[切换 LED 状态]
J --> K{当前 LED 状态?}
K -- 亮 --> L[熄灭 LED]
K -- 灭 --> M[点亮 LED]
L --> N[更新最后按下时间]
M --> N
N --> O[更新上次按键状态]
O --> F
H -- 否 --> P[更新上次按键状态]
P --> F
8.7.1 立创·庐山派K230-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 0
LED_OFF_LEVEL = 1
BUTTON_PRESS_LEVEL = 1
DEBOUNCE_MS = 20
fpioa = FPIOA()
fpioa.set_function(62, FPIOA.GPIO62) # 红灯
fpioa.set_function(53, FPIOA.GPIO53) # 用户按键
LED_R = Pin(62, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
button = Pin(53, Pin.IN, Pin.PULL_DOWN)
last_press_time = 0
button_last_state = 0
led_on = False
def set_led(on):
LED_R.value(LED_ON_LEVEL if on else LED_OFF_LEVEL)
try:
set_led(False)
while True:
os.exitpoint()
button_state = button.value()
current_time = time.ticks_ms()
if button_state == BUTTON_PRESS_LEVEL and button_last_state != BUTTON_PRESS_LEVEL:
if time.ticks_diff(current_time, last_press_time) > DEBOUNCE_MS:
led_on = not led_on
set_led(led_on)
last_press_time = current_time
button_last_state = button_state
time.sleep_ms(10)
except Exception as e:
print("程序退出:", e)
finally:
set_led(False)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
8.7.2 立创·庐山派Lite-K230D-CanMV开发板完整例程
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
# 编写者:LCKFB-YZH
from machine import Pin
from machine import FPIOA
import os
import time
LED_ON_LEVEL = 1
LED_OFF_LEVEL = 0
BUTTON_PRESS_LEVEL = 1
DEBOUNCE_MS = 20
fpioa = FPIOA()
fpioa.set_function(65, FPIOA.GPIO65) # 红灯
fpioa.set_function(64, FPIOA.GPIO64) # 用户按键
LED_R = Pin(65, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
button = Pin(64, Pin.IN, Pin.PULL_DOWN)
last_press_time = 0
button_last_state = 0
led_on = False
def set_led(on):
LED_R.value(LED_ON_LEVEL if on else LED_OFF_LEVEL)
try:
set_led(False)
while True:
os.exitpoint()
button_state = button.value()
current_time = time.ticks_ms()
if button_state == BUTTON_PRESS_LEVEL and button_last_state != BUTTON_PRESS_LEVEL:
if time.ticks_diff(current_time, last_press_time) > DEBOUNCE_MS:
led_on = not led_on
set_led(led_on)
last_press_time = current_time
button_last_state = button_state
time.sleep_ms(10)
except Exception as e:
print("程序退出:", e)
finally:
set_led(False)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
这个程序最重要的改进就是进行了按键消抖,确保每次按钮的状态变化都有效,避免由于机械抖动造成错误触发。
在这个程序中,我们设置按钮的消抖时间为 20ms,然后创建几个变量来辅助进行状态记录:last_press_time 用来记录上次按键按下的时间,led_on 用来记录 LED 的亮灭状态,button_last_state 用来记录上次检查按钮时的按键状态。
两块开发板的按键判断逻辑完全一致,差异仍然集中在红灯 GPIO、用户按键 GPIO 和 LED 点亮电平上。
九 常见问题
9.1 为什么 立创·庐山派Lite-K230D-CanMV开发板 复制立创·庐山派K230-CanMV开发板 代码后 LED 没反应?
因为两块板的板载 RGB 灯引脚不同,点亮电平也不同。立创·庐山派Lite-K230D-CanMV开发板需要使用 GPIO65、GPIO66、GPIO71,并且是高电平点亮。
9.2 为什么按键读取一直不对?
先确认自己使用的开发板型号。立创·庐山派K230-CanMV开发板的用户按键是 GPIO53,立创·庐山派Lite-K230D-CanMV开发板的用户按键是 GPIO64。两块板的按键都是下拉输入,按下为高电平。
9.3 为什么外接模块接到 立创·庐山派Lite-K230D-CanMV开发板 排针后没有反应?
立创·庐山派Lite-K230D-CanMV开发板的部分 40Pin IO 与摄像头复位脚、蜂鸣器、功放、风扇等板载功能存在复用关系。外接模块前,请先确认该 IO 当前没有被其他板载功能占用。