diff --git a/.gitignore b/.gitignore index e248f993a..d071c1dba 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ build-* build_* .DS_Store *.pdf +pio/squarewave/generated/squarewave_wrap.pio.h +pio/squarewave/generated/squarewave.hex +pio/squarewave/generated/squarewave.pio.h +pio/ws2812/generated/ws2812.pio.h +pio/ws2812/generated/ws2812.py +.gitignore \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a76866c0d..4abf24255 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ endif() # Hardware-specific examples in subdirectories: add_subdirectory(adc) +add_subdirectory(async_context) add_subdirectory(binary_info) add_subdirectory(bootloaders) add_subdirectory(clocks) diff --git a/README.md b/README.md index f43a7339a..cd05017e0 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,11 @@ App|Description [dma_capture](adc/dma_capture) | Use the DMA to capture many samples from the ADC. [read_vsys](adc/read_vsys) | Demonstrates how to read VSYS to get the voltage of the power supply. +### async_context +App|Description +---|--- +[simple_at_time_worker](async_context/simple_at_time_worker) | Use a worker on a threadsafe background context to blink the on-board LED. + ### Binary Info App|Description diff --git a/async_context/CMakeLists.txt b/async_context/CMakeLists.txt new file mode 100644 index 000000000..ae458556a --- /dev/null +++ b/async_context/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory_exclude_platforms(simple_at_time_worker) diff --git a/async_context/simple_at_time_worker/CMakeLists.txt b/async_context/simple_at_time_worker/CMakeLists.txt new file mode 100644 index 000000000..dcbd7feb0 --- /dev/null +++ b/async_context/simple_at_time_worker/CMakeLists.txt @@ -0,0 +1,22 @@ +add_executable(simple_at_time_worker + simple_at_time_worker.c + ) + +# common dependencies +target_link_libraries(simple_at_time_worker + pico_stdlib + pico_async_context_threadsafe_background + ) + +# on WiFi boards we need `cyw43_arch_none` to control the on-board LED +if (PICO_CYW43_SUPPORTED) + target_link_libraries(simple_at_time_worker + pico_cyw43_arch_none + ) +endif() + +# create map/bin/hex file etc. +pico_add_extra_outputs(simple_at_time_worker) + +# add url via pico_set_program_url +example_auto_set_url(simple_at_time_worker) diff --git a/async_context/simple_at_time_worker/README.md b/async_context/simple_at_time_worker/README.md new file mode 100644 index 000000000..1c99ef4a6 --- /dev/null +++ b/async_context/simple_at_time_worker/README.md @@ -0,0 +1,10 @@ +# Simple at-time worker + +A simple example demonstrating how to add an *at-time* worker to an `async_context`. + +The program creates an `async_context_threadsafe_background` and uses an *at-time* worker to flash the LED on a Pico or Pico-W board. + +## note +An `async_context_threadsafe_background` is generally used to ensure that a non-reentrant library such as lwIP can be used successfully in a multi tasking application; so a practical networking application would typically use the `async_context` provided by `cyw43_arch` rather than creating its own. + +More details about `async_context` can be found under [High Level APIs](https://www.raspberrypi.com/documentation/pico-sdk/high_level.html#group_pico_async_context) in the SDK documentation. \ No newline at end of file diff --git a/async_context/simple_at_time_worker/simple_at_time_worker.c b/async_context/simple_at_time_worker/simple_at_time_worker.c new file mode 100644 index 000000000..93d277c5e --- /dev/null +++ b/async_context/simple_at_time_worker/simple_at_time_worker.c @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2025 mjcross + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "pico/async_context_threadsafe_background.h" + +// for Pico W devices the on-board LED is controlled via the WiFi module +#ifdef CYW43_WL_GPIO_LED_PIN +#include "pico/cyw43_arch.h" +#endif + +#ifndef LED_DELAY_MS +#define LED_DELAY_MS 500 +#endif + +// generic function to initialise the on-board LED +int pico_led_init(void) { +#if defined(PICO_DEFAULT_LED_PIN) + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); // non-WiFi boards + return PICO_OK; +#elif defined(CYW43_WL_GPIO_LED_PIN) + return cyw43_arch_init(); // WiFi boards +#endif +} + +// generic function to turn the on-board LED on or off +void pico_led_set(bool state) { +#if defined(PICO_DEFAULT_LED_PIN) + gpio_put(PICO_DEFAULT_LED_PIN, state); // non-WiFi boards +#elif defined(CYW43_WL_GPIO_LED_PIN) + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, state); // WiFi boards +#endif +} + +// a simple user data structure for our async at-time worker +typedef struct { + bool state; +} led_state_t; + +// callback function for our async at-time worker +// note: this function MUST be safe to call from an IRQ +void worker_cb(async_context_t *p_ctx, async_at_time_worker_t *p_worker) { + // read user data from worker + led_state_t *p_led = (led_state_t *)(p_worker->user_data); + + // toggle the LED + p_led->state = !p_led->state; + pico_led_set(p_led->state); + + // at-time workers are automatically removed from the context just before they run + // so to keep the LED flashing we must now re-schedule the task + async_context_add_at_time_worker_in_ms(p_ctx, p_worker, LED_DELAY_MS); +} + + +int main() { + // initialise the LED and our user data structure + hard_assert( + pico_led_init() == PICO_OK + ); + led_state_t led_state = { + .state = false + }; + + // create and initialise an async background context + // note: in a networking application we might typically use the context returned by + // cyw43_arch_async_context() instead of creating a new one here + async_context_threadsafe_background_t ctx; + hard_assert( + async_context_threadsafe_background_init_with_defaults(&ctx) == true + ); + + // define an async at-time worker that will run our callback function + async_at_time_worker_t worker = { + .do_work = worker_cb, + .user_data = &led_state + }; + + // add an at-time worker to the context, scheduled to run after LED_DELAY_MS + // note: ctx.core is the underlying async_context_t of our threadsafe background + hard_assert( + async_context_add_at_time_worker_in_ms(&ctx.core, &worker, LED_DELAY_MS) == true + ); + + // the LED will flash in the background + while(true) { + sleep_ms(5000); + } +} diff --git a/pio/squarewave/generated/squarewave.hex b/pio/squarewave/generated/squarewave.hex deleted file mode 100644 index 2449e2938..000000000 --- a/pio/squarewave/generated/squarewave.hex +++ /dev/null @@ -1,4 +0,0 @@ -e081 -e101 -e000 -0001 diff --git a/pio/squarewave/generated/squarewave.pio.h b/pio/squarewave/generated/squarewave.pio.h deleted file mode 100644 index 7bcdc191e..000000000 --- a/pio/squarewave/generated/squarewave.pio.h +++ /dev/null @@ -1,45 +0,0 @@ -// -------------------------------------------------- // -// This file is autogenerated by pioasm; do not edit! // -// -------------------------------------------------- // - -#pragma once - -#if !PICO_NO_HARDWARE -#include "hardware/pio.h" -#endif - -// ---------- // -// squarewave // -// ---------- // - -#define squarewave_wrap_target 0 -#define squarewave_wrap 3 -#define squarewave_pio_version 0 - -static const uint16_t squarewave_program_instructions[] = { - // .wrap_target - 0xe081, // 0: set pindirs, 1 - 0xe101, // 1: set pins, 1 [1] - 0xe000, // 2: set pins, 0 - 0x0001, // 3: jmp 1 - // .wrap -}; - -#if !PICO_NO_HARDWARE -static const struct pio_program squarewave_program = { - .instructions = squarewave_program_instructions, - .length = 4, - .origin = -1, - .pio_version = squarewave_pio_version, -#if PICO_PIO_VERSION > 0 - .used_gpio_ranges = 0x0 -#endif -}; - -static inline pio_sm_config squarewave_program_get_default_config(uint offset) { - pio_sm_config c = pio_get_default_sm_config(); - sm_config_set_wrap(&c, offset + squarewave_wrap_target, offset + squarewave_wrap); - return c; -} -#endif - diff --git a/pio/squarewave/generated/squarewave_wrap.pio.h b/pio/squarewave/generated/squarewave_wrap.pio.h deleted file mode 100644 index 17ceee3cb..000000000 --- a/pio/squarewave/generated/squarewave_wrap.pio.h +++ /dev/null @@ -1,44 +0,0 @@ -// -------------------------------------------------- // -// This file is autogenerated by pioasm; do not edit! // -// -------------------------------------------------- // - -#pragma once - -#if !PICO_NO_HARDWARE -#include "hardware/pio.h" -#endif - -// --------------- // -// squarewave_wrap // -// --------------- // - -#define squarewave_wrap_wrap_target 1 -#define squarewave_wrap_wrap 2 -#define squarewave_wrap_pio_version 0 - -static const uint16_t squarewave_wrap_program_instructions[] = { - 0xe081, // 0: set pindirs, 1 - // .wrap_target - 0xe101, // 1: set pins, 1 [1] - 0xe100, // 2: set pins, 0 [1] - // .wrap -}; - -#if !PICO_NO_HARDWARE -static const struct pio_program squarewave_wrap_program = { - .instructions = squarewave_wrap_program_instructions, - .length = 3, - .origin = -1, - .pio_version = squarewave_wrap_pio_version, -#if PICO_PIO_VERSION > 0 - .used_gpio_ranges = 0x0 -#endif -}; - -static inline pio_sm_config squarewave_wrap_program_get_default_config(uint offset) { - pio_sm_config c = pio_get_default_sm_config(); - sm_config_set_wrap(&c, offset + squarewave_wrap_wrap_target, offset + squarewave_wrap_wrap); - return c; -} -#endif - diff --git a/pio/ws2812/generated/ws2812.pio.h b/pio/ws2812/generated/ws2812.pio.h deleted file mode 100644 index 720153700..000000000 --- a/pio/ws2812/generated/ws2812.pio.h +++ /dev/null @@ -1,123 +0,0 @@ -// -------------------------------------------------- // -// This file is autogenerated by pioasm; do not edit! // -// -------------------------------------------------- // - -#pragma once - -#if !PICO_NO_HARDWARE -#include "hardware/pio.h" -#endif - -// ------ // -// ws2812 // -// ------ // - -#define ws2812_wrap_target 0 -#define ws2812_wrap 3 -#define ws2812_pio_version 0 - -#define ws2812_T1 3 -#define ws2812_T2 3 -#define ws2812_T3 4 - -static const uint16_t ws2812_program_instructions[] = { - // .wrap_target - 0x6321, // 0: out x, 1 side 0 [3] - 0x1223, // 1: jmp !x, 3 side 1 [2] - 0x1200, // 2: jmp 0 side 1 [2] - 0xa242, // 3: nop side 0 [2] - // .wrap -}; - -#if !PICO_NO_HARDWARE -static const struct pio_program ws2812_program = { - .instructions = ws2812_program_instructions, - .length = 4, - .origin = -1, - .pio_version = ws2812_pio_version, -#if PICO_PIO_VERSION > 0 - .used_gpio_ranges = 0x0 -#endif -}; - -static inline pio_sm_config ws2812_program_get_default_config(uint offset) { - pio_sm_config c = pio_get_default_sm_config(); - sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap); - sm_config_set_sideset(&c, 1, false, false); - return c; -} - -#include "hardware/clocks.h" -static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { - pio_gpio_init(pio, pin); - pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); - pio_sm_config c = ws2812_program_get_default_config(offset); - sm_config_set_sideset_pins(&c, pin); - sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); - sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); - int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; - float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); - sm_config_set_clkdiv(&c, div); - pio_sm_init(pio, sm, offset, &c); - pio_sm_set_enabled(pio, sm, true); -} - -#endif - -// --------------- // -// ws2812_parallel // -// --------------- // - -#define ws2812_parallel_wrap_target 0 -#define ws2812_parallel_wrap 3 -#define ws2812_parallel_pio_version 0 - -#define ws2812_parallel_T1 3 -#define ws2812_parallel_T2 3 -#define ws2812_parallel_T3 4 - -static const uint16_t ws2812_parallel_program_instructions[] = { - // .wrap_target - 0x6020, // 0: out x, 32 - 0xa20b, // 1: mov pins, ~null [2] - 0xa201, // 2: mov pins, x [2] - 0xa203, // 3: mov pins, null [2] - // .wrap -}; - -#if !PICO_NO_HARDWARE -static const struct pio_program ws2812_parallel_program = { - .instructions = ws2812_parallel_program_instructions, - .length = 4, - .origin = -1, - .pio_version = ws2812_parallel_pio_version, -#if PICO_PIO_VERSION > 0 - .used_gpio_ranges = 0x0 -#endif -}; - -static inline pio_sm_config ws2812_parallel_program_get_default_config(uint offset) { - pio_sm_config c = pio_get_default_sm_config(); - sm_config_set_wrap(&c, offset + ws2812_parallel_wrap_target, offset + ws2812_parallel_wrap); - return c; -} - -#include "hardware/clocks.h" -static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) { - for(uint i=pin_base; i