Skip to content

Instantly share code, notes, and snippets.

@Lukelectro
Created November 4, 2024 21:49
Show Gist options
  • Save Lukelectro/bcded0cc4e09df868421af01b088456b to your computer and use it in GitHub Desktop.
Save Lukelectro/bcded0cc4e09df868421af01b088456b to your computer and use it in GitHub Desktop.
Playlogic - play logic patterns on raspberry pi pico pins using the pio, as a proof of concept pattern generator
to name gist
cmake_minimum_required(VERSION 3.13)
# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)
project(playlogic C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_BOARD pico_w) #compile for pico_w board
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
# Initialize the SDK
pico_sdk_init()
add_executable(playlogic)
target_sources(playlogic PUBLIC playlogic.c)
pico_generate_pio_header(playlogic ${CMAKE_CURRENT_LIST_DIR}/playlogic.pio)
#target_include_directories(playlogic PUBLIC folder1 folder2)
target_link_libraries(playlogic pico_stdlib hardware_pio)
pico_enable_stdio_usb(playlogic 1)
pico_enable_stdio_uart(playlogic 0)
pico_add_extra_outputs(playlogic)
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/binary_info.h"
#include "playlogic.pio.h"
/*
* Bitpattern in the form of "output bits, delay, output bits, delay etc.".
* Can be changed in RAM in running debuger to quickly test external hardware.
* Like an osiloscope and signal generator can be used for analogue circuits, this pattern generator and a logic analyser can be used to test digital circuits.
*
* The example displays HELLO in the logic analyser window and uses the upper 8 bits. But it can be change it at runtime in a debugger.
* (Or, with an adition to this program, over (usb) serial port... That would be less cumbersome)
*
* Note: In case you change this to use other pins and need some 'regular' IO too:
* It is possible to assign pins to regular GPIO (SIO) even when they are in the OUT pingroup of PIO.
* Being in the OUT group but not assigned to PIO they can be assigned to other peripherals, like SIO.
*/
#define SIZE 1234
uint32_t pattern[SIZE] =
{0xFFFF, 5, 0x0000, 25,
0xFF, 2, 0x18, 3, 0xFF, 2, //H
0x00, 3,
0xFF, 1, 0x99, 3, // E
0x00, 3,
0xFF, 1, 0x80, 3, // L
0x00, 3,
0xff, 1, 0x80, 3, // L
0x00, 3,
0xff, 1, 0x81, 2, 0xff, 1, // O
0x00, 20,
0x7F, 1, 0x80, 1, 0xC0, 1, 0x80, 1, 0x7F, 1, // W
0x00, 3,
0xff, 1, 0x81, 2, 0xff, 1, // O
0x00, 3,
0xFF, 1, 0x09,1, 0x29,1 ,0x87,1, 0x80,1, // R
0x00, 3,
0xff, 1, 0x80, 3, // L
0x00, 3,
0xf0, 1, 0x90, 2, 0xff, 1, // d
0x00, 40,
};
uint size = SIZE; // size is a variable so size can be changed with debugger also.
#define STARTPIN 0 // it uses all pins, starting from this one, wrapping around at 32
int main()
{
bi_decl(bi_program_description("This is a test binary. PlayLogic: a logic pattern generator that plays from RAM in a debugger"));
stdio_init_all();
pio_sm_claim(pio0, 0);
PIO pio = pio0;
uint offset = pio_add_program(pio, &playlogic_program);
uint sm = pio_claim_unused_sm(pio, true);
playlogic_program_init(pio, sm, offset, STARTPIN, 25000000);
while (1)
{
for (uint i = 0; i < size; i++)
{
pio_sm_put_blocking(pio, sm, pattern[i]); // write pattern to PIO, wait if FIFO Full (else there would be data loss).
}
}
}
.program playlogic
;
; // some sort of waveform generator // logic 'stimulator' / waveform player / pattern generator etc.
;
; // this can play waveforms stored in the format "[bits] [delay] [bits] [delay] repeat as long as needed" where both bits and delay are 32 bits
; // these waveforms can then be autopulled
; // it could be a simple way to control basicly any external hardware that needs IO toggling, without wasting a lot of pio instructions on the waveforms for it, as these
; // waveforms can then be stored in an array that gets send to the PIO, potentially even by DMA (Not nescesairy, especially if pattern size is under FIFO size).
.wrap_target
out pins 32
out x 32
waitlbl:
jmp x-- waitlbl
.wrap
% c-sdk {
#include "hardware/clocks.h"
static inline void playlogic_program_init(PIO pio, uint sm, uint offset, uint pinbase, uint clockspeed)
{
pio_sm_config c = playlogic_program_get_default_config(offset);
// set ALL pins for use with OUT
sm_config_set_out_pins(&c, pinbase, 32);
// Set this pin's GPIO function (connect PIO to the pad)
for (uint i = 0; i < 32; i++)
{
pio_gpio_init(pio, pinbase + i);
}
// Set the pin direction to output at the PIO
pio_sm_set_consecutive_pindirs(pio, sm, pinbase, 32, true);
float div = clock_get_hz(clk_sys) / clockspeed; // set clockspeed
sm_config_set_clkdiv(&c, div);
// join both 4-level fifo's into one output fifo (8 levels), as there is no input to CPU from this statemachine
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
sm_config_set_out_shift(&c, true, true, 32); // autopull when 32 bits have been shifted out, right
// 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);
}
static inline void playlogic_blinkaled(PIO pio, uint sm)
{
#define DATALENGTH 8
uint data[DATALENGTH] = {0x11111111, 200, 0x22222222, 200, 0x44444444, 0x200, 0x88888888, 300}; // blink LED's on all pins alternating, with 200 and 300 cycles of delay
for (uint i = 0; i < DATALENGTH; i++)
{
pio_sm_put(pio, sm, data[i]);
}
}
%}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment