From 305d18a4974a76f19dc415a807e211252c56704c Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Tue, 19 Nov 2024 20:48:49 +0000 Subject: [PATCH] Fix some pio examples to work with gpios >= 32 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. --- pio/hello_pio/hello.c | 5 ++++ pio/uart_rx/uart_rx.c | 21 ++++++++++++--- pio/uart_rx/uart_rx_intr.c | 51 +++++++++++------------------------- pio/uart_tx/uart_tx.c | 31 ++++++++++++++++------ pio/uart_tx/uart_tx.pio | 4 +-- pio/ws2812/ws2812.c | 51 ++++++++++++++++++++++-------------- pio/ws2812/ws2812_parallel.c | 22 ++++++++++++---- 7 files changed, 113 insertions(+), 72 deletions(-) diff --git a/pio/hello_pio/hello.c b/pio/hello_pio/hello.c index 8c01eb612..bb7a621cc 100644 --- a/pio/hello_pio/hello.c +++ b/pio/hello_pio/hello.c @@ -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 diff --git a/pio/uart_rx/uart_rx.c b/pio/uart_rx/uart_rx.c index 2b7722e1c..eac0fde83 100644 --- a/pio/uart_rx/uart_rx.c +++ b/pio/uart_rx/uart_rx.c @@ -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(); @@ -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); @@ -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); } diff --git a/pio/uart_rx/uart_rx_intr.c b/pio/uart_rx/uart_rx_intr.c index 6e9c24e02..fc018e46c 100644 --- a/pio/uart_rx/uart_rx_intr.c +++ b/pio/uart_rx/uart_rx_intr.c @@ -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; @@ -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(); @@ -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)) { @@ -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); @@ -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); diff --git a/pio/uart_tx/uart_tx.c b/pio/uart_tx/uart_tx.c index 26266b969..dc203e598 100644 --- a/pio/uart_tx/uart_tx.c +++ b/pio/uart_tx/uart_tx.c @@ -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); } diff --git a/pio/uart_tx/uart_tx.pio b/pio/uart_tx/uart_tx.pio index 97545849f..05faa4f3e 100644 --- a/pio/uart_tx/uart_tx.pio +++ b/pio/uart_tx/uart_tx.pio @@ -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); diff --git a/pio/ws2812/ws2812.c b/pio/ws2812/ws2812.c index 42463652a..56fb5fce4 100644 --- a/pio/ws2812/ws2812.c +++ b/pio/ws2812/ws2812.c @@ -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) { @@ -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; @@ -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); @@ -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); } diff --git a/pio/ws2812/ws2812_parallel.c b/pio/ws2812/ws2812_parallel.c index 37be663cf..ba3cd2050 100644 --- a/pio/ws2812/ws2812_parallel.c +++ b/pio/ws2812/ws2812_parallel.c @@ -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; @@ -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); @@ -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); }