PIO是什么?
(Programmable I/O)PIO是为RP2040设计的一个新硬件。它允许你在基于RP2040的设备上新创建或添加硬件。比如你可以“添加4个uart”,或“输出DPI”视频”,甚至“可能与某某宝上买的串口设备通讯,但找不到其他支持该硬件的设备”,这些都可以通过 PIO 解决。
背景
与其他数字硬件组件的接口是故障的。
PIO 硬件使用 PIO
RP2040 的 PIO非常适合你的 PIO 状态机写小型、简单的程序上,它在发生的频率非常高(由于需要传输的数据量),并且有非常合理的时间要求。RP20 在两个 PIO 实例中闹闹事件的状态机。在需要数据时注意时通过 IRQ 通知处理器。
这些程序运行周期精确最高选择轮询系统的速度(或程序可以划分给不出生的协议去慢慢的运行)。
PIO 状态机比 RP2040上的通用Cortex-M0+处理器要因为扩展。事实上,它们在尺寸上(因此成本)上与标准SPI外设相似,例如RP2040上发现了PL022 SPI,它们的周围也都花在了所有的事件外设共同的组件上,如fifo、移动设备和设备分频器。由于指令集小而规则,所以不需要使用硅来解密接口。没有必要将一个状态只用于机器。一个I/O任务而感到内忧,因为有8个状态机!
天线,当涉及到 I/O 时,PIO 状态机在一个周期内比 Cortex-M0+完成了更多的工作:例如,在一个周期内采样的 GPIO 值,切换信号并推入 FIFO。状态机远程运行通用软件。机器不能将看到的,写PIO状态机对于以前写过代码的人来说是非常了解的,对于没有写过小代码的人,说指令集应该很快快乐上手。
对于简单的硬件协议-如PWM或双工SPI-一个单一的PIO状态机可以自行处理硬件接口的任务。更复杂的协议如SDIO或DPI视频,你可能最终会使用两到三个。
不要试图在RP2040上“篡改”一个协议,而是使用PIO代替。对于重复从个GPIO读写数据的任何东西来说都是这样,但对于旨在传输数据的任何东西来说肯定都是这样。
开始使用PIO
可以在C ++ SDK中或直接从MicroPython中编写PIO程序。
第一个PIO程序
在深入了解PIO汇编语言的所有细节之前,我们应该花点时间看看一个小而完整的应用程序:
将程序加载到PIO的指向位置中
设置一个PIO状态机来运行程序
在状态机运行时与状态机进行交互
清单:
一个PIO程序
一些用C语言运行程序的软件,可以整个
一个 CMake 文件,描述如何将这接触组合成一个程序画面,加载到基于 rp2040 的开发板上
下面是一个PIO程序列表它是用PIO汇编语言编写的。
.program hello
; Repeatedly get one word of data from the TX FIFO, stalling when the FIFO is
; empty. Write the least significant bit to the OUT pin group.
loop:
pull
out pins, 1
jmp loop
pull指令从发送FIFO缓冲区取一个数据项,并把它放在输出移位寄存器(OSR)中。数据每次从FIFO移动一个字(32位)到OSR。OSR能够使用out指令将数据一次移出一个或多个位,移到更远的目的地。(tip: fifo是在硬件上实现的数据队列。每个状态机有两个fifo,在状态机和系统总线之间,用于数据从(TX)发送到(RX)芯片。)
out指令从我们刚刚从FIFO拉出的数据中取一位,并将数据写入一些引脚。我们稍后将看到如何确定这些是哪些引脚。
JMP指令返回到循环:标签,这样程序就会无限地重复所以,总结一下这个程序的功能:重复地从FIFO中取一个数据项,从这个数据项中取一个位,并把它写入一个引脚。
.pio文件还包含了一个帮助函数,用于设置PIO状态机,以便正确执行这个程序:
static inline void hello_program_init(PIO pio, uint sm, uint offset, uint pin) {
pio_sm_config c = hello_program_get_default_config(offset);
// Map the state machine‘s OUT pin group to one pin, namely the `pin`
// parameter to this function.
sm_config_set_out_pins(&c, pin, 1);
// Set this pin’s GPIO function (connect PIO to the pad)
pio_gpio_init(pio, pin);
// Set the pin direction to output at the PIO
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
// Load our configuration, and jump to the start of the program
pio_sm_init(pio, sm, offset, &c);
// Set the state machine running
pio_sm_set_enabled(pio, sm, true);
}
这里要设置的主要内容是我们能否将数据输出到的GPIO。这里有三件事需要考虑:
有四个不同的组,他们被不同的习惯在不同的情况下使用;这里我们用的是外机组,因为我们用的是外机。
GPIO还需要被告知PIO正在控制它(GPIO功能选择)
如果我们只使用输出驱动,我们需要确保 PIO 输出使能高。
PIO在被正确配置之前不会做任何事情,所以我们需要一些软件来做这些。好的,pio 相关程序文件、文件中包含的任何辅助函数以及程序的一些细节信息。
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include “pico/stdlib.h”
#include “hardware/pio.h”
// Our assembled program:
#include “hello.pio.h”
int main() {
// Choose which PIO instance to use (there are two instances)
PIO pio = pio0;
// Our assembled program needs to be loaded into this PIO‘s instruction
// memory. This SDK function will find a location (offset) in the
// instruction memory where there is enough space for our program. We need
// to remember this location!
uint offset = pio_add_program(pio, &hello_program);
// Find a free state machine on our chosen PIO (erroring if there are
// none)。 Configure it to run our program, and start it, using the
// helper function we included in our .pio file.
uint sm = pio_claim_unused_sm(pio, true);
hello_program_init(pio, sm, offset, PICO_DEFAULT_LED_PIN);
// The state machine is now running. Any value we push to its TX FIFO will
// appear on the LED pin.
while (true) {
// Blink
pio_sm_put_blocking(pio, sm, 1);
sleep_ms(500);
// Blonk
pio_sm_put_blocking(pio, sm, 0);
sleep_ms(500);
}
}
上面说到,RP2040有两个PIO,每个PIO块有四个状态机。每个PIO块有一个32槽的指令内存,对块中的4个状态机都是可以看出的。在任何状态运行程序之前,将程序加载到内存中。
指令有32条,这可能不是很多,但可以充分了解PIO指令集的特性,它非常密集。的pio/uart_tx示例说明。状态也有两种方式来自其他来源的指令——直接来自FIFOs——在RP2040数据表中可以阅读到有关这些指令的所有信息。
输入程序被加载,我们就找到一个运行我们的状态机并告诉它们的程序。我们可以命令多个状态机来运行一个。同样,可以指示每个状态机运行的不同的,它们可以同时实时动态内存。
上面的构建的状态机输出数据到PICO上的LED上,可以很明显的看到。此时
,状态机是自动运行的。状态机将立即停止,因为它正在等待TX FIFO中的数据,而我们处理器可以使用pio_sm_put_blocking()函数将数据直接推入状态机的TX FIFO。(_blocking,因为当TX这个FIFO满的时候,函数处理器停止。)写入1会打开LED,写入0会关闭LED
下面写cmake文件
现在有两个文本文件,名称以,pio和.c结尾,但它们并没有给我们带来好处。一个CMake描述了这些是如何构建文件一个二进制文件,适合加载到你的Raspberry Pi Pico或其他基于rp2040的板。
add_executable(hello_pio)
pico_generate_pio_header(hello_pio ${CMAKE_CURRENT_LIST_DIR}/hello.pio)
target_sources(hello_pio PRIVATE hello.c)
target_link_libraries(hello_pio PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(hello_pio)
# add url via pico_set_program_url
example_auto_set_url(hello_pio)
add_executable():声明我们正在构建一个名为hello_pio的程序
pico_generate_pio_header():声明我们有一个PIO程序,hello.pio,我们希望将其构建成C头文件中与我们的程序一起使用
target_sources():过去代码hello_pio程序的源文件。在本示例中,只有一个C文件。
target_link_libraries():确保我们的是使用PIO硬件API程序构建的,这样我们就可以调用类似的函数在C文件中的pio_add_program()。
pico_add_extra_outputs():默认情况下,我们只获得一个.elf文件作为我们的应用程序的构建输出。 在这里,我们声明我们也需要额外的构建格式,比如一个.uf2文件,可以直接拖到USB连接的树莓派Pico上。
然后通过以下命令来构建程序
mkdir build
cd build
cmake 。。
make