From 475a726c0d80fa49a025d7fe7fc9fa2bb554a1d5 Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Mon, 8 Feb 2021 13:37:34 +0000 Subject: [PATCH] Fix logic analyser for non-power-of-2 sample widths, and clean and comment. --- pio/logic_analyser/logic_analyser.c | 45 ++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/pio/logic_analyser/logic_analyser.c b/pio/logic_analyser/logic_analyser.c index faf9809..a0d9db4 100644 --- a/pio/logic_analyser/logic_analyser.c +++ b/pio/logic_analyser/logic_analyser.c @@ -14,10 +14,12 @@ // frequency. #include +#include #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" @@ -26,6 +28,15 @@ 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. @@ -43,7 +54,10 @@ void logic_analyser_init(PIO pio, uint sm, uint pin_base, uint pin_count, float sm_config_set_in_pins(&c, pin_base); sm_config_set_wrap(&c, offset, offset); sm_config_set_clkdiv(&c, div); - sm_config_set_in_shift(&c, true, true, 32); + // 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); } @@ -74,12 +88,17 @@ void print_capture_buf(const uint32_t *buf, uint pin_base, uint pin_count, uint3 // 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; - bool level = !!(buf[bit_index / 32] & 1u << (bit_index % 32)); - printf(level ? "-" : "_"); + 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"); } @@ -89,7 +108,19 @@ int main() { stdio_init_all(); printf("PIO logic analyser example\n"); - uint32_t capture_buf[(CAPTURE_PIN_COUNT * CAPTURE_N_SAMPLES + 31) / 32]; + // 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; @@ -98,9 +129,7 @@ int main() { 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, //; - (CAPTURE_PIN_COUNT * CAPTURE_N_SAMPLES + 31) / 32, - CAPTURE_PIN_BASE, true); + logic_analyser_arm(pio, sm, dma_chan, capture_buf, buf_size_words, CAPTURE_PIN_BASE, true); printf("Starting PWM example\n"); // PWM example: ----------------------------------------------------------- @@ -119,6 +148,8 @@ int main() { 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);