From 09aa04b3c7419921a8514c8ad21ba02fd3a7ba70 Mon Sep 17 00:00:00 2001 From: William Grant <williamgrantuk@gmail.com> Date: Sat, 13 May 2023 10:13:48 +0100 Subject: [PATCH] morse code generator --- CMakeLists.txt | 1 + morse/CMakeLists.txt | 14 ++ morse/morse.c | 412 +++++++++++++++++++++++++++++++++++++++++++ rios/CMakeLists.txt | 2 +- rios/main.c | 36 ++-- rios/rios.c | 54 ++++-- rios/rios.h | 2 - 7 files changed, 485 insertions(+), 36 deletions(-) create mode 100644 morse/CMakeLists.txt create mode 100644 morse/morse.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf669d..5145408 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,4 +8,5 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) add_subdirectory(rios) +add_subdirectory(morse) diff --git a/morse/CMakeLists.txt b/morse/CMakeLists.txt new file mode 100644 index 0000000..894e753 --- /dev/null +++ b/morse/CMakeLists.txt @@ -0,0 +1,14 @@ +if (TARGET tinyusb_device) + +add_executable(morse morse.c) +target_link_libraries(morse pico_stdlib hardware_pwm) + +pico_enable_stdio_usb(morse 1) +pico_enable_stdio_uart(morse 0) + +pico_add_extra_outputs(morse) + +elseif(PICO_ON_DEVICE) + message(WARNING "cannot build morse because TinyUSB submodule is not initialised in the SDK") +endif() + diff --git a/morse/morse.c b/morse/morse.c new file mode 100644 index 0000000..9c47f6c --- /dev/null +++ b/morse/morse.c @@ -0,0 +1,412 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include "pico/stdlib.h" +#include "hardware/pwm.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; + +typedef enum { + SHORT_SPACE, // the space between letters, 7 time units long + LONG_SPACE, // the space between words, 3 time units long + DOT, // a dot, 1 time unit long + DASH // a dash, 3 time units long +} morse_t; + +// 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); + + 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; + for (size_t i = 0; i < size; ++i) { + switch (string[i]) { + case 'a': + case 'A': + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case 'b': + case 'B': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case 'c': + case 'C': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case 'd': + case 'D': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case 'e': + case 'E': + morse[n++] = DOT; + break; + + case 'f': + case 'F': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case 'g': + case 'G': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case 'h': + case 'H': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case 'i': + case 'I': + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case 'j': + case 'J': + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case 'k': + case 'K': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case 'l': + case 'L': + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case 'm': + case 'M': + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case 'n': + case 'N': + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case 'o': + case 'O': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case 'p': + case 'P': + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case 'q': + case 'Q': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case 'r': + case 'R': + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case 's': + case 'S': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case 't': + case 'T': + morse[n++] = DASH; + break; + + case 'u': + case 'U': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case 'v': + case 'V': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case 'w': + case 'W': + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case 'x': + case 'X': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case 'y': + case 'Y': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case 'z': + case 'Z': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case '1': + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case '2': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case '3': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + case '4': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DASH; + break; + + case '5': + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case '6': + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case '7': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case '8': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + morse[n++] = DOT; + break; + + case '9': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DOT; + break; + + case '0': + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + morse[n++] = DASH; + break; + + default: + break; // skip unknown charaters + } + + if (string[i] == ' ') { + // avoid double and leading spaces + if (n > 0u && morse[n - 1u] == SHORT_SPACE) { + morse[n - 1u] = LONG_SPACE; + } + } else { + morse[n++] = SHORT_SPACE; + } + } + + return n; +} + +// print the morse code to the terminal and play it with the buzzer +static void play_morse(morse_t* morse, size_t size) { + for (size_t i = 0; i < size; ++i) { + switch (morse[i]) { + case SHORT_SPACE: + putchar(' '); + sleep_ms(UNIT_TIME * 2u); + break; + + case LONG_SPACE: + printf(" // "); + sleep_ms(UNIT_TIME * 6u); + break; + + case DOT: + putchar('.'); + toggle_buzzer(UNIT_TIME); + break; + + case DASH: + putchar('-'); + toggle_buzzer(UNIT_TIME * 3u); + break; + } + + sleep_ms(UNIT_TIME); + } + + putchar('\n'); +} + +// get a line of input from the user +static size_t get_line(char* buf, size_t size) { + size_t i; + + printf(">> "); + for (i = 0; i < size; ++i) { + char c = getchar(); + if (c == EOF || c == '\r' || c == '\n') { + putchar('\n'); + buf[i] = '\0'; + break; + } else { + putchar(c); + buf[i] = c; + } + } + + return i; +} + +int main(void) { + setup(); + + char* char_buf = malloc(sizeof(char) * MAX_CHARS); + morse_t* morse_buf = malloc(sizeof(morse_t) * MORSE_SIZE); + if (char_buf == NULL || morse_buf == NULL) { + perror("could not allocate buffers"); + exit(1); + } + + while (true) { + // read a line from input + size_t char_size = get_line(char_buf, MAX_CHARS); + + // output line as morse code + size_t morse_size = string_to_morse(morse_buf, char_buf, char_size); + play_morse(morse_buf, morse_size); + } +} + diff --git a/rios/CMakeLists.txt b/rios/CMakeLists.txt index 1b2f2a2..afdcbe8 100644 --- a/rios/CMakeLists.txt +++ b/rios/CMakeLists.txt @@ -1,7 +1,7 @@ if (TARGET tinyusb_device) add_library(rios STATIC rios.c rios.h) -target_link_libraries(rios hardware_irq) +target_link_libraries(rios hardware_irq pico_stdlib) add_executable(rios_main main.c) target_link_libraries(rios_main rios pico_stdlib) diff --git a/rios/main.c b/rios/main.c index 1aaaf1d..d044fac 100644 --- a/rios/main.c +++ b/rios/main.c @@ -1,39 +1,47 @@ #include <stdio.h> +#include <stdbool.h> #include "pico/stdlib.h" #include "rios.h" static uint32_t task1(uint32_t state) { - printf("[T1%d<", state); + printf("[T1 %d<", state); sleep_ms(20); - printf(">T1%d]", state); + printf(">T1 %d]", state); + return ++state; } static uint32_t task2(uint32_t state) { - printf("[T2%d<", state); + printf("[T2 %d<", state); sleep_ms(600); - printf(">T2%d]", state); + printf(">T2 %d]", state); + return ++state; } static uint32_t task3(uint32_t state) { - printf("[T3%d<", state); + printf("[T3 %d<", state); sleep_ms(2000); - printf(">T3%d]", state); + printf(">T3 %d]", state); + return ++state; } -static inline void init(void) { +int main(void) { stdio_init_all(); - rios_init(); -} + sleep_ms(1000); + printf("stdio initialised\n"); -int main(void) { - init(); + rios_add_task(250u, 0u, task1); + rios_add_task(750u, 0u, task2); + rios_add_task(1500u, 0u, task3); + printf("tasks added\n"); - rios_add_task(29u, 0u, task1); - rios_add_task(77u, 0u, task2); - rios_add_task(162u, 0u, task3); rios_start(); + printf("rios started\n"); + + while (true) {} + + return 0; } diff --git a/rios/rios.c b/rios/rios.c index 0d39456..30cd346 100644 --- a/rios/rios.c +++ b/rios/rios.c @@ -2,6 +2,9 @@ #include <stdint.h> #include <stdbool.h> +#include <stdio.h> +#include "pico/stdlib.h" +#include "hardware/sync.h" #include "hardware/irq.h" typedef struct { @@ -13,7 +16,7 @@ typedef struct { } task_data_t; static const uint32_t tick_ms = 400u; // Real time between ticks in ms -static const uint32_t tasks_period_GCD = 25u; // Timer tick rate +static uint32_t tasks_period_GCD; // Timer tick rate static task_data_t tasks[RIOS_MAX_TASKS]; static uint32_t tasks_index = 0u; @@ -22,44 +25,45 @@ static uint32_t running_tasks[RIOS_MAX_TASKS+1] = {UINT32_MAX}; static uint32_t current_task = 0u; static const uint32_t idle_task = UINT32_MAX; -static const uint irq_num = 0u; - -static inline void enable_irq(void) { - irq_set_enabled(irq_num, true); +static uint32_t gcd(uint32_t x, uint32_t y) { + while (y != 0) { + uint32_t t = y; + y = x % y; + x = t; + } + return x; } -static inline void disable_irq(void) { - irq_set_enabled(irq_num, false); -} +static int64_t scheduler_isr(alarm_id_t id, void* data) { + printf("running scheduler\n"); -static void scheduler_isr(void) { for (uint32_t i = 0u; i < tasks_index; ++i) { + if ((tasks[i].elapsed_time >= tasks[i].period) && (running_tasks[current_task] > i) && (!tasks[i].running)) { - - disable_irq(); + + printf("running task %d, %d, %p\n", tasks[i].period, tasks[i].state, tasks[i].tick_func); + uint32_t flags = save_and_disable_interrupts(); tasks[i].elapsed_time = 0u; tasks[i].running = true; ++current_task; running_tasks[current_task] = i; - enable_irq(); + restore_interrupts(flags); tasks[i].state = tasks[i].tick_func(tasks[i].state); // execute tick - disable_irq(); + flags = save_and_disable_interrupts(); tasks[i].running = false; running_tasks[current_task] = idle_task; --current_task; - enable_irq(); + restore_interrupts(flags); } tasks[i].elapsed_time += tasks_period_GCD; } -} -void rios_init(void) { - irq_set_exclusive_handler(irq_num, scheduler_isr); - enable_irq(); + printf("scheduler finished\n"); + return tasks_period_GCD; // re-arm the alarm } void rios_add_task(uint32_t period, uint32_t initial_state, rios_task_t task) { @@ -69,11 +73,23 @@ void rios_add_task(uint32_t period, uint32_t initial_state, rios_task_t task) { .elapsed_time = period, .tick_func = task }; tasks[tasks_index] = task_data; + printf("task %d, %d, %p added\n", period, initial_state, task); + + // update the timer tick rate + if (tasks_index == 0u) { + tasks_period_GCD = period; + } else { + tasks_period_GCD = gcd(tasks_period_GCD, period); + } + printf("tasks_period_GCD: %d\n", tasks_period_GCD); ++tasks_index; } + void rios_start(void) { - enable_irq(); + alarm_pool_t* alarm_pool = alarm_pool_create(2, 16); + irq_set_priority(2, 0xc0); + alarm_pool_add_alarm_in_ms(alarm_pool, tasks_period_GCD, scheduler_isr, NULL, false); } diff --git a/rios/rios.h b/rios/rios.h index 696435d..7324189 100644 --- a/rios/rios.h +++ b/rios/rios.h @@ -6,8 +6,6 @@ typedef uint32_t (*rios_task_t)(uint32_t state); -void rios_init(void); - void rios_add_task(uint32_t period, uint32_t initial_state, rios_task_t task); void rios_start(void); -- GitLab