树莓派 pico pio 可编程io

3.2. 开始使用PIO

3.2.1. 第一个PIO程序

在了解pio汇编语言的所有细节之前,我们需要花点时间看一下这件简短但是完整的应用过程:

​ 1、加载程序到pio的指令内存

​ 2、设置pio状态机运行程序

​ 3、当状态机运行起来之后,与他进行互动

这些操作主要需要已下几个内容:

  • 一个 PIO 程序
  • 一些 c 程序
  • cmake file描述文件,描述这两个部分如何组成一个程序镜像然后加载到RP2040开发板

3.2.1.1 PIO 编程

第一个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),每次会移动32位数据从缓冲到输出移位寄存器。输出移位寄存器可以一次移出多个数据,使用out指令可以实现这个功能

FIFOs?

FIFO 是一个数据缓冲区,每个状态机有2个FIFO,在状态机和系统总线之间,用于数据输入输出到芯片

out 指令从我们刚刚从fifo获取到输出移位寄存器的数据中取出来一个bit,把这个bit的数据写入到某个pin,控制该pin的电平。后面会接收如何配置对应的pin

jmp指令返回到loop:标记处,程序马上从新开始执行。故这个程序的功能就是循环从FIFO中获取数据,然后取一个bit输出到引脚

.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`
    // 配置使用那个pin作为输出功能
    sm_config_set_out_pins(&c, pin, 1);
    // 设置这个gpio作pio功能
    pio_gpio_init(pio, pin);
    // 设置pin的方向时输出方向
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

    // 加载配置,然后跳转到程序开始的地方
    pio_sm_init(pio, sm, offset, &c);
    // 开始运行状态机
    pio_sm_set_enabled(pio, sm, true);
} 

这里的主要工作就是设置我们要输出数据的引脚。需要完成以下三件事情:

​ 1、状态机需要指导使用那个gpio作输出功能。这里有四种不同的引脚组,对应不同的指令和不同的情况使用。 现在我们使用输出引脚组,因为我我们只使用了out指令。

​ 2、GPIO 需要被告知 PIO 正在控制它(选择复用功能)。

​ 3、如果我们只使用一个pin作输出功能,我们需要确保 PIO 配置了引脚的输出方向使能。pio可以控制引脚输入或者输出方向。例如使用 out pindirs指令,这里我们在启动程序之前进行设置

3.2.1.2. c语言程序

PIO 不会做任何事情直到被配置完成,所以我们需要一些软件去做这个事情。上面编辑的PIO文件,会自动转换为一个头文件,头文件中会包含二进制格式的汇编程序,以及一些辅助函数。c程序中需要包含这个文件 hello.pio.h

/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "pico/stdlib.h"
#include "hardware/pio.h"
// Our assembled program:
#include "hello.pio.h"

int main() {
    #ifndef PICO_DEFAULT_LED_PIN
    #warning pio/hello_pio example requires a board with a regular LED
    #else
    // 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);
    }
#endif
}

可以你已经了解到RP2040包含2个PIO 单元,每个pio单元有四个状态机。每个PIO单元有32条指令空间可以被四个状态机共享。我们需要在状态机运行起来之前把指令加载到指令存储空间。 pio_add_programe()这个程序寻找空闲的指令内存空间,然后加载过去。

32 条指令

这个可能听上去有点少,但是pio指令集可以非常高效一旦你了解了他的全部特性。一个串口程序可以使用四条指令完成,如pio/uart_tx例子。这里同样有一系列方法让状态机从其他来源执行指令,比如可以直接从FIFO中获取,可以查询RP2040 Datasheet。

一旦程序被加载,我们会寻找一个空闲的状态机并且告诉他运行我们的程序。我们还可以控制多个状态机运行同一个程序,也可以不同状态机运行不同的程序。

在这个程序中状态机会立即停下来,因为他会等待TX_FIFO中的数据,但是我们没有提供。处理器可以使用pio_sm_put_blocking()函数推送数据到TX_FIFO(blocking 是因为如果TX FIFO满了,该指令会阻塞等待),写1会让等亮,写0会让灯灭掉。

3.2.1.3 CMake 文件

我们有2种喜爱的文件在我们的电脑上,分别是.c .pio文件,一个cmake file文件描述如何构建一个二进制镜像。

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 hardware api构建,例如调用 pio_add_programe()`程序
  • pico_add_extra_outputs()默认我们会获取一个elf文件作为输出的app,这里我们声明一个额外的编译格式,比如uf2可以通过usb拖拽文件实现下载文件

确保程序的SDK安装完成之后,运行下面的命令可以编译程序

mkdir build
cd build
cmake ..
make hello_pio

文章截取自树莓派 RP2040 C/C++ 软件编程手册

  • 1
    点赞
  • 4
    收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 3
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值