PIO 入门:手把手实现板载 LED 闪烁,原理与代码全解析

PIO 控制板载 LED 灯闪烁

以下代码可以在我们提供的资料包中 elegance-devkit v1\Demo\29 PIO_LED 文件夹找到。

我们首先使用 PIO 控制 Pico 的板载 LED 灯闪烁,示例代码如下:

Python 复制代码
# Python env   : MicroPython v1.23.0
# -*- coding: utf-8 -*-        
# @Time    : 2024/7/27 下午11:48   
# @Author  : 李清水            
# @File    : main.py       
# @Description : 通过PIO控制板载LED灯闪烁

# ======================================== 导入相关模块 ========================================

# 导入硬件相关的模块
from machine import Pin
# 导入时间相关的模块
import time
# 导入RP2040相关的模块
import rp2

# ======================================== 全局变量 ============================================

# ======================================== 功能函数 ============================================

# 定义一个 PIO 程序,将输出引脚初始化为低电平
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink() -> None:
    """
​    定义一个闪烁LED灯的PIO程序。

​    Args:
​        None

​    Returns:
​        None

​    Description:
​        该PIO程序通过设置引脚的高低电平来控制LED灯的闪烁。
​        高电平和低电平各持续160个时钟周期,总周期为320个时钟周期。
​    """
​    ​# 标记程序的起始位置
    wrap_target()
    # 将输出引脚设置为高电平,并延迟 31 个时钟周期
    set(pins, 1)   [31]
    # 以下nop指令无操作,并延迟 31 个时钟周期
    nop()          [31]
    nop()          [31]
    nop()          [31]
    nop()          [31]
    # 将输出引脚设置为低电平,并延迟 31 个时钟周期
    set(pins, 0)   [31]
    # 以下nop指令无操作,并延迟 31 个时钟周期
    nop()          [31]
    nop()          [31]
    nop()          [31]
    nop()          [31]
    # 程序跳转到wrap_target()处
    wrap()

# ======================================== 自定义类 ============================================

# ======================================== 初始化配置 ==========================================

# 上电延时3s
time.sleep(3)
# 打印调试信息
print("FreakStudio : Use PIO to control onboard LED")
# 创建状态机0,加载blink()程序,设置PIO执行频率为 系统时钟/2000 Hz,设置引脚为25号引脚
sm = rp2.StateMachine(0, blink, freq=2000, set_base=Pin(25))

# ========================================  主程序  ===========================================

# 启动状态机0
sm.active(1)
# 延时5s
time.sleep(5)
# 关闭状态机0
sm.active(0)

这里,我们主要进行以下操作:

  1. ​导入模块:​首先导入了必要的模块,包括 machine 用于处理硬件操作,time 用于处理时间相关的任务,以及 rp2 模块,它是 MicroPython 中用于处理 Raspberry Pi Pico RP2040 芯片的特定功能。
  2. ​功能函数:​ 定义了一个名为 blink 的函数,它是一个 PIO 程序,用于控制 LED 灯的闪烁。这个函数使用了 @rp2.asm_pio 装饰器来定义 PIO 程序,并设置了程序的初始状态、频率和引脚。
  3. ​初始化配置:​ 创建了一个名为 sm 的状态机对象,它加载了之前定义的 blink 函数,并将其频率设置为 2000Hz,引脚设置为 25 号引脚。
  4. ​主程序:​ 在主程序部分,首先启动状态机 sm,使其开始执行 blink 函数,从而控制 LED 灯闪烁。然后等待 5 秒,最后关闭状态机 sm,停止 LED 灯的闪烁。

其中,blink 函数是一个 PIO 程序,它被设计成在每次迭代时改变 LED 灯的状态。具体来说,它将输出引脚设置为高电平,保持一段时间(由 nop 指令实现),然后将引脚设置为低电平,再次保持一段时间。这个过程不断重复,形成了 LED 灯的闪烁效果。

状态机 sm 负责执行 blink 函数。当状态机被激活(sm.active(1))时,它会开始执行 blink 函数;当状态机被停用(sm.active(0))时,它会停止执行 blink 函数,频率的设置决定了 LED 灯闪烁的速度。

这里,PIO 程序的头尾为:

Python 复制代码
wrap_target()
...
wrap()  # 程序跳转到wrap_target()处

我们也可以使用 label 标签和 jmp 指令进行替代:

Python 复制代码
label('loop')
...
jmp('loop') # 程序跳转到loop处

二者区别在于 jmp 指令执行时需要用到 1 个时钟周期的处理时间,而 wrap 不会花费任何时间。

我们通过 set 指令设定引脚电平:

Python 复制代码
set(pins, 1)  # 高电平
set(pins, 0)  # 低电平

这里,pins 是 pioasm 会自动解读的一个名称,代表 set 指令要写入的引脚,这我们通过 rp2.StateMachine() 的 set_base 参数来指定 pins 的基引脚(pins 为多个引脚的元组,set_base 为 pins 首个元素​),也就是 GPIO25。

其中每个完整的闪烁周期由 320 个时钟周期组成(高电平和低电平各占 160 个时钟周期),而状态机的频率为 2000 Hz,因此每个周期耗时 0.16 秒,对应 LED 闪烁频率为 6.25 Hz:

  • set(pins, 1)​/​ **set(pins, 0)指令:**指令本身消耗 1 个时钟周期,延迟 31 个时钟周期,总共消耗 32 个时钟周期。
  • nop()​指令:​每个 nop() 消耗 1 个时钟周期,加上延迟 31 个时钟周期,每个 nop() 总共消耗 32 个时钟周期,4 个 nop()总共消耗 128 个时钟周期。
  • ​高电平部分:​ set(pins, 1) 4 个 nop() = 32 + 128 = 160 个时钟周期。
  • ​低电平部分:​ set(pins, 0) 4 个 nop() = 32 + 128 = 160 个时钟周期。

程序整体流程如下图所示:

烧录程序到 Pico 中,可以看到 Pico 板载的 LED 闪烁 5s 后停止闪烁:

我们将输出引脚修改为 GP28,输入指令如下:

python 复制代码
sm = rp2.StateMachine(0, blink, freq=2000, set_base=Pin(28))
sm.active(1)

这里,我们使用逻辑分析仪查看输出波形,可以看到波形频率确实为计算的 6.25Hz 左右:

相关推荐
Freak嵌入式9 小时前
一文搞懂树莓派 Pico 任务调度器:基于定时器的 MicroPython 多任务实现
嵌入式
花 满 楼1 天前
国芯科技CCFC3008PCS BIST (内建自测试) 分析总结
mcu·嵌入式·bist
虎王物联1 天前
ESP32 FreeRTOS多任务优先级翻转与互斥量保护实战
物联网·嵌入式·esp32·freertos
与你4032 天前
CLion 中为什么要重写 _write,而不是 fputc?
嵌入式
Freak嵌入式2 天前
MicroPython 开发避坑:用 Signal 类解决不同电路的电平兼容问题
嵌入式
智码看视界3 天前
Edge AI 全栈1:ESP32 传感器采集到云端大模型推理的完整链路
人工智能·物联网·嵌入式·esp32·aiot·全栈开发·edgeai
Freak嵌入式3 天前
告别硬件差异!MicroPython Signal 类:让你的 GPIO 代码跨板运行无压力
嵌入式
v_for_van3 天前
C语言__attribute__
服务器·c语言·开发语言·mcu·嵌入式·嵌入式实时数据库
xx~t4 天前
嵌入式——进程与线程1
linux·c语言·学习·嵌入式·进程与线程