From 560ce9a6a1479bc9876c25f17dd1fd6bcc7f8f47 Mon Sep 17 00:00:00 2001 From: Steve Markgraf Date: Fri, 13 Dec 2024 00:37:17 +0100 Subject: [PATCH] add 16 bit logic analyzer example --- README.md | 5 + apps/CMakeLists.txt | 1 + apps/logic_analyzer/16bit_input.pio | 78 +++++++++++++++ apps/logic_analyzer/CMakeLists.txt | 20 ++++ apps/logic_analyzer/logic_analyzer.c | 144 +++++++++++++++++++++++++++ 5 files changed, 248 insertions(+) create mode 100644 apps/logic_analyzer/16bit_input.pio create mode 100644 apps/logic_analyzer/CMakeLists.txt create mode 100644 apps/logic_analyzer/logic_analyzer.c diff --git a/README.md b/README.md index 870d481..9cdc89c 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ In addition to that, the apps folder contains a couple of example applications: This application uses the PIO to generate a 16-bit counter value which is written to a DMA ringbuffer, which is then streamed out via hsdaoh. The counter can be verified using the hsdaoh_test host application. +### logic_analyzer + +Sample 16 GPIOs with the PIO and transfer the data, can be used as a 16 bit @ 32 MHz logic analyzer, or be adapted to 8 bit @ 64 MHz and so on. +The IOs used for input are GP0-11, GP20-22 and GP26. + ### internal_adc The data from the internal ADC is streamed out via USB. Default configuration is overclocking the ADC to 3.33 MS/s. Using the USB PLL and overvolting beyond VREG_VOLTAGE_MAX, up to 7.9 MS/s can be achieved. diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index b14d41d..224ce08 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(counter) add_subdirectory(external_adc) add_subdirectory(internal_adc) +add_subdirectory(logic_analyzer) diff --git a/apps/logic_analyzer/16bit_input.pio b/apps/logic_analyzer/16bit_input.pio new file mode 100644 index 0000000..9323937 --- /dev/null +++ b/apps/logic_analyzer/16bit_input.pio @@ -0,0 +1,78 @@ +; +; Copyright (c) 2024 Steve Markgraf +; +; SPDX-License-Identifier: BSD-3-Clause +; +; 16 bit logic analyzer +; + +.pio_version 0 +.program la_16bit_input + +public entry_point: + +.wrap_target + ; GP0 - GP11 are contiguous, then come the HSTX pins which are already in use + ; Next pins we can use are 20, 21, 22 and then 26, 27, 28 + mov osr, pins ; Sample all 32 pins + in osr, 12 ; shift values of GP0-GP11 to ISR + out null, 20 ; shift out GP0-GP11 and discard GP12-GP19 + in osr, 3 ; shift in GP20 - GP22 + out null, 6 ; drop GP20 - GP25 + in osr, 1 ; shift in GP26 - now the ISR is full and the autopush is happening + in null, 16 ; fill with zeroes so that data is in lower 16 bits + nop [3] +.wrap + +% c-sdk { +static inline void la_16bit_input_program_init(PIO pio, uint sm, uint offset, uint pin) +{ + pio_sm_config c = la_16bit_input_program_get_default_config(offset); + + // Set the IN base pin to the provided `pin` parameter. + sm_config_set_in_pins(&c, pin); + + // Set the pin directions to input at the PIO + // Set D0-D11 of the ADC as input + pio_sm_set_consecutive_pindirs(pio, sm, pin, 27, false); + + // Connect these GPIOs to this PIO block + for (int i = pin; i < (pin+12); i++) { + pio_gpio_init(pio, pin + i); + gpio_set_pulls(pin + i, false, false); + } + + // Connect these GPIOs to this PIO block + for (int i = 20; i < 23; i++) { + pio_gpio_init(pio, i); + gpio_set_pulls(i, false, false); + } + + pio_gpio_init(pio, 26); + gpio_set_pulls(26, false, false); + + sm_config_set_in_shift( + &c, + true, // Shift-to-right = true + true, // Autopush enabled + 32 // Autopush threshold = 32 + ); + + // required in order to set shift-to-right to true (for the out, null ops) + sm_config_set_out_shift( + &c, + true, // Shift-to-right = true + false, // Autopush disabled + 1 // Autopush threshold: ignored + ); + + // We only receive, so disable the TX FIFO to make the RX FIFO deeper. + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + + sm_config_set_clkdiv(&c, 1.f); + + // Load our configuration, and start the program from the beginning + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%} diff --git a/apps/logic_analyzer/CMakeLists.txt b/apps/logic_analyzer/CMakeLists.txt new file mode 100644 index 0000000..c31facc --- /dev/null +++ b/apps/logic_analyzer/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(logic_analyzer + logic_analyzer.c +) + +target_compile_options(logic_analyzer PRIVATE -Wall) + +target_link_libraries(logic_analyzer + pico_stdlib + pico_util + hardware_pio + hardware_dma + libpicohsdaoh +) +pico_generate_pio_header(logic_analyzer ${CMAKE_CURRENT_LIST_DIR}/16bit_input.pio) + +# enable usb output, disable uart output +pico_enable_stdio_usb(logic_analyzer 1) + +# create map/bin/hex file etc. +pico_add_extra_outputs(logic_analyzer) diff --git a/apps/logic_analyzer/logic_analyzer.c b/apps/logic_analyzer/logic_analyzer.c new file mode 100644 index 0000000..d858b4e --- /dev/null +++ b/apps/logic_analyzer/logic_analyzer.c @@ -0,0 +1,144 @@ +/* + * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks + * Implementation for the Raspberry Pi RP2350 HSTX peripheral + * + * 16 bit logic analyzer example + * + * Copyright (c) 2024 by Steve Markgraf + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors may + * be used to endorse or promote products derived from this software + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "pico/stdlib.h" +#include "hardware/clocks.h" +#include "hardware/irq.h" +#include "hardware/sync.h" +#include "hardware/vreg.h" +#include "hardware/dma.h" +#include "hardware/pio.h" + +#include "picohsdaoh.h" +#include "16bit_input.pio.h" + +/* The PIO is running with sys_clk, and needs 10 cycles per sample, + * so the LA is sampling with 32 MHz */ +#define SYS_CLK 320000 + +#define PIO_INPUT_PIN_BASE 0 + +#define DMACH_PIO_PING 0 +#define DMACH_PIO_PONG 1 + +static bool pio_dma_pong = false; +uint16_t ringbuffer[RBUF_TOTAL_LEN]; +int ringbuf_head = 0; + +void __scratch_y("") pio_dma_irq_handler() +{ + uint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING; + dma_channel_hw_t *ch = &dma_hw->ch[ch_num]; + dma_hw->intr = 1u << ch_num; + pio_dma_pong = !pio_dma_pong; + + ringbuf_head = (ringbuf_head + 1) % RBUF_SLICES; + + ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN]; + ch->transfer_count = RBUF_DATA_LEN; + + hsdaoh_update_head(ringbuf_head); +} + +void init_pio_input(void) +{ + PIO pio = pio0; + uint offset = pio_add_program(pio, &la_16bit_input_program); + uint sm_data = pio_claim_unused_sm(pio, true); + la_16bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE); + + dma_channel_config c; + c = dma_channel_get_default_config(DMACH_PIO_PING); + channel_config_set_chain_to(&c, DMACH_PIO_PONG); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false)); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_16); + + dma_channel_configure( + DMACH_PIO_PING, + &c, + &ringbuffer[0 * RBUF_SLICE_LEN], + &pio->rxf[sm_data], + RBUF_DATA_LEN, + false + ); + c = dma_channel_get_default_config(DMACH_PIO_PONG); + channel_config_set_chain_to(&c, DMACH_PIO_PING); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false)); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_16); + + dma_channel_configure( + DMACH_PIO_PONG, + &c, + &ringbuffer[1 * RBUF_SLICE_LEN], + &pio->rxf[sm_data], + RBUF_DATA_LEN, + false + ); + + dma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG); + dma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG); + irq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler); + irq_set_enabled(DMA_IRQ_0, true); + + dma_channel_start(DMACH_PIO_PING); +} + +int main() +{ + /* set maximum 'allowed' voltage without voiding warranty */ + vreg_set_voltage(VREG_VOLTAGE_MAX); + sleep_ms(1); + + set_sys_clock_khz(SYS_CLK, true); + + /* set HSTX clock to sysclk/2 */ + hw_write_masked( + &clocks_hw->clk[clk_hstx].div, + 2 << CLOCKS_CLK_HSTX_DIV_INT_LSB, + CLOCKS_CLK_HSTX_DIV_INT_BITS + ); + + stdio_init_all(); + + hsdaoh_init(ringbuffer); + hsdaoh_start(); + init_pio_input(); + + while (1) + __wfi(); +}