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..78670962b --- /dev/null +++ b/async_context/simple_at_time_worker/CMakeLists.txt @@ -0,0 +1,16 @@ +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 + pico_status_led # generic control of status LED on different boards + ) + +# 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..06bfa0650 --- /dev/null +++ b/async_context/simple_at_time_worker/simple_at_time_worker.c @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2026 mjcross + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "pico/async_context_threadsafe_background.h" +#include "pico/status_led.h" + +#ifndef LED_DELAY_MS +#define LED_DELAY_MS 500 +#endif + +// define a user data type to be passed to our worker's callback function +typedef struct { + bool led_state; +} my_data_t; + + +// define a callback function for our worker (MUST be safe to call from an IRQ) +void worker_cb(async_context_t *p_ctx, async_at_time_worker_t *p_worker) { + // unpack our user data instance from `(void *)worker.user_data` + my_data_t *p_my_data = p_worker->user_data; + + // update the status LED from our user data and toggle it + status_led_set_state(p_my_data->led_state); + p_my_data->led_state = !p_my_data->led_state; + + // re-schedule the worker, so as to flash the LED continuously (an at_time worker is + // automatically removed from the context just before it runs) + async_context_add_at_time_worker_in_ms(p_ctx, p_worker, LED_DELAY_MS); +} + +// create an instance of our user data type (MUST still exist when the worker runs) +static my_data_t my_data = { + .led_state = true +}; + +// create an asynchronous at-time worker that points to our callback function and user +// data (MUST still exist when the worker runs) +static async_at_time_worker_t worker = { + .do_work = &worker_cb, + .user_data = &my_data +}; + + +int main() { + // initialise the status LED + hard_assert( + status_led_init() == true + ); + + // create and initialise an asynchronous background context with default settings + // note: in a networking application we'd probably reuse the existing cyw43_arch + // context directly with `async_context_t ctx = cyw43_arch_async_context()` + async_context_threadsafe_background_t background; + hard_assert( + async_context_threadsafe_background_init_with_defaults(&background) == true + ); + + // add our at-time worker to the background context, to run after LED_DELAY_MS + // note: `background.core` is the async_context_t of our threadsafe background. If + // we were using the cyw43 context instead (see above) we'd just put `&ctx` here. + hard_assert( + async_context_add_at_time_worker_in_ms(&background.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