From 06935026a66d9f87d012a8cba31e85e94228f71a Mon Sep 17 00:00:00 2001 From: William Grant <williamgrantuk@gmail.com> Date: Sun, 14 May 2023 16:01:44 +0100 Subject: [PATCH] binary_buzz + neopixel --- CMakeLists.txt | 4 ++ binary_buzz/CMakeLists.txt | 15 +++++ binary_buzz/binary_buzz.c | 93 ++++++++++++++++++++++++++++++ buzz_cycle/CMakeLists.txt | 15 +++++ buzz_cycle/buzz_cycle.c | 2 + buzzer/CMakeLists.txt | 3 + buzzer/buzzer.h | 31 ++++++++++ morse/CMakeLists.txt | 3 +- morse/morse.c | 33 ++--------- neopixel/CMakeLists.txt | 6 ++ neopixel/neoPixel.c | 50 ++++++++++++++++ neopixel/ws2812.pio.h | 114 +++++++++++++++++++++++++++++++++++++ 12 files changed, 339 insertions(+), 30 deletions(-) create mode 100644 binary_buzz/CMakeLists.txt create mode 100644 binary_buzz/binary_buzz.c create mode 100644 buzz_cycle/CMakeLists.txt create mode 100644 buzz_cycle/buzz_cycle.c create mode 100644 buzzer/CMakeLists.txt create mode 100644 buzzer/buzzer.h create mode 100644 neopixel/CMakeLists.txt create mode 100644 neopixel/neoPixel.c create mode 100644 neopixel/ws2812.pio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5145408..f9cfa1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,10 @@ project(comp2215-coursework C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) +add_subdirectory(buzzer) add_subdirectory(rios) add_subdirectory(morse) +add_subdirectory(binary_buzz) +add_subdirectory(neopixel) +add_subdirectory(display_driver) diff --git a/binary_buzz/CMakeLists.txt b/binary_buzz/CMakeLists.txt new file mode 100644 index 0000000..123e288 --- /dev/null +++ b/binary_buzz/CMakeLists.txt @@ -0,0 +1,15 @@ +if (TARGET tinyusb_device) + +add_executable(binary_buzz binary_buzz.c) +target_include_directories(binary_buzz PUBLIC "../buzzer/") +target_link_libraries(binary_buzz buzzer pico_stdlib) + +pico_enable_stdio_usb(binary_buzz 1) +pico_enable_stdio_uart(binary_buzz 0) + +pico_add_extra_outputs(binary_buzz) + +elseif(PICO_ON_DEVICE) + message(WARNING "cannot build binary_buzz because TinyUSB submodule is not initialised in the SDK") +endif() + diff --git a/binary_buzz/binary_buzz.c b/binary_buzz/binary_buzz.c new file mode 100644 index 0000000..5ed1337 --- /dev/null +++ b/binary_buzz/binary_buzz.c @@ -0,0 +1,93 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "buzzer.h" + +typedef enum { + zero_button = 20, + play_button = 21, + one_button = 22 +} button_t; + +static inline void setup_button(button_t button) { + gpio_init(button); + gpio_set_dir(button, GPIO_IN); + gpio_pull_up(button); +} + +static bool is_pressed(button_t button) { + return gpio_get(button) ? false : true; +} + +static button_t get_button() { + while (true) { // block until a button is pressed + for (int button = zero_button; button <= one_button; ++button) { + if (is_pressed(button)) { + while (is_pressed(button)) {} // block until button is released + return button; + } + } + } +} + +static inline void setup() { + stdio_init_all(); + + buzzer_init(); + + setup_button(zero_button); + setup_button(one_button); + setup_button(play_button); + + sleep_ms(1000); + printf("setup complete\n"); +} + +static size_t get_input(uint64_t* bits) { + size_t i; + for (i = 0; i < sizeof(uint64_t); ++i) { + switch (get_button()) { + case zero_button: + putchar('0'); + (*bits) &= ~(1ul << i); + break; + + case one_button: + putchar('1'); + (*bits) |= 1ul << i; + break; + + case play_button: + putchar('\n'); + return i; + } + } + + putchar('\n'); + return i; +} + +static void play_input(uint64_t bits, size_t size) { + for (size_t i = 0; i < size; ++i) { + if ((bits >> i) & 1u) { + buzzer_enable(); + } else { + buzzer_disable(); + } + + sleep_ms(500); + } +} + +int main(void) { + setup(); + + uint64_t bits; + while (true) { + size_t size = get_input(&bits); + play_input(bits, size); + } +} + diff --git a/buzz_cycle/CMakeLists.txt b/buzz_cycle/CMakeLists.txt new file mode 100644 index 0000000..21ee1b1 --- /dev/null +++ b/buzz_cycle/CMakeLists.txt @@ -0,0 +1,15 @@ +if (TARGET tinyusb_device) + +add_executable(buzz_cycle buzz_cycle.c) +target_include_directories(buzz_cycle PUBLIC "../buzzer/") +target_link_libraries(buzz_cycle buzzer pico_stdlib) + +pico_enable_stdio_usb(buzz_cycle 1) +pico_enable_stdio_uart(buzz_cycle 0) + +pico_add_extra_outputs(buzz_cycle) + +elseif(PICO_ON_DEVICE) + message(WARNING "cannot build buzz_cycle because TinyUSB submodule is not initialised in the SDK") +endif() + diff --git a/buzz_cycle/buzz_cycle.c b/buzz_cycle/buzz_cycle.c new file mode 100644 index 0000000..67f0e8f --- /dev/null +++ b/buzz_cycle/buzz_cycle.c @@ -0,0 +1,2 @@ +#include "buzzer.h"; + diff --git a/buzzer/CMakeLists.txt b/buzzer/CMakeLists.txt new file mode 100644 index 0000000..46123aa --- /dev/null +++ b/buzzer/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(buzzer STATIC buzzer.h) +target_link_libraries(buzzer hardware_pwm pico_stdlib) + diff --git a/buzzer/buzzer.h b/buzzer/buzzer.h new file mode 100644 index 0000000..67a0bdf --- /dev/null +++ b/buzzer/buzzer.h @@ -0,0 +1,31 @@ +#pragma once + +#include <stdint.h> +#include "pico/stdlib.h" +#include "hardware/pwm.h" + +const unsigned int buzzer = 18u; +unsigned int buzzer_pwm_slice; + +static inline void buzzer_enable() { + pwm_set_chan_level(buzzer_pwm_slice, PWM_CHAN_A, 3); +} + +static inline void buzzer_disable() { + pwm_set_chan_level(buzzer_pwm_slice, PWM_CHAN_A, 0); +} + +static inline void buzzer_toggle(uint64_t time) { + buzzer_enable(); + sleep_ms(time); + buzzer_disable(); +} + +void buzzer_init() { + gpio_set_function(buzzer, GPIO_FUNC_PWM); + buzzer_pwm_slice = pwm_gpio_to_slice_num(buzzer); + pwm_set_wrap(buzzer_pwm_slice, 3); + buzzer_disable(); + pwm_set_enabled(buzzer_pwm_slice, true); +} + diff --git a/morse/CMakeLists.txt b/morse/CMakeLists.txt index 894e753..6b07750 100644 --- a/morse/CMakeLists.txt +++ b/morse/CMakeLists.txt @@ -1,7 +1,8 @@ if (TARGET tinyusb_device) add_executable(morse morse.c) -target_link_libraries(morse pico_stdlib hardware_pwm) +target_include_directories(morse PUBLIC "../buzzer/") +target_link_libraries(morse buzzer pico_stdlib) pico_enable_stdio_usb(morse 1) pico_enable_stdio_uart(morse 0) diff --git a/morse/morse.c b/morse/morse.c index 9c47f6c..8d369c8 100644 --- a/morse/morse.c +++ b/morse/morse.c @@ -3,15 +3,12 @@ #include <stdio.h> #include <stdlib.h> #include "pico/stdlib.h" -#include "hardware/pwm.h" +#include "buzzer.h" // buffer sizes static const size_t MAX_CHARS = 100u; static const size_t MORSE_SIZE = MAX_CHARS * 6u; -// the GPIO pin the buzzer is connected to -static const unsigned int BUZZER = 18u; - // the 'unit' time in miliseconds static const uint64_t UNIT_TIME = 50u; @@ -25,32 +22,10 @@ typedef enum { // enable I/O and configure the pwm static inline void setup() { stdio_init_all(); - - gpio_set_function(BUZZER, GPIO_FUNC_PWM); - unsigned int slice = pwm_gpio_to_slice_num(BUZZER); - pwm_set_wrap(slice, 3); - pwm_set_chan_level(slice, PWM_CHAN_A, 0); // start with the buzzer disabled - pwm_set_enabled(slice, true); - + buzzer_init(); sleep_ms(1000); // I've found it helps if you wait a bit for everything to get going } -static inline void enable_buzzer() { - pwm_set_chan_level(pwm_gpio_to_slice_num(BUZZER), PWM_CHAN_A, 3); // 75% duty cycle -} - -static inline void disable_buzzer() { - pwm_set_chan_level(pwm_gpio_to_slice_num(BUZZER), PWM_CHAN_A, 0); // 0% duty cycle (disabled) -} - -// enable the buzzer, wait, then disable it again -static inline void toggle_buzzer(uint64_t time) { - enable_buzzer(); - sleep_ms(time); - disable_buzzer(); -} - - // convert the given string to morse code static size_t string_to_morse(morse_t* morse, char* string, size_t size) { size_t n = 0; @@ -355,12 +330,12 @@ static void play_morse(morse_t* morse, size_t size) { case DOT: putchar('.'); - toggle_buzzer(UNIT_TIME); + buzzer_toggle(UNIT_TIME); break; case DASH: putchar('-'); - toggle_buzzer(UNIT_TIME * 3u); + buzzer_toggle(UNIT_TIME * 3u); break; } diff --git a/neopixel/CMakeLists.txt b/neopixel/CMakeLists.txt new file mode 100644 index 0000000..671e47b --- /dev/null +++ b/neopixel/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(neopixel neoPixel.c) + +target_link_libraries(neopixel pico_stdlib hardware_pio) + +pico_add_extra_outputs(neopixel) + diff --git a/neopixel/neoPixel.c b/neopixel/neoPixel.c new file mode 100644 index 0000000..97eb853 --- /dev/null +++ b/neopixel/neoPixel.c @@ -0,0 +1,50 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" +#include "ws2812.pio.h" + +static const unsigned int neopixel = 28u; + +static inline void init() { + stdio_init_all(); + uint offset = pio_add_program(pio0, &ws2812_program); + ws2812_program_init(pio0, 0, offset, neopixel, 800000, true); + + sleep_ms(1000); +} + +static inline void set_pixel(uint32_t pixel_grb) { + pio_sm_put_blocking(pio0, 0, pixel_grb * 256); +} + +static inline void set_pixel_rgb(uint8_t r, uint8_t g, uint8_t b) { + set_pixel((32 << 24) + ((uint32_t) r << 16) + ((uint32_t) g << 8) + ((uint32_t) b)); +} + +int main() { + init(); + + while (true) { + uint8_t r = 0u; + do { + set_pixel_rgb(r, 0u, 255u - r); + sleep_ms(100); + } while (++r > 0u); + + uint8_t g = 0u; + do { + set_pixel_rgb(255u - g, g, 0u); + sleep_ms(100); + } while (++g > 0u); + + uint8_t b = 0u; + do { + set_pixel_rgb(0u, 0u, b); + sleep_ms(100); + } while (++b > 0u); + } +} + diff --git a/neopixel/ws2812.pio.h b/neopixel/ws2812.pio.h new file mode 100644 index 0000000..8cbeea6 --- /dev/null +++ b/neopixel/ws2812.pio.h @@ -0,0 +1,114 @@ +// -------------------------------------------------- // +// 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_T1 2 +#define ws2812_T2 5 +#define ws2812_T3 3 + +static const uint16_t ws2812_program_instructions[] = { + // .wrap_target + 0x6221, // 0: out x, 1 side 0 [2] + 0x1123, // 1: jmp !x, 3 side 1 [1] + 0x1400, // 2: jmp 0 side 1 [4] + 0xa442, // 3: nop side 0 [4] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program ws2812_program = { + .instructions = ws2812_program_instructions, + .length = 4, + .origin = -1, +}; + +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_T1 2 +#define ws2812_parallel_T2 5 +#define ws2812_parallel_T3 3 + +static const uint16_t ws2812_parallel_program_instructions[] = { + // .wrap_target + 0x6020, // 0: out x, 32 + 0xa10b, // 1: mov pins, !null [1] + 0xa401, // 2: mov pins, x [4] + 0xa103, // 3: mov pins, null [1] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program ws2812_parallel_program = { + .instructions = ws2812_parallel_program_instructions, + .length = 4, + .origin = -1, +}; + +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<pin_base+pin_count; i++) { + pio_gpio_init(pio, i); + } + pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true); + pio_sm_config c = ws2812_parallel_program_get_default_config(offset); + sm_config_set_out_shift(&c, true, true, 32); + sm_config_set_out_pins(&c, pin_base, pin_count); + sm_config_set_set_pins(&c, pin_base, pin_count); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_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 + -- GitLab