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