Skip to content

Commit

Permalink
Fix some pio examples to work with gpios >= 32
Browse files Browse the repository at this point in the history
Use pio_claim_free_sm_and_add_program_for_gpio_range as a good example
of how you should find a free pio and state machine that's compatible
with a particular gpio.
  • Loading branch information
peterharperuk committed Nov 22, 2024
1 parent 362f676 commit 305d18a
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 72 deletions.
5 changes: 5 additions & 0 deletions pio/hello_pio/hello.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
#define HELLO_PIO_LED_PIN PICO_DEFAULT_LED_PIN
#endif

// Check the pin is compatible with the platform
#if HELLO_PIO_LED_PIN >= 32 && !PICO_PIO_USE_GPIO_BASE
#error Attempting to use a pin>32 on a platform that does not support it
#endif

int main() {
#ifndef HELLO_PIO_LED_PIN
#warning pio/hello_pio example requires a board with a regular LED
Expand Down
21 changes: 18 additions & 3 deletions pio/uart_rx/uart_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
#define HARD_UART_TX_PIN 4
#define PIO_RX_PIN 3

// Check the pin is compatible with the platform
#if PIO_RX_PIN >= 32 && !PICO_PIO_USE_GPIO_BASE
#error Attempting to use a pin>32 on a platform that does not support it
#endif

// Ask core 1 to print a string, to make things easier on core 0
void core1_main() {
const char *s = (const char *) multicore_fifo_pop_blocking();
Expand All @@ -42,10 +47,17 @@ int main() {
gpio_set_function(HARD_UART_TX_PIN, GPIO_FUNC_UART);

// Set up the state machine we're going to use to receive them.
PIO pio = pio0;
uint sm = 0;
uint offset = pio_add_program(pio, &uart_rx_program);
PIO pio;
uint sm;
uint offset;

// This will find a free pio and state machine for our program and load it for us
// We use pio_claim_free_sm_and_add_program_for_gpio_range so we can address gpios >= 32 if needed and supported by the hardware
bool success = pio_claim_free_sm_and_add_program_for_gpio_range(&uart_rx_program, &pio, &sm, &offset, PIO_RX_PIN, 1, true);
hard_assert(success);

uart_rx_program_init(pio, sm, offset, PIO_RX_PIN, SERIAL_BAUD);
//uart_rx_mini_program_init(pio, sm, offset, PIO_RX_PIN, SERIAL_BAUD);

// Tell core 1 to print some text to uart1 as fast as it can
multicore_launch_core1(core1_main);
Expand All @@ -57,4 +69,7 @@ int main() {
char c = uart_rx_program_getc(pio, sm);
putchar(c);
}

// This will free resources and unload our program
pio_remove_program_and_unclaim_sm(&uart_rx_program, pio, sm, offset);
}
51 changes: 16 additions & 35 deletions pio/uart_rx/uart_rx_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
#define FIFO_SIZE 64
#define MAX_COUNTER 10

// Check the pin is compatible with the platform
#if PIO_RX_PIN >= NUM_BANK0_GPIOS
#error Attempting to use a pin>32 on a platform that does not support it
#endif

static PIO pio;
static uint sm;
static int8_t pio_irq;
Expand Down Expand Up @@ -84,27 +89,6 @@ static void async_worker_func(__unused async_context_t *async_context, __unused
}
}

// Find a free pio and state machine and load the program into it.
// Returns false if this fails
static bool init_pio(const pio_program_t *program, PIO *pio_hw, uint *sm, uint *offset) {
// Find a free pio
*pio_hw = pio1;
if (!pio_can_add_program(*pio_hw, program)) {
*pio_hw = pio0;
if (!pio_can_add_program(*pio_hw, program)) {
*offset = -1;
return false;
}
}
*offset = pio_add_program(*pio_hw, program);
// Find a state machine
*sm = (int8_t)pio_claim_unused_sm(*pio_hw, false);
if (*sm < 0) {
return false;
}
return true;
}

int main() {
// Console output (also a UART, yes it's confusing)
setup_default_uart();
Expand All @@ -123,16 +107,15 @@ int main() {
}
async_context_add_when_pending_worker(&async_context.core, &worker);

// Set up the state machine we're going to use to receive them.
// In real code you need to find a free pio and state machine in case pio resources are used elsewhere
if (!init_pio(&uart_rx_program, &pio, &sm, &offset)) {
panic("failed to setup pio");
}
// This will find a free pio and state machine for our program and load it for us
// We use pio_claim_free_sm_and_add_program_for_gpio_range so we can address gpios >= 32 if needed and supported by the hardware
bool success = pio_claim_free_sm_and_add_program_for_gpio_range(&uart_rx_program, &pio, &sm, &offset, PIO_RX_PIN, 1, true);
hard_assert(success);

uart_rx_program_init(pio, sm, offset, PIO_RX_PIN, SERIAL_BAUD);

// Find a free irq
static_assert(PIO0_IRQ_1 == PIO0_IRQ_0 + 1 && PIO1_IRQ_1 == PIO1_IRQ_0 + 1, "");
pio_irq = (pio == pio0) ? PIO0_IRQ_0 : PIO1_IRQ_0;
pio_irq = pio_get_irq_num(pio, 0);
if (irq_get_exclusive_handler(pio_irq)) {
pio_irq++;
if (irq_get_exclusive_handler(pio_irq)) {
Expand All @@ -143,8 +126,8 @@ int main() {
// Enable interrupt
irq_add_shared_handler(pio_irq, pio_irq_func, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); // Add a shared IRQ handler
irq_set_enabled(pio_irq, true); // Enable the IRQ
const uint irq_index = pio_irq - ((pio == pio0) ? PIO0_IRQ_0 : PIO1_IRQ_0); // Get index of the IRQ
pio_set_irqn_source_enabled(pio, irq_index, pis_sm0_rx_fifo_not_empty + sm, true); // Set pio to tell us when the FIFO is NOT empty
const uint irq_index = pio_irq - pio_get_irq_num(pio, 0); // Get index of the IRQ
pio_set_irqn_source_enabled(pio, irq_index, pio_get_rx_fifo_not_empty_interrupt_source(sm), true); // Set pio to tell us when the FIFO is NOT empty

// Tell core 1 to print text to uart1
multicore_launch_core1(core1_main);
Expand All @@ -160,14 +143,12 @@ int main() {
}

// Disable interrupt
pio_set_irqn_source_enabled(pio, irq_index, pis_sm0_rx_fifo_not_empty + sm, false);
pio_set_irqn_source_enabled(pio, irq_index, pio_get_rx_fifo_not_empty_interrupt_source(sm), false);
irq_set_enabled(pio_irq, false);
irq_remove_handler(pio_irq, pio_irq_func);

// Cleanup pio
pio_sm_set_enabled(pio, sm, false);
pio_remove_program(pio, &uart_rx_program, offset);
pio_sm_unclaim(pio, sm);
// This will free resources and unload our program
pio_remove_program_and_unclaim_sm(&uart_rx_program, pio, sm, offset);

async_context_remove_when_pending_worker(&async_context.core, &worker);
async_context_deinit(&async_context.core);
Expand Down
31 changes: 23 additions & 8 deletions pio/uart_tx/uart_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,35 @@
#include "hardware/pio.h"
#include "uart_tx.pio.h"

// We're going to use PIO to print "Hello, world!" on the same GPIO which we
// normally attach UART0 to.
#define PIO_TX_PIN 0

// Check the pin is compatible with the platform
#if PIO_TX_PIN >= NUM_BANK0_GPIOS
#error Attempting to use a pin>32 on a platform that does not support it
#endif

int main() {
// We're going to use PIO to print "Hello, world!" on the same GPIO which we
// normally attach UART0 to.
const uint PIN_TX = 0;
// This is the same as the default UART baud rate on Pico
const uint SERIAL_BAUD = 115200;

PIO pio = pio0;
uint sm = 0;
uint offset = pio_add_program(pio, &uart_tx_program);
uart_tx_program_init(pio, sm, offset, PIN_TX, SERIAL_BAUD);
PIO pio;
uint sm;
uint offset;

// This will find a free pio and state machine for our program and load it for us
// We use pio_claim_free_sm_and_add_program_for_gpio_range so we can address gpios >= 32 if needed and supported by the hardware
bool success = pio_claim_free_sm_and_add_program_for_gpio_range(&uart_tx_program, &pio, &sm, &offset, PIO_TX_PIN, 1, true);
hard_assert(success);

uart_tx_program_init(pio, sm, offset, PIO_TX_PIN, SERIAL_BAUD);

while (true) {
uart_tx_program_puts(pio, sm, "Hello, world! (from PIO!)\n");
uart_tx_program_puts(pio, sm, "Hello, world! (from PIO!)\r\n");
sleep_ms(1000);
}

// This will free resources and unload our program
pio_remove_program_and_unclaim_sm(&uart_tx_program, pio, sm, offset);
}
4 changes: 2 additions & 2 deletions pio/uart_tx/uart_tx.pio
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ bitloop: ; This loop will run 8 times (8n1 UART)
static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) {
// Tell PIO to initially drive output-high on the selected pin, then map PIO
// onto that pin with the IO muxes.
pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
pio_sm_set_pins_with_mask64(pio, sm, 1ull << pin_tx, 1ull << pin_tx);
pio_sm_set_pindirs_with_mask64(pio, sm, 1ull << pin_tx, 1ull << pin_tx);
pio_gpio_init(pio, pin_tx);

pio_sm_config c = uart_tx_program_get_default_config(offset);
Expand Down
51 changes: 32 additions & 19 deletions pio/ws2812/ws2812.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@
#define WS2812_PIN 2
#endif

static inline void put_pixel(uint32_t pixel_grb) {
pio_sm_put_blocking(pio0, 0, pixel_grb << 8u);
// Check the pin is compatible with the platform
#if WS2812_PIN >= NUM_BANK0_GPIOS
#error Attempting to use a pin>32 on a platform that does not support it
#endif

static inline void put_pixel(PIO pio, uint sm, uint32_t pixel_grb) {
pio_sm_put_blocking(pio, sm, pixel_grb << 8u);
}

static inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b) {
Expand All @@ -54,44 +59,44 @@ static inline uint32_t urgbw_u32(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
(uint32_t) (b);
}

void pattern_snakes(uint len, uint t) {
void pattern_snakes(PIO pio, uint sm, uint len, uint t) {
for (uint i = 0; i < len; ++i) {
uint x = (i + (t >> 1)) % 64;
if (x < 10)
put_pixel(urgb_u32(0xff, 0, 0));
put_pixel(pio, sm, urgb_u32(0xff, 0, 0));
else if (x >= 15 && x < 25)
put_pixel(urgb_u32(0, 0xff, 0));
put_pixel(pio, sm, urgb_u32(0, 0xff, 0));
else if (x >= 30 && x < 40)
put_pixel(urgb_u32(0, 0, 0xff));
put_pixel(pio, sm, urgb_u32(0, 0, 0xff));
else
put_pixel(0);
put_pixel(pio, sm, 0);
}
}

void pattern_random(uint len, uint t) {
void pattern_random(PIO pio, uint sm, uint len, uint t) {
if (t % 8)
return;
for (uint i = 0; i < len; ++i)
put_pixel(rand());
put_pixel(pio, sm, rand());
}

void pattern_sparkle(uint len, uint t) {
void pattern_sparkle(PIO pio, uint sm, uint len, uint t) {
if (t % 8)
return;
for (uint i = 0; i < len; ++i)
put_pixel(rand() % 16 ? 0 : 0xffffffff);
put_pixel(pio, sm, rand() % 16 ? 0 : 0xffffffff);
}

void pattern_greys(uint len, uint t) {
void pattern_greys(PIO pio, uint sm, uint len, uint t) {
uint max = 100; // let's not draw too much current!
t %= max;
for (uint i = 0; i < len; ++i) {
put_pixel(t * 0x10101);
put_pixel(pio, sm, t * 0x10101);
if (++t >= max) t = 0;
}
}

typedef void (*pattern)(uint len, uint t);
typedef void (*pattern)(PIO pio, uint sm, uint len, uint t);
const struct {
pattern pat;
const char *name;
Expand All @@ -105,12 +110,17 @@ const struct {
int main() {
//set_sys_clock_48();
stdio_init_all();
printf("WS2812 Smoke Test, using pin %d", WS2812_PIN);
printf("WS2812 Smoke Test, using pin %d\n", WS2812_PIN);

// todo get free sm
PIO pio = pio0;
int sm = 0;
uint offset = pio_add_program(pio, &ws2812_program);
PIO pio;
uint sm;
uint offset;

// This will find a free pio and state machine for our program and load it for us
// We use pio_claim_free_sm_and_add_program_for_gpio_range so we can address gpios >= 32 if needed and supported by the hardware
bool success = pio_claim_free_sm_and_add_program_for_gpio_range(&ws2812_program, &pio, &sm, &offset, WS2812_PIN, 1, true);
hard_assert(success);

ws2812_program_init(pio, sm, offset, WS2812_PIN, 800000, IS_RGBW);

Expand All @@ -121,9 +131,12 @@ int main() {
puts(pattern_table[pat].name);
puts(dir == 1 ? "(forward)" : "(backward)");
for (int i = 0; i < 1000; ++i) {
pattern_table[pat].pat(NUM_PIXELS, t);
pattern_table[pat].pat(pio, sm, NUM_PIXELS, t);
sleep_ms(10);
t += dir;
}
}

// This will free resources and unload our program
pio_remove_program_and_unclaim_sm(&ws2812_program, pio, sm, offset);
}
22 changes: 17 additions & 5 deletions pio/ws2812/ws2812_parallel.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
#define NUM_PIXELS 64
#define WS2812_PIN_BASE 2

// Check the pin is compatible with the platform
#if WS2812_PIN_BASE >= NUM_BANK0_GPIOS
#error Attempting to use a pin>32 on a platform that does not support it
#endif

// horrible temporary hack to avoid changing pattern code
static uint8_t *current_strip_out;
static bool current_strip_4color;
Expand Down Expand Up @@ -278,12 +283,16 @@ void output_strips_dma(value_bits_t *bits, uint value_length) {
int main() {
//set_sys_clock_48();
stdio_init_all();
puts("WS2812 parallel");
printf("WS2812 parallel using pin %d\n", WS2812_PIN_BASE);

PIO pio;
uint sm;
uint offset;

// todo get free sm
PIO pio = pio0;
int sm = 0;
uint offset = pio_add_program(pio, &ws2812_parallel_program);
// This will find a free pio and state machine for our program and load it for us
// We use pio_claim_free_sm_and_add_program_for_gpio_range so we can address gpios >= 32 if needed and supported by the hardware
bool success = pio_claim_free_sm_and_add_program_for_gpio_range(&ws2812_parallel_program, &pio, &sm, &offset, WS2812_PIN_BASE, count_of(strips), true);
hard_assert(success);

ws2812_parallel_program_init(pio, sm, offset, WS2812_PIN_BASE, count_of(strips), 800000);

Expand Down Expand Up @@ -318,4 +327,7 @@ int main() {
}
memset(&states, 0, sizeof(states)); // clear out errors
}

// This will free resources and unload our program
pio_remove_program_and_unclaim_sm(&ws2812_parallel_program, pio, sm, offset);
}

0 comments on commit 305d18a

Please sign in to comment.