使用arduino操作RP2040 PIO之简要笔记
前情提要:
RP2040是一个双核133MHz的ARM M0+微处理器,具有264k RAM,支持最大16M外置ROM。
最吸引人的一点是它还具有可编程IO,在较小的晶圆面积下做出了可编程的效果,使得通用性和DIY性更强。
PIO结构&必要知识介绍:
RP2040具有两个PIO块,每个PIO块有四个状态机。通过测试程序可知,如果不设置分频则状态机每步指令都可以运行在133MHz下,速度非常快。
每个PIO块具有32步程序空间,PIO内的四个状态机共享32步的程序空间。状态机初始化时需要指定程序的起始和结束地址。如果觉得空间不够用,也可以通过FIFO传输程序给状态机运行。
每个状态机用OSR和ISR寄存器对接了两个FIFO,分别是TX和RX,使用pull指令将会从TXFIFO中拉取数据到OSR寄存器,使用push则会把ISR寄存器中数据压入RXFIFO中。(这里的TX和RX是相对于CPU的)FIFO可以通过CPU或者DMA读写。
状态机对接IO使用了任意映射的方式,首先指定一个映射IO号,例如15,再指定映射数量例如5,则15-19这5个IO都会被映射到状态机。
状态机映射IO时有四种映射,包括IN、OUT、SET和SIDE SET指令对应的IO,它们四个可以映射到不同的IO。
状态机还可以发出和等待中断。

PIO可以执行如下九种指令:

其中Delay/side-set 这5位是很有用的。
sideset的作用:
Delay/side-set共用了5位的空间,当初始化状态机时需要设定SIDESET_COUNT和SIDE_EN,SIDESET_COUNT决定了sideset需要占用5位中最高的多少位,SIDE_EN决定了指令中sideset的最高位是否用于使能sideset操作。
例如sideset指令映射到了pin15。设初始化为sideset count=3,并且最高位用于使能。
则当某条指令的bit12=1,bit11=1,bit10=0,在执行这条指令时,状态机会同时设置io16=1(bit11) , io15=0(bit10)。如果bit12=0则状态机不操作IO。
这使得状态机可以在执行指令的同时操作IO,实现一步程序做两步的事情,减少空间占用,并且其指令频率保持不变。这意味着133M的时钟下PIO操作IO比CPU更快。还有一种情况是当需要编写SPI这种协议时,sideset可以使得在SDA移出的同时SCLK变化,达到产生精准时序的目的。
Delay的作用:
Delay占用的是这5位中的低几位,当设置Delay时,在状态机执行指令后会等待几个周期,这对于程序空间紧缺的状态机非常重要,可以实现一步程序同时做到延时,不需要浪费空间写延时函数(如果所需延时较短的话)。状态机也可以设置时钟分频来改变指令周期。
JMP指令:当满足Condition(条件)时,跳转到指定地址(0-31),条件可以是always,或者 !X,!Y,X--,Y--,X!=Y,input pin,OSR非空这几个。
OUT指令:将OSR寄存器移出指定位数到指定寄存器中,空位补零。bitcount决定了移出多少位,Destination决定要移出到哪里,可以是output pin或者X,Y等。
如果Destination是EXEC寄存器则下一步将立即执行移到EXEC中的指令。如果设定了OSR自动pull,则移出指令到EXEC后,OSR将从TXFIFO中自动拉取下一个数据,搭配DMA应当可以实现自动运行一系列程序,但每条指令要多花费一个周期来运行OUT。
MOV指令:MOV可以将一个寄存器中数据移到另一个里面,当目标寄存器是EXEC时就会执行指令。当目标是pin将使用out指令映射到的引脚,当来源寄存器是pin则将使用in指令映射到的引脚。MOV指令有两位op操作,可以将原寄存器进行补码或取反后再移动到目标寄存器。
如何将自己编写的汇编内容运行搭配PIO上:
对于Arduino及PlatformIO里面的pico开发板,库中已经包含有了pico-sdk,其中有一个pio.h文件,直接包含它即可使用操作PIO的基本函数。
对于其他C++编程的玩家,则需要自己移植一份pico-sdk。
这里我编写了个简单的PIO操作程序,封装了常用PIO操作,读者直接调用即可。
首先是myPIO_program.h
然后是myPIO_program.cpp
最后是main.cpp中的点灯测试代码(必须运行在有LED的PICO上):
编译、烧录,看到灯亮则说明PIO运行成功。
根据rp2040-datasheet中对9种指令的介绍,使用encoder.put_code()函数一个个放入程序指令即可,不能超过32步。
pio_pin_program_init函数是用来设定状态机初始化参数的,包括sideset,IO映射,OSR移位顺序和是否自动拉取等等。如果闲下来了可以单独讲一下。
随手小记,如有纰漏还请指正