从数据手册出发:RP2040 PIO 指令集详解

PIO 指令集

PIO 指令集共包含 9 条指令,每条指令均为 16 位长度,按功能分为 3 个固定字段(对应指令编码表):

  • 指令编码段(Bit15~13)​:3 位,用于区分不同指令(如 JMP 对应 000、WAIT 对应 001);
  • 侧集 / 延时段(Bit12~8)​:5 位,存储侧集操作值和延时周期数,具体分配规则依赖状态机的 SIDESET_COUNT 配置(最多 5 位,其中 SIDESET_COUNT 位用于侧集,剩余位用于延时);
  • 指令参数段(Bit7~0)​:8 位,存储指令的条件、地址、源 / 目标、位数等具体参数。

1280X1280.PNG

所有 ​PIO​ ​指令​​的执行时间都是一个时钟周期

5 位长的 Delay/side-set 字段的含义依赖于状态机的 SIDESET_COUNT 配置:

  • 最多 5 个 LSB 位长(位长为 5 - SIDESET_COUNT)为当前指令和下一条指令之间插入的 Delay 延迟周期数
  • 最多 5 个 MSB 位长(位长为 SIDESET_COUNT)为 side-set 侧集操作,可以在该指令执行的同时,将某些 GPIO 管脚设置为某个常量

这里,延时操作可以在我们需要精确定时和或信号同步时应用,而 side-set 侧集操作允许我们在执行指令同时额外设置引脚。这对于时钟和使能等控制信号很有用,side-set 侧集操作用于控制引脚,而 OUT、MOV等指令用于处理数据。

下面对每个指令的具体含义进行介绍。

1.JMP 跳转指令

JMP 跳转指令用于根据条件判断是否执行指令跳转,改变当前程序的顺序执行流。

1280X1280 (1).PNG

根据 Condition 条件判断是否修改程序计数器(PC):若条件为真,将 PC 设置为 Address 对应的指令地址;否则保持 PC 自增。无论条件是否满足,指令均消耗 1 个时钟周期,延时在条件求值后执行。

JMP 指令执行时无论 Condition 是否为真都会消耗一个时钟周期。Delay 延迟在 Condition 被求值、程序计数器被更新后发生。

参数含义如下:

  • Condition:Condition 是一个 3 位的二进制值(对应 000~111),决定了 “什么时候才跳转”。你可以把它理解成 “跳不跳的开关”,每个开关对应不同的判断规则;
  • Address:Address 是一个 5 位的二进制值,对应 0~31 的整数,表示目标指令在 PIO 指令内存中的绝对地址(可以理解成 “指令的编号”)。

Condition 取值和含义如下:
ScreenShot_2026-06-08_120515_833.png

Address 取值和含义如下:

  • PIO 的指令内存最多能存 32 条指令,所以地址只能是 0(第一条指令)到 31(最后一条指令);
  • 你不用手动算这个地址,写程序时用标签(比如 loop)代替就行,SDK 会自动把标签转换成绝对地址(新手不用管转换过程,记住用标签就行)。

汇编语法为:jmp (<cond>) <target>

其中:

  • <cond> 是上节列出的可选的条件。如果未指定条件,则总是跳转。
  • <target> 是一个程序标签或值,表示程序内部的指令偏移量(第一条指令的偏移量为 0)。
    • 注意,由于 PIO JMP 指令使用 PIO 指令内存之内的绝对地址,JMP 需要在运行时根据程序的加载偏移量进行调整
    • 这一步在加载程序时由 SDK 负责,但在编码 JMP 指令供 OUT EXEC 使用时需要留意这一点。

2.WAIT 延时指令

WAIT 延迟指令用于等待各种事件,例如引脚状态更改或中断发生。
1280X1280 (2).PNG

WAIT 延迟指令持续等待 Source 指定的事件满足 Pol 极性条件,条件满足前状态机暂停执行;Delay 延时在等待完成后执行。

参数含义如下:

  • Polarity:Polarity 是一个 1 位的二进制值(只有 0 或 1),决定了 “等待的事件要处于什么状态才满足条件”,可以理解成 “等的是 0 还是 1”。
  • Source:Source 是一个 2 位的二进制值(00~11),决定了 “我们要等什么类型的事件”,这是 WAIT 指令的核心选择项。
  • Index:Index 是一个 5 位的二进制值,对应 0~31 的整数,表示 “在选定的事件类型中,具体等哪一个对象”。

Polarity 可选值如下所示:

ScreenShot_2026-06-08_120540_052.png

Source 可选值如下所示:

ScreenShot_2026-06-08_120555_105.png

Index 可选值如下所示:

ScreenShot_2026-06-08_120606_924.png

汇编语法为:

  • wait <polarity> gpio <gpio_num>
  • wait <polarity> pin <pin_num>
  • wait <polarity> irq <irq_num> (rel)

其中:

  • <polarity> 是一个值,指定极性(0 或 1);
  • <pin_num> 是一个值,指定输入管脚的编号(在状态机输入管脚映射中的编号);
  • <gpio_num> 是一个值,指定实际的 GPIO 管脚编号;
  • <irq_num> (rel) 是一个值,指定要等待的 IRQ 编号(0 ~ 7)。如果设置了 rel,则实际的 IRQ 编号的计算方式为,将 IRQ 编号的最低两个数据位(irq_num(10))替换成和的最低两个数据位(irq_num(10) + sm_num(10)),其中 sm_num(10) 为状态机编号;

3.IN 数据读出指令

IN 数据读出指令用于从引脚输入或 X 或 Y 暂存寄存器等数据输入源将数据转移到 ISR 中。

1280X1280 (3).PNG

Source 移位 Bit count 个数据位(最大到 32)到输入移位寄存器(ISR)中。移位方向由各个状态机的 SHIFTCTRL_IN_SHIFTDIR 决定。

参数含义如下:

  • Source::数据取出的源地址,Source 是 3 位二进制值,对应不同的数据源;
  • Bit count:Bit count 是 5 位二进制值,对应要从数据源取的位数,规则很简单:
    • 取值范围:1~32 位(比如拿 1 位、8 位、32 位);
    • 特殊编码:32 位对应的二进制是 00000(因为 5 位最多表示 31,所以用 0 代表 32)。

Source 可选值如下所示

ScreenShot_2026-06-08_120625_323.png

如果启用了自动推出(autopush),可以把 autopush 理解成 ISR 的 “自动存盘” 功能:

  • 先给 ISR 设一个 “存盘阈值”(SHIFTCTRL_PUSH_THRES,比如 8 位);
  • 每次执行 IN 指令,ISR 里的数据位数会累加,当达到阈值时,ISR 会自动把数据 “推” 到 RX FIFO(给 CPU/DMA 读);
  • 推完后 ISR 会清空为 0,同时暂停接收数据(直到 FIFO 有空位);
  • 如果 FIFO 满了,状态机会 “等”(暂停执行),直到 FIFO 有空位。

IN 永远使用源数据的最低 Bit count 个数据位。例如,如果 PINCTRL_IN_BASE 设置为 5(状态机输入映射从 GPIO5 开始),那么指令 IN 3, PINS 会读取管脚 5、6 和 7(不是最高位),并将这些值移入 ISR;首先,ISR 会左移或右移,为新的输入数据腾出空间,然后将输入数据复制到腾出的空间中。需要注意的是,输入数据的数据位顺序与移位方向无关。

汇编语法为:in <source>, <bit_count>

其中:

  • <source> 是上述源之一;
  • <bit_count> 是一个值,指定了要移位的数据位数(有效值为 1 ~ 32)。

4.OUT 数据写入指令

OUT 数据写入指令用于将数据位从 OSR 移至数据目的地,例如引脚输入或 X 或 Y 暂存寄存器。

1280X1280 (4).PNG

Bit count 个数据位移出输入移位寄存器(OSR),并写入 Destination。此外,还会增加输出移位计数器 Bit count,直到最大值 32。

参数含义如下:

  • Destination:数据写入的目的地址,Destination 是 3 位二进制值(000~111),决定了数据的最终去向。

  • Bit count:Bit count 是 5 位二进制值,规则和 IN 指令完全一致:

    • 取值范围:1~32 位(比如移出 1 位、8 位、32 位)。
    • 特殊编码:32 位对应的二进制是 00000(因为 5 位最多表示 31,所以用 0 代表 32)。

    Destination 可选值如下所示:

    ScreenShot_2026-06-08_120645_555.png

将一个 32 位值写入 Destination:写入目的地的 32 位值,默认取 OSR 的低 Bit count 位,右移位时取高 Bit count 位,其余位为 0

如果启用自动加载(autopull),autopull 理解成 OSR 的 “自动补货” 功能,和 IN 指令的 autopush 是一对互补的机制:

  • ​设置阈值:​先给 OSR 设一个 “补货阈值”(SHIFTCTRL_PULL_THRESH,比如 8 位)。
  • ​自动补货:​每次执行 OUT 指令,输出移位计数器会累加,当达到阈值时,OSR 会自动从 TX FIFO(CPU/DMA 给 PIO 发数据的缓冲区)加载新的 32 位数据,同时输出移位计数器清零。
  • ​等待机制:​如果 TX FIFO 为空(没数据可补),状态机会暂停执行(直到 TX FIFO 有数据),但 OUT 指令的执行时间仍然是 1 个时钟周期。

汇编语法为:out <destination>, <bit_count>

其中:

  • <destination> 是上述数据目的地之一
  • <bit_count> 是一个值,指定要移位的数据位数(有效值为 1 ~ 32)

5.PUSH 数据推出指令

PUSH 数据推出指令用于将 ISR 的 32 位数据推入 RX FIFO,同时清空 ISR,执行时间固定为 1 个时钟周期。

1280X1280 (5).PNG

将 ISR 的内容作为一个 32 位字推入到 RX FIFO。同时将 ISR 清除为全零。

参数含义如下:

  • IfFull:IfFull 是 1 位二进制值(0 或 1),决定 “是否要等 ISR 里的数据位数达到阈值(SHIFTCTRL_PUSH_THRESH)才推数据”;
  • Block:Block 是 1 位二进制值(0 或 1),决定 “RX FIFO 满了时,是暂停等还是直接扔数据”。

IfFull 的含义和参数如下:

ScreenShot_2026-06-08_120702_217.png

Block 的含义和参数如下:

ScreenShot_2026-06-08_120712_939.png

汇编语法为:

  • push (iffull)

  • push (iffull) block

  • push (iffull) nonblock

    其中:

  • iffull 相当于上述 IfFull == 1,也就是说,不指定的情况下,默认值为 IfFull == 0

  • block 相当于上述 Block == 1,如果不指定 blocknoblock,则此值为默认值;

  • noblock 相当于上述 Block == 0

    ScreenShot_2026-06-08_120723_576.png

6.PULL 数据加载指令

PULL 数据加载指令用于从 TX FIFO 取 1 个 32 位数据,加载到 OSR 中,同时清空 OSR,执行时间固定为 1 个时钟周期。

1280X1280 (6).PNG

从 TX FIFO 加载一个 32 位字到 OSR 中。

参数含义如下:

  • IfEmpty:IfEmpty 用于控制 PULL 指令的执行时机,仅当输出移位计数器达到配置阈值(SHIFTCTRL_PULL_THRESH,与自动加载 autopull 的阈值一致)时,才执行数据加载操作;
  • Block:Block 用于定义 TX FIFO 为空时的状态机行为,决定是否暂停执行以等待数据。

IfEmpty 的含义和参数如下:

ScreenShot_2026-06-08_120740_074.png

Block 的含义和参数如下:

ScreenShot_2026-06-08_120749_897.png

注意,当启用自动加载时,在 OSR 满的情况下(OSR 中仍存储有未被 OUT ​指令移出的完整 32 位数据)任何 PULL 指令都是误操作,会直接将 TX FIFO 的新数据覆盖到 OSR 中,导致 OSR 中原有的未输出数据被丢失,这种操作属于硬件层面的误操作,会破坏数据输出的连续性和完整性。

PULL 指令的 “屏障” 特性,本质是利用其​执行时机的可控性​(通过 ifempty 参数)来阻止误操作:

  • 当配置 pull ifempty 时,PULL 指令仅会在输出移位计数器达到阈值(OSR 中的数据已移出大部分,接近空)时执行;
  • 这相当于在 OSR “满” 和 “空” 之间建立了一道 “屏障”,确保只有 OSR 数据已充分输出后,才会触发手动 PULL 操作,避免了 OSR 满时的误加载。

汇编语法为:

  • pull (ifempty)

  • pull (ifempty) block

  • pull (ifempty) noblock

    其中:

  • ifempty 相当于上述 IfEmpty == 1,即如果没有指定,则默认值为 IfEmpty == 0

  • block 相当于上述 Block == 1。如果不指定 blocknoblock,则此值为默认值

  • noblock 相当于上述 Block == 0

ScreenShot_2026-06-08_120801_571.png

7.MOV 数据移动指令

MOV 数据移动指令用于将数据从源地址移动到目的地址。可以使用寄存器和引脚作为数据移动的源地址、目标地址以及几个具有特殊功能的位置。

1280X1280 (7).PNG

Source 复制数据到 Destination

参数含义如下:

  • DestinationDestination 为 3 位配置位,指定数据最终写入的硬件单元,部分目标具有特殊功能;
  • OperationOperation 为 2 位配置位,用于对源数据进行预处理后再复制;
  • SourceSource 为 3 位配置位,指定数据的来源,其中 STATUS 为特殊状态源。

Destination 的含义和参数如下:

ScreenShot_2026-06-08_120817_769.png

Operation 的含义和参数如下:

ScreenShot_2026-06-08_120829_658.png

Source 的含义和参数如下:

ScreenShot_2026-06-08_120839_329.png

汇编语法为:mov <destination>, (op) <source>

其中:

  • <destination> 是上述数据移动目的地之一
  • <op> 如果存在的话,为下列值之一:
    • !~ 表示 NOT
    • :: 表示反转数据位顺序
  • <source> 是上述源之一

8.IRQ 中断写入或清除指令

IRQ 中断写入或清除指令用于引发一个中断,或对触发的中断进行清除。

1280X1280 (8).PNG

参数含义如下:

  • ClearClear 为 1 位配置位,决定对 IRQ 标志位执行 “清除” 还是 “设置” 操作,具有最高优先级(若 Clear=1,则忽略 Wait 参数);
  • WaitWait 为 1 位配置位,仅在 Clear=0(设置 IRQ)时生效,决定是否等待标志位被清除;
  • IndexIndex 为 5 位配置位,其中最低 3 位(LSB)指定 IRQ 索引(0~7),最高位(MSB)控制是否启用相对寻址(模 4 运算):
    • 基础索引​:最低 3 位直接对应 IRQ 索引 0~7,指定操作的 IRQ 标志位;
    • 相对寻址(MSB=1)​:将状态机 ID(0~3)与 IRQ 索引的最低两位相加后取模 4,替换原索引的最低两位,第三个 LSB 位保持不变。

ScreenShot_2026-06-08_120858_369.png

Clear 参数含义如下:

ScreenShot_2026-06-08_120910_571.png

Wait 参数含义如下:

ScreenShot_2026-06-08_120919_185.png

汇编语法如下:

irq <op> <irq_num> (_rel)

其中:

  • <op>:可选操作标识,决定指令行为(设置 / 清除 / 等待);

  • <irq_num>:IRQ 编号(0~7),对应 Index 的最低 3 位;

  • _rel:可选,启用相对寻址(对应 Index 的 MSB=1),实际 IRQ 编号按 “(irq_num 最低两位 + 状态机 ID) % 4” 计算(第三个 LSB 位不变)。

    ScreenShot_2026-06-08_120929_929.png

常见用法如下:

ScreenShot_2026-06-08_120941_168.png

可以在 INTR 寄存器中查看原始中断输出状态:

ScreenShot_2026-06-08_123345_882.png

可以在 INTS 寄存器查看掩码中断输出状态:

ScreenShot_2026-06-08_123358_139.png

ScreenShot_2026-06-08_123410_916.png

9.SET 设置映射 GPIO 指令

SET 设置映射 GPIO 指令用于将数据写入到 GPIO 引脚(即控制高低电平),也可以将数据写入到 X\Y 寄存器中。

1280X1280 (9).PNG

将立即数 Data 写入到 Destination

参数含义如下:

  • Destination:Destination 为 3 位配置位,指定 5 位立即数的写入对象;
  • Data: Data 为 5 位配置位,是直接写入目标的固定值,​取值范围为 0~31​(5 位二进制的最大值):
    • 若目标为 PINS/PINDIRS:Data 的每一位对应 SET 管脚映射中的一个 GPIO,位值 1/0 对应 GPIO 的高 / 低电平(PINS)或输出 / 输入方向(PINDIRS);
    • 若目标为 X/Y:Data 直接作为寄存器的低 5 位,高 27 位清 0,因此 X/Y 通过 SET 可初始化的最大值为 31(适合短周期循环计数)。

该指令用于设置控制信号,如时钟或芯片选择,或初始化循环计数器。由于 Data 只有 5 个数据位,因此可擦写寄存器可以 SET 的值为 0 ~ 31,最大可循环 32 次。

SETOUT 的管脚映射是独立配置的。它们可以映射到不同的位置,例如,一个管脚作为时钟信号使用,另一个作为数据使用。它们的管脚范围也可以重叠: UART 传输程序可以使用 SET 来设置开始和终止数据位,用 OUT 指令向同一个管脚上移出 FIFO 数据。

汇编语法为:set <destination>, <value>

其中:

  • <destination> 为上述数据操作目的地;
  • <value> 是要设置的值,有效值为 0 ~ 31。
posted @ 2026-09-01 14:54  FreakStudio  阅读(5)  评论(0)    收藏  举报