对rp2040使用pio和dma联合传输数据的记录

本文介绍了一个基于PIO的逻辑分析器示例,它能实时捕获1到32个引脚的样本,在触发条件满足时,通过DMA高效传输至缓冲区。示例展示了如何配置硬件、初始化分析器以及捕获和显示数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


// PIO logic analyser example
//
// This program captures samples from a group of pins, at a fixed rate, once a
// trigger condition is detected (level condition on one pin). The samples are
// transferred to a capture buffer using the system DMA.
//
// 1 to 32 pins can be captured, at a sample rate no greater than system clock
// frequency.

#include <stdio.h>
#include <stdlib.h>

#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hardware/structs/bus_ctrl.h"

// Some logic to analyse:
#include "hardware/structs/pwm.h"

const uint CAPTURE_PIN_BASE = 16;
const uint CAPTURE_PIN_COUNT = 2;
const uint CAPTURE_N_SAMPLES = 96;

static inline uint bits_packed_per_word(uint pin_count) {
    // If the number of pins to be sampled divides the shift register size, we
    // can use the full SR and FIFO width, and push when the input shift count
    // exactly reaches 32. If not, we have to push earlier, so we use the FIFO
    // a little less efficiently.
    const uint SHIFT_REG_WIDTH = 32;
    return SHIFT_REG_WIDTH - (SHIFT_REG_WIDTH % pin_count);
}

void logic_analyser_init(PIO pio, uint sm, uint pin_base, uint pin_count, float div) {
    // Load a program to capture n pins. This is just a single `in pins, n`
    // instruction with a wrap.
    uint16_t capture_prog_instr = pio_encode_in(pio_pins, pin_count);
    struct pio_program capture_prog = {
            .instructions = &capture_prog_instr,
            .length = 1,
            .origin = -1
    };
    uint offset = pio_add_program(pio, &capture_prog);

    // Configure state machine to loop over this `in` instruction forever,
    // with autopush enabled.
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_in_pins(&c, pin_base);
    sm_config_set_wrap(&c, offset, offset);
    sm_config_set_clkdiv(&c, div);
    // Note that we may push at a < 32 bit threshold if pin_count does not
    // divide 32. We are using shift-to-right, so the sample data ends up
    // left-justified in the FIFO in this case, with some zeroes at the LSBs.
    sm_config_set_in_shift(&c, true, true, bits_packed_per_word(pin_count));
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
    pio_sm_init(pio, sm, offset, &c);
}

void logic_analyser_arm(PIO pio, uint sm, uint dma_chan, uint32_t *capture_buf, size_t capture_size_words,
                        uint trigger_pin, bool trigger_level) {
    pio_sm_set_enabled(pio, sm, false);
    // Need to clear _input shift counter_, as well as FIFO, because there may be
    // partial ISR contents left over from a previous run. sm_restart does this.
    pio_sm_clear_fifos(pio, sm);
    pio_sm_restart(pio, sm);

    dma_channel_config c = dma_channel_get_default_config(dma_chan);
    channel_config_set_read_increment(&c, false);
    channel_config_set_write_increment(&c, true);
    channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false));

    dma_channel_configure(dma_chan, &c,
        capture_buf,        // Destination pointer
        &pio->rxf[sm],      // Source pointer
        capture_size_words, // Number of transfers
        true                // Start immediately
    );

    pio_sm_exec(pio, sm, pio_encode_wait_gpio(trigger_level, trigger_pin));
    pio_sm_set_enabled(pio, sm, true);
}

void print_capture_buf(const uint32_t *buf, uint pin_base, uint pin_count, uint32_t n_samples) {
    // Display the capture buffer in text form, like this:
    // 00: __--__--__--__--__--__--
    // 01: ____----____----____----
    printf("Capture:\n");
    // Each FIFO record may be only partially filled with bits, depending on
    // whether pin_count is a factor of 32.
    uint record_size_bits = bits_packed_per_word(pin_count);
    for (int pin = 0; pin < pin_count; ++pin) {
        printf("%02d: ", pin + pin_base);
        for (int sample = 0; sample < n_samples; ++sample) {
            uint bit_index = pin + sample * pin_count;
            uint word_index = bit_index / record_size_bits;
            // Data is left-justified in each FIFO entry, hence the (32 - record_size_bits) offset
            uint word_mask = 1u << (bit_index % record_size_bits + 32 - record_size_bits);
            printf(buf[word_index] & word_mask ? "-" : "_");
        }
        printf("\n");
    }
}

int main() {
    stdio_init_all();
    printf("PIO logic analyser example\n");

    // We're going to capture into a u32 buffer, for best DMA efficiency. Need
    // to be careful of rounding in case the number of pins being sampled
    // isn't a power of 2.
    uint total_sample_bits = CAPTURE_N_SAMPLES * CAPTURE_PIN_COUNT;
    total_sample_bits += bits_packed_per_word(CAPTURE_PIN_COUNT) - 1;
    uint buf_size_words = total_sample_bits / bits_packed_per_word(CAPTURE_PIN_COUNT);
    uint32_t *capture_buf = malloc(buf_size_words * sizeof(uint32_t));
    hard_assert(capture_buf);

    // Grant high bus priority to the DMA, so it can shove the processors out
    // of the way. This should only be needed if you are pushing things up to
    // >16bits/clk here, i.e. if you need to saturate the bus completely.
    bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;

    PIO pio = pio0;
    uint sm = 0;
    uint dma_chan = 0;

    logic_analyser_init(pio, sm, CAPTURE_PIN_BASE, CAPTURE_PIN_COUNT, 1.f);

    printf("Arming trigger\n");
    logic_analyser_arm(pio, sm, dma_chan, capture_buf, buf_size_words, CAPTURE_PIN_BASE, true);

    printf("Starting PWM example\n");
    // PWM example: -----------------------------------------------------------
    gpio_set_function(CAPTURE_PIN_BASE, GPIO_FUNC_PWM);
    gpio_set_function(CAPTURE_PIN_BASE + 1, GPIO_FUNC_PWM);
    // Topmost value of 3: count from 0 to 3 and then wrap, so period is 4 cycles
    pwm_hw->slice[0].top = 3;
    // Divide frequency by two to slow things down a little
    pwm_hw->slice[0].div = 4 << PWM_CH0_DIV_INT_LSB;
    // Set channel A to be high for 1 cycle each period (duty cycle 1/4) and
    // channel B for 3 cycles (duty cycle 3/4)
    pwm_hw->slice[0].cc =
            (1 << PWM_CH0_CC_A_LSB) |
            (3 << PWM_CH0_CC_B_LSB);
    // Enable this PWM slice
    pwm_hw->slice[0].csr = PWM_CH0_CSR_EN_BITS;
    // ------------------------------------------------------------------------

    // The logic analyser should have started capturing as soon as it saw the
    // first transition. Wait until the last sample comes in from the DMA.
    dma_channel_wait_for_finish_blocking(dma_chan);

    print_capture_buf(capture_buf, CAPTURE_PIN_BASE, CAPTURE_PIN_COUNT, CAPTURE_N_SAMPLES);
}

这段代码是一个示例,用于演示如何使用PIO(Programmable Input/Output)逻辑分析器来捕获一组引脚上的样本。它会在检测到触发条件(即某个引脚上的电平条件)后,以固定的速率从引脚捕获样本。捕获的样本通过系统的DMA(Direct Memory Access,直接内存访问)传输到捕获缓冲区。

以下是对代码和注释的中文解释:

  • PIO逻辑分析器示例
    • 这是一个PIO逻辑分析器的使用示例。
  • 程序功能
    • 该程序能够捕获一组引脚上的样本。
    • 捕获的速率是固定的,并且是在触发条件被检测到之后开始的。
    • 触发条件是一个引脚上的电平条件。
    • 捕获的样本通过系统的DMA传输到捕获缓冲区。
  • 引脚数量与采样率
    • 可以捕获的引脚数量从1到32个不等。
    • 采样率不能大于系统时钟频率。

从代码中可以看出,这个逻辑分析器的主要目的是对树莓派上的某些引脚进行实时监控,当满足特定条件(如某个引脚的电平变化)时,以一定的速率采集这些引脚的状态,并将数据保存到内存中。

typedef pio_hw_t *PIO;

在C或C++编程中,typedef 是一个关键字,用于为已有的数据类型创建一个新的名称或别名。这通常用于使代码更具可读性,或者为了简化复杂数据类型的声明。

在给定的代码片段 typedef pio_hw_t *PIO; 中:

  • pio_hw_t 是一个已经定义好的数据类型,它可能是一个结构体、联合体或者其他任何数据类型。
  • *PIO 是一个指向 pio_hw_t 类型数据的指针。

通过使用 typedef,我们为 pio_hw_t 类型的指针创建了一个新的别名 PIO。这意味着,在后续的代码中,每当你想声明一个指向 pio_hw_t 的指针时,你可以直接使用 PIO 而不是 pio_hw_t *

例如,没有 typedef 的情况下,你可能会这样声明一个指针:

pio_hw_t *my_pointer;

但是,使用 typedef 之后,你可以这样声明:

PIO my_pointer;

这样做的好处是,如果你的 pio_hw_t 类型在未来发生变化(例如,你决定使用不同的数据类型),你只需修改 typedef 语句,而不需要修改所有使用该指针类型的代码。此外,使用 PIO 这样的别名也使得代码更易于阅读和维护,因为它可能表示某种特定的硬件接口或功能。

基于RP2040(Arduino框架)运行频率200Mhz,实现以50Msps速率驱动AD9220读取一定数量数据。 IO13作为按钮btn1设计为低电平触发,使用内部上拉,在主程序里运行,并软件消抖,触发一次,就启动sm0发射单个100ns宽度低电平脉冲,然后启动sm1读取一定数据,等待下一次按钮触发。 设计两个pio状态机sm0,sm1,分频均设置为1。IO14负责发射单个100ns宽度的低电平,100ns宽度使用pio指令内置延时[]功能,然后IO14恢复高电平,由状态机sm0负责驱动,状态机sm0中增加同步标记,发射100ns低电平后,立刻启动状态机sm1开始读取数据。 状态机sm1驱动IO15作为pclk输出到ad9220工作时钟,rp2040读取12位数据读取脚是io0~IO11, sm1在IO15输出50mhz时钟pclk,涉及延时的使用pio内置延时 [],ad9220在pclk低电平开始自行采样,在上升沿高锁存数据,使用测设置功能,在pclk高电平期间状态机sm1并行读取io0~IO11的12位数据,并存入一个buff数组。当一定数量(20000)数据被读取后,停止sm1 ,使用proce_data()函数处理。IO16负责LED指示灯使用反馈触发状态。请确认各个io口功能使用,请进行dma优化,分别给出pio代码arduino代码,复核pio代码,特别是在pclk高电平期间是否读取12位数据,符合功能要求。 并扩展优化: 添加双缓冲机制实现连续采样 使用PIO IRQ实现精确同步 实现数据校验机制 添加tinyUSB数据传输接口
最新发布
03-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星海露水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值