Skip to content
Snippets Groups Projects
Commit 09aa04b3 authored by William Grant's avatar William Grant
Browse files

morse code generator

parent aeb22d50
Branches
No related tags found
No related merge requests found
......@@ -8,4 +8,5 @@ set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(rios)
add_subdirectory(morse)
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()
#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);
}
}
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)
......
#include <stdio.h>
#include <stdbool.h>
#include "pico/stdlib.h"
#include "rios.h"
......@@ -6,6 +7,7 @@ static uint32_t task1(uint32_t state) {
printf("[T1 %d<", state);
sleep_ms(20);
printf(">T1 %d]", state);
return ++state;
}
......@@ -13,6 +15,7 @@ static uint32_t task2(uint32_t state) {
printf("[T2 %d<", state);
sleep_ms(600);
printf(">T2 %d]", state);
return ++state;
}
......@@ -20,20 +23,25 @@ static uint32_t task3(uint32_t state) {
printf("[T3 %d<", state);
sleep_ms(2000);
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;
}
......@@ -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;
}
static inline void disable_irq(void) {
irq_set_enabled(irq_num, false);
return x;
}
static void scheduler_isr(void) {
static int64_t scheduler_isr(alarm_id_t id, void* data) {
printf("running scheduler\n");
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);
}
......@@ -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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment