aboutsummaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2022-06-05 18:36:34 -0400
committerClyne Sullivan <clyne@bitgloo.com>2022-06-05 18:36:34 -0400
commit7fbb94cb15fdd20cd9ada11588568583128c77f4 (patch)
treeb774f823d35c9c77bf719d17a7dd70715effadce /source
parentfedc108c8f664b44ae1a4c0d6b6378584533c314 (diff)
move source to folder; openocd.sh programs directly
Diffstat (limited to 'source')
-rw-r--r--source/2048.c104
-rw-r--r--source/2048.h3
-rw-r--r--source/buttons.c58
-rw-r--r--source/buttons.h34
-rw-r--r--source/dogs.c356
-rw-r--r--source/dogs.h33
-rw-r--r--source/flapbird.c80
-rw-r--r--source/flapbird.h8
-rw-r--r--source/main.c150
-rw-r--r--source/osal.c467
-rw-r--r--source/osal.h754
11 files changed, 2047 insertions, 0 deletions
diff --git a/source/2048.c b/source/2048.c
new file mode 100644
index 0000000..ce73844
--- /dev/null
+++ b/source/2048.c
@@ -0,0 +1,104 @@
+#include "buttons.h"
+#include "dogs.h"
+
+static int values[16];
+
+void g2048_init()
+{
+ for (int i = 0; i < 16; i++)
+ values[i] = 0;
+}
+
+int g2048_loop()
+{
+ if ((button_state & BUTTON_JOYUP) == BUTTON_JOYUP) {
+ for (int x = 0; x < 3; x++) {
+ for (int y = 0; y < 3; y++) {
+
+ if (values[4 * y + x] != 0)
+ continue;
+ }
+ }
+
+ for (int y = 3; y > 0; y--) {
+ for (int x = 0; x < 4; x++) {
+ if (values[4 * y + x] != 0) {
+ if (values[4 * (y - 1) + x] == 0)
+ values[4 * (y - 1) + x] = values[4 * y + x], values[4 * y + x] = 0;
+ else if (values[4 * (y - 1) + x] == values[4 * y + x])
+ values[4 * (y - 1) + x]++, values[4 * y + x] = 0;
+ }
+ }
+ }
+
+ for (int i = 15; i >= 0; i--) {
+ if (values[i] == 0) {
+ values[i] = 1;
+ break;
+ }
+ }
+ } else if ((button_state & BUTTON_JOYDOWN) == BUTTON_JOYDOWN) {
+ for (int y = 0; y < 3; y++) {
+ for (int x = 0; x < 4; x++) {
+ if (values[4 * y + x] != 0) {
+ if (values[4 * (y + 1) + x] == 0)
+ values[4 * (y + 1) + x] = values[4 * y + x], values[4 * y + x] = 0;
+ else if (values[4 * (y + 1) + x] == values[4 * y + x])
+ values[4 * (y + 1) + x]++, values[4 * y + x] = 0;
+ }
+ }
+ }
+
+ for (int i = 15; i >= 0; i--) {
+ if (values[i] == 0) {
+ values[i] = 1;
+ break;
+ }
+ }
+ } else if ((button_state & BUTTON_JOYLEFT) == BUTTON_JOYLEFT) {
+ for (int x = 3; x > 0; x--) {
+ for (int y = 0; y < 4; y++) {
+ if (values[4 * y + x] != 0) {
+ if (values[4 * y + x - 1] == 0)
+ values[4 * y + x - 1] = values[4 * y + x], values[4 * y + x] = 0;
+ else if (values[4 * y + x - 1] == values[4 * y + x])
+ values[4 * y + x - 1]++, values[4 * y + x] = 0;
+ }
+ }
+ }
+
+ for (int i = 15; i >= 0; i--) {
+ if (values[i] == 0) {
+ values[i] = 1;
+ break;
+ }
+ }
+ } else if ((button_state & BUTTON_JOYRIGHT) == BUTTON_JOYRIGHT) {
+ for (int x = 0; x < 3; x++) {
+ for (int y = 0; y < 4; y++) {
+ if (values[4 * y + x] != 0) {
+ if (values[4 * y + x + 1] == 0)
+ values[4 * y + x + 1] = values[4 * y + x], values[4 * y + x] = 0;
+ else if (values[4 * y + x + 1] == values[4 * y + x])
+ values[4 * y + x + 1]++, values[4 * y + x] = 0;
+ }
+ }
+ }
+
+ for (int i = 15; i >= 0; i--) {
+ if (values[i] == 0) {
+ values[i] = 1;
+ break;
+ }
+ }
+ }
+
+ dogs_clear();
+ for (int i = 0; i < 16; i++) {
+ if (values[i] != 0)
+ draw_number(31 + 10 * (i % 4), 12 + 10 * (3 - (i / 4)), values[i]);
+ }
+ dogs_flush();
+
+ return 500;
+}
diff --git a/source/2048.h b/source/2048.h
new file mode 100644
index 0000000..b8bdf14
--- /dev/null
+++ b/source/2048.h
@@ -0,0 +1,3 @@
+void g2048_init();
+int g2048_loop();
+
diff --git a/source/buttons.c b/source/buttons.c
new file mode 100644
index 0000000..45ea7bd
--- /dev/null
+++ b/source/buttons.c
@@ -0,0 +1,58 @@
+/**
+ * buttons.c - Provides ability to read button state.
+ * Copyright (C) 2020 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * See the GNU General Public License for more details.
+ */
+
+#include "buttons.h"
+#include "hal.h"
+
+unsigned char button_state = 0;
+
+static void buttonStateHandler(void *arg)
+{
+ int pad = (unsigned int)arg & 0xFF;
+ int bit = 1 << ((unsigned int)arg >> 8);
+
+ if (!palReadPad(GPIOA, pad)) {
+ if ((button_state & bit) != bit)
+ button_state |= bit;
+ } else {
+ if ((button_state & bit) == bit)
+ button_state &= ~(bit);
+ }
+}
+
+void buttons_init()
+{
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_PULLUP); // Joy A (UL)
+ palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_PULLUP); // Joy B (UR)
+ palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_PULLUP); // Joy C (DR)
+ palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_PULLUP); // Joy D (DL)
+ palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_PULLUP); // Joy button
+ palSetPadMode(GPIOA, 7, PAL_MODE_INPUT_PULLUP); // Button 1
+ palSetPadMode(GPIOA, 9, PAL_MODE_INPUT_PULLUP); // Button 2
+ palSetPadMode(GPIOA, 10, PAL_MODE_INPUT_PULLUP); // Button 3
+ palEnablePadEvent(GPIOA, 0, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 1, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 2, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 3, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 6, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 7, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 9, PAL_EVENT_MODE_BOTH_EDGES);
+ palEnablePadEvent(GPIOA, 10, PAL_EVENT_MODE_BOTH_EDGES);
+ palSetPadCallback(GPIOA, 0, buttonStateHandler, (void *)0x0700);
+ palSetPadCallback(GPIOA, 1, buttonStateHandler, (void *)0x0601);
+ palSetPadCallback(GPIOA, 2, buttonStateHandler, (void *)0x0502);
+ palSetPadCallback(GPIOA, 3, buttonStateHandler, (void *)0x0403);
+ palSetPadCallback(GPIOA, 6, buttonStateHandler, (void *)0x0306);
+ palSetPadCallback(GPIOA, 7, buttonStateHandler, (void *)0x0207);
+ palSetPadCallback(GPIOA, 9, buttonStateHandler, (void *)0x0109);
+ palSetPadCallback(GPIOA, 10, buttonStateHandler, (void *)0x000A);
+}
+
diff --git a/source/buttons.h b/source/buttons.h
new file mode 100644
index 0000000..ba7e02d
--- /dev/null
+++ b/source/buttons.h
@@ -0,0 +1,34 @@
+/**
+ * buttons.h - Provides ability to read button state.
+ * Copyright (C) 2020 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * See the GNU General Public License for more details.
+ */
+
+#ifndef BUTTONS_H_
+#define BUTTONS_H_
+
+#define BUTTON_JOYUL (1 << 7)
+#define BUTTON_JOYUR (1 << 6)
+#define BUTTON_JOYDR (1 << 5)
+#define BUTTON_JOYDL (1 << 4)
+#define BUTTON_JOY (1 << 3)
+#define BUTTON_1 (1 << 2)
+#define BUTTON_2 (1 << 1)
+#define BUTTON_3 (1 << 0)
+
+#define BUTTON_JOYUP (BUTTON_JOYUL | BUTTON_JOYUR)
+#define BUTTON_JOYDOWN (BUTTON_JOYDL | BUTTON_JOYDR)
+#define BUTTON_JOYLEFT (BUTTON_JOYUL | BUTTON_JOYDL)
+#define BUTTON_JOYRIGHT (BUTTON_JOYUR | BUTTON_JOYDR)
+
+extern unsigned char button_state;
+
+void buttons_init();
+
+#endif // BUTTONS_H_
+
diff --git a/source/dogs.c b/source/dogs.c
new file mode 100644
index 0000000..b986845
--- /dev/null
+++ b/source/dogs.c
@@ -0,0 +1,356 @@
+/**
+ * dogs.c - Interface for drawing to the EADOGS102N-6 display.
+ * Copyright (C) 2020 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * See the GNU General Public License for more details.
+ */
+
+//#include "ch.h"
+#include "dogs.h"
+#include "hal.h"
+
+#define SET_DATA palSetPad(GPIOC, 15)
+#define SET_CMD palClearPad(GPIOC, 15)
+#define CS_HIGH palSetPad(GPIOA, 4)
+#define CS_LOW palClearPad(GPIOA, 4)
+unsigned char dogs_buffer[DISP_WIDTH * DISP_HEIGHT / 8];
+
+//static volatile bool dogs_spi_done = false;
+
+#define spi_send(data, len) spiSend(&SPID1, len, data)
+//static void spi_send(unsigned char *data, unsigned int len)
+//{
+ //dogs_spi_done = false;
+ //spiStartSend(&SPID1, len, data);
+ //while (!dogs_spi_done)
+ // asm("wfi");
+
+ //for (; len > 0; --len)
+ // spiPolledExchange(&SPID1, *data++);
+//}
+
+static void dogs_init_display();
+//static void dogs_spi_callback(SPIDriver *spid)
+//{
+// if (spiIsBufferComplete(spid))
+// dogs_spi_done = true;
+//}
+
+void dogs_init()
+{
+ // SPI
+ palSetPadMode(GPIOB, 1, PAL_MODE_ALTERNATE(1)); // MOSI
+ palSetPadMode(GPIOA, 5, PAL_MODE_ALTERNATE(0)); // CLK
+ palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL); // nCS
+ palSetPadMode(GPIOC, 14, PAL_MODE_OUTPUT_PUSHPULL); // nRST
+ palSetPadMode(GPIOC, 15, PAL_MODE_OUTPUT_PUSHPULL); // CD
+
+ palSetPad(GPIOA, 4);
+ palSetPad(GPIOC, 14);
+ palClearPad(GPIOC, 15);
+
+ static const SPIConfig spicfg = {
+ false,
+ NULL /*dogs_spi_callback*/,
+ 0, // cr1
+ 0
+ };
+ spiStart(&SPID1, &spicfg);
+
+ dogs_init_display();
+ dogs_clear();
+ dogs_flush();
+}
+
+void dogs_write_cmd(unsigned char byte)
+{
+ spi_send(&byte, 1);
+}
+
+void dogs_set_column(unsigned int col)
+{
+ dogs_write_cmd(0x10 | ((col >> 4) & 0xF));
+ dogs_write_cmd(0x00 | (col & 0xF));
+}
+void dogs_set_power(unsigned int bits)
+{
+ dogs_write_cmd(0x28 | (bits & 0x7));
+}
+void dogs_set_scroll_line(unsigned int startline)
+{
+ dogs_write_cmd(0x40 | (startline & 0x3F));
+}
+void dogs_set_page(unsigned int page)
+{
+ dogs_write_cmd(0xB0 | (page & 0xF));
+}
+void dogs_set_vlcd_ratio(unsigned int ratio)
+{
+ dogs_write_cmd(0x20 | (ratio & 0x7));
+}
+void dogs_set_contrast(unsigned int value)
+{
+ dogs_write_cmd(0x81);
+ dogs_write_cmd(0x00 | (value & 0x3F));
+}
+void dogs_set_pixelson(bool on)
+{
+ dogs_write_cmd(on ? 0xA5 : 0xA4);
+}
+void dogs_set_invert(bool invert)
+{
+ dogs_write_cmd(invert ? 0xA7 : 0xA6);
+}
+void dogs_set_sleep(bool sleep)
+{
+ dogs_write_cmd(sleep ? 0xAE : 0xAF);
+}
+void dogs_set_segdir(bool normal)
+{
+ dogs_write_cmd(normal ? 0xA0 : 0xA1);
+}
+void dogs_set_comdir(bool normal)
+{
+ dogs_write_cmd(normal ? 0xC0 : 0xC8);
+}
+void dogs_reset()
+{
+ dogs_write_cmd(0xE2);
+}
+void dogs_set_bias(bool higher)
+{
+ dogs_write_cmd(higher ? 0xA2 : 0xA3);
+}
+void dogs_set_advanced(unsigned int bits)
+{
+ dogs_write_cmd(0xFA);
+ dogs_write_cmd(0x10 | bits);
+}
+
+void dogs_init_display()
+{
+ SET_CMD;
+ CS_LOW;
+ dogs_reset();
+ CS_HIGH;
+
+ unsigned long int reset_sleep = (STM32_SYSCLK / 1000) * 100;
+ while (reset_sleep != 0) {
+ asm("nop; nop; nop; nop; nop");
+ reset_sleep -= 8;
+ }
+
+ CS_LOW;
+ dogs_set_scroll_line(0);
+ dogs_set_segdir(true);
+ dogs_set_comdir(false);
+ dogs_set_pixelson(false);
+ dogs_set_invert(false);
+ dogs_set_bias(true);
+ dogs_set_power(0x07);
+ dogs_set_vlcd_ratio(7);
+ dogs_set_contrast(12);
+ dogs_set_advanced(0x83);
+ dogs_set_sleep(false);
+ CS_HIGH;
+}
+
+void dogs_clear()
+{
+ uint32_t *ptr = (uint32_t *)dogs_buffer;
+ unsigned int count = sizeof(dogs_buffer) / sizeof(uint32_t) / 12;
+
+ for (; count; --count) {
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ }
+}
+
+void dogs_flush()
+{
+ unsigned char *ptr = dogs_buffer;
+ CS_LOW;
+ for (int page = 0; page < 8; ++page) {
+ SET_CMD;
+ dogs_set_page(page);
+ dogs_set_column(30);
+
+ SET_DATA;
+ spi_send(ptr, 102);
+ ptr += 102;
+ }
+ CS_HIGH;
+}
+
+void draw_pixel(int x, int y, bool state)
+{
+ if (x < 0 || y < 0 || x >= DISP_WIDTH || y >= DISP_HEIGHT)
+ return;
+ y = DISP_HEIGHT - 1 - y;
+ if (state)
+ dogs_buffer[y / 8 * DISP_WIDTH + x] |= (1 << (y % 8));
+ else
+ dogs_buffer[y / 8 * DISP_WIDTH + x] &= ~(1 << (y % 8));
+}
+
+void draw_rect(int x, int y, int w, int h)
+{
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++)
+ draw_pixel(x + i, y + j, true);
+ }
+}
+
+void draw_bitmap(int x, int y, const unsigned char *buffer)
+{
+ // Prepare source information
+ const unsigned char *src = buffer;
+ const int width = *src++;
+ const int height = *src++;
+ int sbit = 0;
+
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ draw_pixel(x + i, y + j, *src & (1 << sbit));
+ if (++sbit == 8) {
+ sbit = 0;
+ ++src;
+ }
+ }
+ }
+}
+
+static const unsigned char draw_number_bitmaps[10][10] = {
+ { 8, 8, // '0'
+ 0b00011000,
+ 0b00100100,
+ 0b01000010,
+ 0b01000010,
+ 0b01000010,
+ 0b01000010,
+ 0b00100100,
+ 0b00011000,
+ },
+ { 8, 8, // '1'
+ 0b01111100,
+ 0b00010000,
+ 0b00010000,
+ 0b00010000,
+ 0b00010000,
+ 0b00010000,
+ 0b00010100,
+ 0b00011000,
+ },
+ { 8, 8, // '2'
+ 0b01111110,
+ 0b00001000,
+ 0b00010000,
+ 0b00100000,
+ 0b01000000,
+ 0b01000010,
+ 0b00100100,
+ 0b00011000,
+ },
+ { 8, 8, // '3'
+ 0b00011100,
+ 0b00100010,
+ 0b01000000,
+ 0b00100000,
+ 0b00111100,
+ 0b01000000,
+ 0b00100010,
+ 0b00011100,
+ },
+ { 8, 8, // '4'
+ 0b00100000,
+ 0b00100000,
+ 0b00100000,
+ 0b00111110,
+ 0b00100100,
+ 0b00101000,
+ 0b00110000,
+ 0b00100000,
+ },
+ { 8, 8, // '5'
+ 0b00011100,
+ 0b00100010,
+ 0b01000000,
+ 0b01000000,
+ 0b00100000,
+ 0b00011110,
+ 0b00000010,
+ 0b01111110,
+ },
+ { 8, 8, // '6'
+ 0b00011000,
+ 0b00100100,
+ 0b01000010,
+ 0b01000110,
+ 0b00111010,
+ 0b00000010,
+ 0b00000100,
+ 0b00111000,
+ },
+ { 8, 8, // '7'
+ 0b00001000,
+ 0b00001000,
+ 0b00001000,
+ 0b00001000,
+ 0b00010000,
+ 0b00100000,
+ 0b01000000,
+ 0b01111110,
+ },
+ { 8, 8, // '8'
+ 0b00011000,
+ 0b00100100,
+ 0b01000010,
+ 0b00100100,
+ 0b00111100,
+ 0b01000010,
+ 0b00100100,
+ 0b00011000,
+ },
+ { 8, 8, // '9'
+ 0b00011100,
+ 0b00100010,
+ 0b01000000,
+ 0b01111000,
+ 0b01000100,
+ 0b01000010,
+ 0b00100100,
+ 0b00011000,
+ },
+};
+
+void draw_number(int x, int y, int number)
+{
+ if (number < 0)
+ number = -number;
+ int tmp = number;
+ int count;
+ for (count = 0; tmp; count++)
+ tmp /= 10;
+ if (count == 0)
+ count = 1;
+ x += count * 8;
+ do {
+ x -= 8;
+ draw_bitmap(x, y, draw_number_bitmaps[number % 10]);
+ } while (number /= 10);
+}
+
diff --git a/source/dogs.h b/source/dogs.h
new file mode 100644
index 0000000..8af24fd
--- /dev/null
+++ b/source/dogs.h
@@ -0,0 +1,33 @@
+/**
+ * dogs.h - Interface for drawing to the EADOGS102N-6 display.
+ * Copyright (C) 2020 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * See the GNU General Public License for more details.
+ */
+
+#ifndef DOGS_H_
+#define DOGS_H_
+
+#include <stdbool.h>
+
+#define DISP_WIDTH 102
+#define DISP_HEIGHT 64
+
+extern unsigned char dogs_buffer[DISP_WIDTH * DISP_HEIGHT / 8];
+
+void dogs_init();
+void dogs_clear();
+void dogs_flush();
+void dogs_set_sleep(bool sleep);
+
+void draw_pixel(int x, int y, bool state);
+void draw_rect(int x, int y, int w, int h);
+void draw_bitmap(int x, int y, const unsigned char *buffer);
+void draw_number(int x, int y, int number);
+
+#endif // DOGS_H_
+
diff --git a/source/flapbird.c b/source/flapbird.c
new file mode 100644
index 0000000..f2dd2cf
--- /dev/null
+++ b/source/flapbird.c
@@ -0,0 +1,80 @@
+#include "buttons.h"
+#include "dogs.h"
+
+static const unsigned char bird[] = {
+ 8, 8,
+ 0b00111100,
+ 0b01100110,
+ 0b01000010,
+ 0b01111110,
+ 0b01100110,
+ 0b01000010,
+ 0b11000000,
+ 0b11000000,
+};
+
+static int score;
+static int t1x, t1o;
+static int t2x, t2o;
+static int py;
+static int vy;
+static int counter;
+
+void flapbird_init()
+{
+ score = 0;
+ t1x = DISP_WIDTH / 2, t1o = 15;
+ t2x = DISP_WIDTH, t2o = 49;
+ py = DISP_HEIGHT / 2 - 4;
+ vy = 0;
+ counter = 0;
+}
+
+int flapbird_loop()
+{
+ // Player logic
+ if (py > 0) {
+ py += vy;
+ if (vy > -3)
+ vy--;
+ } else {
+ if (py < 0)
+ py = 0;
+ if (score > 0)
+ score = 0;
+ }
+
+ if (button_state & BUTTON_2) {
+ vy = 5;
+ if (py <= 0)
+ py = 1;
+ }
+
+ // Rendering
+ dogs_clear();
+
+ draw_rect(t1x, 0, 4, t1o - 10);
+ draw_rect(t1x, t1o + 10, 4, DISP_HEIGHT - t1o + 10);
+ draw_rect(t2x, 0, 4, t2o - 10);
+ draw_rect(t2x, t2o + 10, 4, DISP_HEIGHT - t2o + 10);
+ draw_bitmap(4, py, bird);
+
+ draw_number(DISP_WIDTH - 25, DISP_HEIGHT - 8, score);
+ dogs_flush();
+
+ // Game logic
+ if (t1x == 4)
+ score = (py + 2 > t1o - 10 && py + 6 < t1o + 10) ? score + 1 : 0;
+ if (t2x == 4)
+ score = (py + 2 > t2o - 10 && py + 6 < t2o + 10) ? score + 1 : 0;
+
+ t1x -= 2;
+ if (t1x <= -5)
+ t1x = DISP_WIDTH;
+ t2x -= 2;
+ if (t2x <= -5)
+ t2x = DISP_WIDTH;
+
+ return 100;
+}
+
diff --git a/source/flapbird.h b/source/flapbird.h
new file mode 100644
index 0000000..7ab4199
--- /dev/null
+++ b/source/flapbird.h
@@ -0,0 +1,8 @@
+#ifndef FLAPBIRD_H_
+#define FLAPBIRD_H_
+
+void flapbird_init();
+int flapbird_loop();
+
+#endif // FLAPBIRD_H_
+
diff --git a/source/main.c b/source/main.c
new file mode 100644
index 0000000..fd753ea
--- /dev/null
+++ b/source/main.c
@@ -0,0 +1,150 @@
+/**
+ * main.c - Firmware entry point and main loop for game or testing.
+ * Copyright (C) 2020 Clyne Sullivan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * See the GNU General Public License for more details.
+ */
+
+#include "buttons.h"
+#include "dogs.h"
+#include "hal.h"
+
+//#include "2048.h"
+#include "flapbird.h"
+
+/*
+ * Progress:
+ * - Serial through LPUART1 works (38400 baud, takes over swdio pins)
+ * - Display comm. over SPI, can clear screen
+ * - Can read buttons through PAL (through interrupts now)
+ * - Use ADC to read Vintref, print to screen in mV
+ * - Sleep mode via WFI, saves ~0.5mA (we're running around 1.1mA)
+ * - Run at 512kHz, only use HSI for ADC: 360uA (jumpy)
+ * - Drop to 1.2V Vcore (range 3), enable low-V detector: 375uA (steady) (440uA at 1MHz)
+ * - Run at 4MHz, drop to low-power run/sleep @ 64kHz for idle: 375uA (also lowered contrast)
+ * - Sleep display for 'pause': ~240uA
+ *
+ * - Use RTC for delay, Stop mode when idle: 348uA
+ *
+ * - Flappy bird is going, 2048 next
+ */
+
+static int readVddmv();
+
+static void alarm_callback(RTCDriver *rtcp, rtcevent_t event)
+{
+ (void)rtcp;
+ (void)event;
+
+ static bool sleep = false;
+
+ bool sleep_button = (button_state & BUTTON_1) != 0;
+ if (sleep && !sleep_button)
+ return;
+
+ RCC->ICSCR |= 6 << RCC_ICSCR_MSIRANGE_Pos;
+ dogs_set_sleep(false);
+
+ if (sleep_button) {
+ sleep ^= true;
+ if (sleep) {
+ draw_number(DISP_WIDTH - 33, 0,
+ !(PWR->CSR & PWR_CSR_PVDO) ? readVddmv() : 1);
+ dogs_flush();
+ }
+ }
+
+ if (!sleep)
+ flapbird_loop();
+
+ dogs_set_sleep(true);
+}
+
+int main(void)
+{
+ halInit();
+ //chSysInit();
+ buttons_init();
+
+ dogs_init();
+ flapbird_init();
+
+ static const RTCWakeup wakeupcfg = {
+ (0 << 16) | // wucksel (37k /16 = ~2k)
+ 200 // wut (hope for 10Hz)
+ };
+ rtcSTM32SetPeriodicWakeup(&RTCD1, &wakeupcfg);
+ rtcSetCallback(&RTCD1, alarm_callback);
+
+ RCC->CR &= ~RCC_CR_HSION;
+ PWR->CR |= PWR_CR_LPSDSR | PWR_CR_ULP;
+ PWR->CR |= PWR_CR_LPRUN;
+ SCB->SCR = 6;
+ //FLASH->ACR |= FLASH_ACR_SLEEP_PD;
+
+ // Below code for serial -- note that it cuts off debugging, and MUST be used in a thread
+ //chThdSleepMilliseconds(2000);
+ //palSetPadMode(GPIOA, 13, PAL_MODE_ALTERNATE(6));
+ //palSetPadMode(GPIOA, 14, PAL_MODE_ALTERNATE(6));
+ //sdStart(&LPSD1, NULL);
+ //chnWrite(&LPSD1, (const uint8_t *)"Hello World!\r\n", 14);
+
+ /* This is now the idle thread loop, you may perform here a low priority
+ task but YOU MUST NEVER TRY TO SLEEP OR WAIT in this loop. Note that
+ this tasks runs at the lowest priority level so any instruction added
+ here will be executed after all other tasks have been started. */
+ while (1)
+ asm("wfe");
+}
+
+void HardFault_Handler()
+{
+ while (1);
+}
+
+static volatile bool adc_is_complete = false;
+static void adc_callback(ADCDriver *adcd)
+{
+ (void)adcd;
+ adc_is_complete = true;
+}
+
+static const ADCConfig adccfg = {
+ .dummy = 0
+};
+
+static const ADCConversionGroup adcgrpcfg = {
+ .circular = false,
+ .num_channels = 1,
+ .end_cb = adc_callback,
+ .error_cb = NULL,
+ .cfgr1 = ADC_CFGR1_RES_12BIT, /* CFGR1 */
+ .cfgr2 = 0, /* CFGR2 */
+ .tr = ADC_TR(0, 0), /* TR */
+ .smpr = ADC_SMPR_SMP_1P5, /* SMPR */
+ .chselr = ADC_CHSELR_CHSEL17 /* CHSELR */
+};
+
+int readVddmv()
+{
+ adcsample_t reading = 0;
+
+ RCC->CR |= RCC_CR_HSION;
+ while (!(RCC->CR & RCC_CR_HSIRDY));
+
+ adcStart(&ADCD1, &adccfg);
+ adcSTM32EnableVREF(&ADCD1);
+ adcStartConversion(&ADCD1, &adcgrpcfg, &reading, 1);
+ while (!adc_is_complete);
+ adcStopConversion(&ADCD1);
+ adcSTM32DisableVREF(&ADCD1);
+ adcStop(&ADCD1);
+
+ RCC->CR &= ~RCC_CR_HSION;
+
+ return 3000 * /* CAL */ *((adcsample_t *)0x1FF80078) / reading;
+}
diff --git a/source/osal.c b/source/osal.c
new file mode 100644
index 0000000..5c62069
--- /dev/null
+++ b/source/osal.c
@@ -0,0 +1,467 @@
+/*
+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file osal.c
+ * @brief OSAL module code.
+ *
+ * @addtogroup OSAL
+ * @{
+ */
+
+#include "osal.h"
+#include "osal_vt.h"
+
+/*===========================================================================*/
+/* Module local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Module exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief Pointer to a halt error message.
+ * @note The message is meant to be retrieved by the debugger after the
+ * system halt caused by an unexpected error.
+ */
+const char *osal_halt_msg;
+
+/*===========================================================================*/
+/* Module local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Module local variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Module local functions. */
+/*===========================================================================*/
+
+static void callback_timeout(void *p) {
+ osalSysLockFromISR();
+ osalThreadResumeI((thread_reference_t *)p, MSG_TIMEOUT);
+ osalSysUnlockFromISR();
+}
+
+/*===========================================================================*/
+/* Module exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief OSAL module initialization.
+ *
+ * @api
+ */
+void osalInit(void) {
+
+ vtInit();
+
+ OSAL_INIT_HOOK();
+}
+
+/**
+ * @brief System halt with error message.
+ *
+ * @param[in] reason the halt message pointer
+ *
+ * @api
+ */
+#if !defined(__DOXYGEN__)
+__attribute__((weak, noreturn))
+#endif
+void osalSysHalt(const char *reason) {
+
+ osalSysDisable();
+ osal_halt_msg = reason;
+ while (true) {
+ }
+}
+
+/**
+ * @brief Polled delay.
+ * @note The real delay is always few cycles in excess of the specified
+ * value.
+ *
+ * @param[in] cycles number of cycles
+ *
+ * @xclass
+ */
+void osalSysPolledDelayX(rtcnt_t cycles) {
+
+ (void)cycles;
+}
+
+/**
+ * @brief System timer handler.
+ * @details The handler is used for scheduling and Virtual Timers management.
+ *
+ * @iclass
+ */
+void osalOsTimerHandlerI(void) {
+
+ osalDbgCheckClassI();
+
+ vtDoTickI();
+}
+
+/**
+ * @brief Checks if a reschedule is required and performs it.
+ * @note I-Class functions invoked from thread context must not reschedule
+ * by themselves, an explicit reschedule using this function is
+ * required in this scenario.
+ * @note Not implemented in this simplified OSAL.
+ *
+ * @sclass
+ */
+void osalOsRescheduleS(void) {
+
+}
+
+/**
+ * @brief Current system time.
+ * @details Returns the number of system ticks since the @p osalInit()
+ * invocation.
+ * @note The counter can reach its maximum and then restart from zero.
+ * @note This function can be called from any context but its atomicity
+ * is not guaranteed on architectures whose word size is less than
+ * @p systime_t size.
+ *
+ * @return The system time in ticks.
+ *
+ * @xclass
+ */
+systime_t osalOsGetSystemTimeX(void) {
+
+ return vtlist.vt_systime;
+}
+
+/**
+ * @brief Suspends the invoking thread for the specified time.
+ *
+ * @param[in] time the delay in system ticks, the special values are
+ * handled as follow:
+ * - @a TIME_INFINITE is allowed but interpreted as a
+ * normal time specification.
+ * - @a TIME_IMMEDIATE this value is not allowed.
+ * .
+ *
+ * @sclass
+ */
+void osalThreadSleepS(sysinterval_t time) {
+ virtual_timer_t vt;
+ thread_reference_t tr;
+
+ tr = NULL;
+ vtSetI(&vt, time, callback_timeout, (void *)&tr);
+ osalThreadSuspendS(&tr);
+}
+
+/**
+ * @brief Suspends the invoking thread for the specified time.
+ *
+ * @param[in] time the delay in system ticks, the special values are
+ * handled as follow:
+ * - @a TIME_INFINITE is allowed but interpreted as a
+ * normal time specification.
+ * - @a TIME_IMMEDIATE this value is not allowed.
+ * .
+ *
+ * @api
+ */
+void osalThreadSleep(sysinterval_t time) {
+
+ osalSysLock();
+ osalThreadSleepS(time);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Sends the current thread sleeping and sets a reference variable.
+ * @note This function must reschedule, it can only be called from thread
+ * context.
+ *
+ * @param[in] trp a pointer to a thread reference object
+ * @return The wake up message.
+ *
+ * @sclass
+ */
+msg_t osalThreadSuspendS(thread_reference_t *trp) {
+ thread_t self = {MSG_WAIT};
+
+ osalDbgCheck(trp != NULL);
+
+ *trp = &self;
+ while (self.message == MSG_WAIT) {
+ osalSysUnlock();
+ /* A state-changing interrupt could occur here and cause the loop to
+ terminate, an hook macro is executed while waiting.*/
+ OSAL_IDLE_HOOK();
+ osalSysLock();
+ }
+
+ return self.message;
+}
+
+/**
+ * @brief Sends the current thread sleeping and sets a reference variable.
+ * @note This function must reschedule, it can only be called from thread
+ * context.
+ *
+ * @param[in] trp a pointer to a thread reference object
+ * @param[in] timeout the timeout in system ticks, the special values are
+ * handled as follow:
+ * - @a TIME_INFINITE the thread enters an infinite sleep
+ * state.
+ * - @a TIME_IMMEDIATE the thread is not enqueued and
+ * the function returns @p MSG_TIMEOUT as if a timeout
+ * occurred.
+ * .
+ * @return The wake up message.
+ * @retval MSG_TIMEOUT if the operation timed out.
+ *
+ * @sclass
+ */
+msg_t osalThreadSuspendTimeoutS(thread_reference_t *trp, sysinterval_t timeout) {
+ msg_t msg;
+ virtual_timer_t vt;
+
+ osalDbgCheck(trp != NULL);
+
+ if (TIME_INFINITE == timeout)
+ return osalThreadSuspendS(trp);
+
+ vtSetI(&vt, timeout, callback_timeout, (void *)trp);
+ msg = osalThreadSuspendS(trp);
+ if (vtIsArmedI(&vt))
+ vtResetI(&vt);
+
+ return msg;
+}
+
+/**
+ * @brief Wakes up a thread waiting on a thread reference object.
+ * @note This function must not reschedule because it can be called from
+ * ISR context.
+ *
+ * @param[in] trp a pointer to a thread reference object
+ * @param[in] msg the message code
+ *
+ * @iclass
+ */
+void osalThreadResumeI(thread_reference_t *trp, msg_t msg) {
+
+ osalDbgCheck(trp != NULL);
+
+ if (*trp != NULL) {
+ (*trp)->message = msg;
+ *trp = NULL;
+ }
+}
+
+/**
+ * @brief Wakes up a thread waiting on a thread reference object.
+ * @note This function must reschedule, it can only be called from thread
+ * context.
+ *
+ * @param[in] trp a pointer to a thread reference object
+ * @param[in] msg the message code
+ *
+ * @iclass
+ */
+void osalThreadResumeS(thread_reference_t *trp, msg_t msg) {
+
+ osalDbgCheck(trp != NULL);
+
+ if (*trp != NULL) {
+ (*trp)->message = msg;
+ *trp = NULL;
+ }
+}
+
+/**
+ * @brief Enqueues the caller thread.
+ * @details The caller thread is enqueued and put to sleep until it is
+ * dequeued or the specified timeouts expires.
+ *
+ * @param[in] tqp pointer to the threads queue object
+ * @param[in] timeout the timeout in system ticks, the special values are
+ * handled as follow:
+ * - @a TIME_INFINITE the thread enters an infinite sleep
+ * state.
+ * - @a TIME_IMMEDIATE the thread is not enqueued and
+ * the function returns @p MSG_TIMEOUT as if a timeout
+ * occurred.
+ * .
+ * @return The message from @p osalQueueWakeupOneI() or
+ * @p osalQueueWakeupAllI() functions.
+ * @retval MSG_TIMEOUT if the thread has not been dequeued within the
+ * specified timeout or if the function has been
+ * invoked with @p TIME_IMMEDIATE as timeout
+ * specification.
+ *
+ * @sclass
+ */
+msg_t osalThreadEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout) {
+ msg_t msg;
+ virtual_timer_t vt;
+
+ osalDbgCheck(tqp != NULL);
+
+ if (TIME_IMMEDIATE == timeout)
+ return MSG_TIMEOUT;
+
+ tqp->tr = NULL;
+
+ if (TIME_INFINITE == timeout)
+ return osalThreadSuspendS(&tqp->tr);
+
+ vtSetI(&vt, timeout, callback_timeout, (void *)&tqp->tr);
+ msg = osalThreadSuspendS(&tqp->tr);
+ if (vtIsArmedI(&vt))
+ vtResetI(&vt);
+
+ return msg;
+}
+
+/**
+ * @brief Dequeues and wakes up one thread from the queue, if any.
+ *
+ * @param[in] tqp pointer to the threads queue object
+ * @param[in] msg the message code
+ *
+ * @iclass
+ */
+void osalThreadDequeueNextI(threads_queue_t *tqp, msg_t msg) {
+
+ osalDbgCheck(tqp != NULL);
+
+ osalThreadResumeI(&tqp->tr, msg);
+}
+
+/**
+ * @brief Dequeues and wakes up all threads from the queue.
+ *
+ * @param[in] tqp pointer to the threads queue object
+ * @param[in] msg the message code
+ *
+ * @iclass
+ */
+void osalThreadDequeueAllI(threads_queue_t *tqp, msg_t msg) {
+
+ osalDbgCheck(tqp != NULL);
+
+ osalThreadResumeI(&tqp->tr, msg);
+}
+
+/**
+ * @brief Add flags to an event source object.
+ *
+ * @param[in] esp pointer to the event flags object
+ * @param[in] flags flags to be ORed to the flags mask
+ *
+ * @iclass
+ */
+void osalEventBroadcastFlagsI(event_source_t *esp, eventflags_t flags) {
+
+ osalDbgCheck(esp != NULL);
+
+ esp->flags |= flags;
+ if (esp->cb != NULL) {
+ esp->cb(esp);
+ }
+}
+
+/**
+ * @brief Add flags to an event source object.
+ *
+ * @param[in] esp pointer to the event flags object
+ * @param[in] flags flags to be ORed to the flags mask
+ *
+ * @iclass
+ */
+void osalEventBroadcastFlags(event_source_t *esp, eventflags_t flags) {
+
+ osalDbgCheck(esp != NULL);
+
+ osalSysLock();
+ osalEventBroadcastFlagsI(esp, flags);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Event callback setup.
+ * @note The callback is invoked from ISR context and can
+ * only invoke I-Class functions. The callback is meant
+ * to wakeup the task that will handle the event by
+ * calling @p osalEventGetAndClearFlagsI().
+ * @note This function is not part of the OSAL API and is provided
+ * exclusively as an example and for convenience.
+ *
+ * @param[in] esp pointer to the event flags object
+ * @param[in] cb pointer to the callback function
+ * @param[in] param parameter to be passed to the callback function
+ *
+ * @api
+ */
+void osalEventSetCallback(event_source_t *esp,
+ eventcallback_t cb,
+ void *param) {
+
+ osalDbgCheck(esp != NULL);
+
+ esp->cb = cb;
+ esp->param = param;
+}
+
+/**
+ * @brief Locks the specified mutex.
+ * @post The mutex is locked and inserted in the per-thread stack of owned
+ * mutexes.
+ *
+ * @param[in,out] mp pointer to the @p mutex_t object
+ *
+ * @api
+ */
+void osalMutexLock(mutex_t *mp) {
+
+ osalDbgCheck(mp != NULL);
+
+ *mp = 1;
+}
+
+/**
+ * @brief Unlocks the specified mutex.
+ * @note The HAL guarantees to release mutex in reverse lock order. The
+ * mutex being unlocked is guaranteed to be the last locked mutex
+ * by the invoking thread.
+ * The implementation can rely on this behavior and eventually
+ * ignore the @p mp parameter which is supplied in order to support
+ * those OSes not supporting a stack of the owned mutexes.
+ *
+ * @param[in,out] mp pointer to the @p mutex_t object
+ *
+ * @api
+ */
+void osalMutexUnlock(mutex_t *mp) {
+
+ osalDbgCheck(mp != NULL);
+
+ *mp = 0;
+}
+
+/** @} */
diff --git a/source/osal.h b/source/osal.h
new file mode 100644
index 0000000..30e1592
--- /dev/null
+++ b/source/osal.h
@@ -0,0 +1,754 @@
+/*
+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file osal.h
+ * @brief OSAL module header.
+ *
+ * @addtogroup OSAL
+ * @{
+ */
+
+#ifndef OSAL_H
+#define OSAL_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "cmparams.h"
+
+#include "osalconf.h"
+
+/*===========================================================================*/
+/* Module constants. */
+/*===========================================================================*/
+
+/**
+ * @name Common constants
+ * @{
+ */
+#if !defined(FALSE) || defined(__DOXYGEN__)
+#define FALSE 0
+#endif
+
+#if !defined(TRUE) || defined(__DOXYGEN__)
+#define TRUE 1
+#endif
+
+#define OSAL_SUCCESS false
+#define OSAL_FAILED true
+/** @} */
+
+/**
+ * @name Messages
+ * @{
+ */
+#define MSG_OK (msg_t)0
+#define MSG_RESET (msg_t)-1
+#define MSG_TIMEOUT (msg_t)-2
+#define MSG_WAIT (msg_t)-10
+/** @} */
+
+/**
+ * @name Special time constants
+ * @{
+ */
+#define TIME_IMMEDIATE ((sysinterval_t)0)
+#define TIME_INFINITE ((sysinterval_t)-1)
+/** @} */
+
+/**
+ * @name Systick modes.
+ * @{
+ */
+#define OSAL_ST_MODE_NONE 0
+#define OSAL_ST_MODE_PERIODIC 1
+#define OSAL_ST_MODE_FREERUNNING 2
+/** @} */
+
+/**
+ * @name Systick parameters.
+ * @{
+ */
+/**
+ * @brief Size in bits of the @p systick_t type.
+ */
+#define OSAL_ST_RESOLUTION 32
+
+/**
+ * @brief Systick mode required by the underlying OS.
+ */
+#define OSAL_ST_MODE OSAL_ST_MODE_NONE
+/** @} */
+
+/**
+ * @name IRQ-related constants
+ * @{
+ */
+/**
+ * @brief Total priority levels.
+ */
+#define OSAL_IRQ_PRIORITY_LEVELS (1U << CORTEX_PRIORITY_BITS)
+
+/**
+ * @brief Highest IRQ priority for HAL drivers.
+ */
+#if (CORTEX_MODEL == 0) || defined(__DOXYGEN__)
+#define OSAL_IRQ_MAXIMUM_PRIORITY 0
+#else
+#define OSAL_IRQ_MAXIMUM_PRIORITY 1
+#endif
+
+/**
+ * @brief Converts from numeric priority to BASEPRI register value.
+ */
+#define OSAL_BASEPRI(priority) ((priority) << (8U - CORTEX_PRIORITY_BITS))
+/** @} */
+
+/*===========================================================================*/
+/* Module pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Frequency in Hertz of the system tick.
+ */
+#if !defined(OSAL_ST_FREQUENCY) || defined(__DOXYGEN__)
+#define OSAL_ST_FREQUENCY 1000
+#endif
+
+/**
+ * @brief Enables OSAL assertions.
+ */
+#if !defined(OSAL_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
+#define OSAL_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Enables OSAL functions parameters checks.
+ */
+#if !defined(OSAL_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
+#define OSAL_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief OSAL initialization hook.
+ */
+#if !defined(OSAL_INIT_HOOK) || defined(__DOXYGEN__)
+#define OSAL_INIT_HOOK()
+#endif
+
+/**
+ * @brief Idle loop hook macro.
+ */
+#if !defined(OSAL_IDLE_HOOK) || defined(__DOXYGEN__)
+#define OSAL_IDLE_HOOK()
+#endif
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Module data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of a system status word.
+ */
+typedef uint32_t syssts_t;
+
+/**
+ * @brief Type of a message.
+ */
+typedef int32_t msg_t;
+
+/**
+ * @brief Type of system time counter.
+ */
+typedef uint32_t systime_t;
+
+/**
+ * @brief Type of system time interval.
+ */
+typedef uint32_t sysinterval_t;
+
+/**
+ * @brief Type of realtime counter.
+ */
+typedef uint32_t rtcnt_t;
+
+/**
+ * @brief Type of a thread.
+ * @note The content of this structure is not part of the API and should
+ * not be relied upon. Implementers may define this structure in
+ * an entirely different way.
+ */
+typedef struct {
+ volatile msg_t message;
+} thread_t;
+
+/**
+ * @brief Type of a thread reference.
+ */
+typedef thread_t * thread_reference_t;
+
+/**
+ * @brief Type of an event flags mask.
+ */
+typedef uint32_t eventflags_t;
+
+/**
+ * @brief Type of an event flags object.
+ * @note The content of this structure is not part of the API and should
+ * not be relied upon. Implementers may define this structure in
+ * an entirely different way.
+ * @note Retrieval and clearing of the flags are not defined in this
+ * API and are implementation-dependent.
+ */
+typedef struct event_source event_source_t;
+
+/**
+ * @brief Type of an event source callback.
+ * @note This type is not part of the OSAL API and is provided
+ * exclusively as an example and for convenience.
+ */
+typedef void (*eventcallback_t)(event_source_t *esp);
+
+/**
+ * @brief Events source object.
+ * @note The content of this structure is not part of the API and should
+ * not be relied upon. Implementers may define this structure in
+ * an entirely different way.
+ * @note Retrieval and clearing of the flags are not defined in this
+ * API and are implementation-dependent.
+ */
+struct event_source {
+ volatile eventflags_t flags; /**< @brief Stored event flags. */
+ eventcallback_t cb; /**< @brief Event source callback. */
+ void *param; /**< @brief User defined field. */
+};
+
+/**
+ * @brief Type of a mutex.
+ * @note If the OS does not support mutexes or there is no OS then them
+ * mechanism can be simulated.
+ */
+typedef uint32_t mutex_t;
+
+/**
+ * @brief Type of a thread queue.
+ * @details A thread queue is a queue of sleeping threads, queued threads
+ * can be dequeued one at time or all together.
+ * @note If the OSAL is implemented on a bare metal machine without RTOS
+ * then the queue can be implemented as a single thread reference.
+ */
+typedef struct {
+ thread_reference_t tr;
+} threads_queue_t;
+
+/*===========================================================================*/
+/* Module macros. */
+/*===========================================================================*/
+
+/**
+ * @name Debug related macros
+ * @{
+ */
+/**
+ * @brief Condition assertion.
+ * @details If the condition check fails then the OSAL panics with a
+ * message and halts.
+ * @note The condition is tested only if the @p OSAL_ENABLE_ASSERTIONS
+ * switch is enabled.
+ * @note The remark string is not currently used except for putting a
+ * comment in the code about the assertion.
+ *
+ * @param[in] c the condition to be verified to be true
+ * @param[in] remark a remark string
+ *
+ * @api
+ */
+#define osalDbgAssert(c, remark) do { \
+ /*lint -save -e506 -e774 [2.1, 14.3] Can be a constant by design.*/ \
+ if (OSAL_DBG_ENABLE_ASSERTS != FALSE) { \
+ if (!(c)) { \
+ /*lint -restore*/ \
+ osalSysHalt(__func__); \
+ } \
+ } \
+} while (false)
+
+/**
+ * @brief Function parameters check.
+ * @details If the condition check fails then the OSAL panics and halts.
+ * @note The condition is tested only if the @p OSAL_ENABLE_CHECKS switch
+ * is enabled.
+ *
+ * @param[in] c the condition to be verified to be true
+ *
+ * @api
+ */
+#define osalDbgCheck(c) do { \
+ /*lint -save -e506 -e774 [2.1, 14.3] Can be a constant by design.*/ \
+ if (OSAL_DBG_ENABLE_CHECKS != FALSE) { \
+ if (!(c)) { \
+ /*lint -restore*/ \
+ osalSysHalt(__func__); \
+ } \
+ } \
+} while (false)
+
+/**
+ * @brief I-Class state check.
+ * @note Implementation is optional.
+ */
+#define osalDbgCheckClassI()
+
+/**
+ * @brief S-Class state check.
+ * @note Implementation is optional.
+ */
+#define osalDbgCheckClassS()
+/** @} */
+
+/**
+ * @name IRQ service routines wrappers
+ * @{
+ */
+/**
+ * @brief Priority level verification macro.
+ */
+#define OSAL_IRQ_IS_VALID_PRIORITY(n) \
+ (((n) >= OSAL_IRQ_MAXIMUM_PRIORITY) && ((n) < OSAL_IRQ_PRIORITY_LEVELS))
+
+/**
+ * @brief IRQ prologue code.
+ * @details This macro must be inserted at the start of all IRQ handlers.
+ */
+#define OSAL_IRQ_PROLOGUE()
+
+/**
+ * @brief IRQ epilogue code.
+ * @details This macro must be inserted at the end of all IRQ handlers.
+ */
+#define OSAL_IRQ_EPILOGUE()
+
+/**
+ * @brief IRQ handler function declaration.
+ * @details This macro hides the details of an ISR function declaration.
+ *
+ * @param[in] id a vector name as defined in @p vectors.s
+ */
+#define OSAL_IRQ_HANDLER(id) void id(void)
+/** @} */
+
+/**
+ * @name Time conversion utilities
+ * @{
+ */
+/**
+ * @brief Seconds to system ticks.
+ * @details Converts from seconds to system ticks number.
+ * @note The result is rounded upward to the next tick boundary.
+ *
+ * @param[in] secs number of seconds
+ * @return The number of ticks.
+ *
+ * @api
+ */
+#define OSAL_S2I(secs) \
+ ((sysinterval_t)((uint32_t)(secs) * (uint32_t)OSAL_ST_FREQUENCY))
+
+/**
+ * @brief Milliseconds to system ticks.
+ * @details Converts from milliseconds to system ticks number.
+ * @note The result is rounded upward to the next tick boundary.
+ *
+ * @param[in] msecs number of milliseconds
+ * @return The number of ticks.
+ *
+ * @api
+ */
+#define OSAL_MS2I(msecs) \
+ ((sysinterval_t)((((((uint32_t)(msecs)) * \
+ ((uint32_t)OSAL_ST_FREQUENCY)) - 1UL) / 1000UL) + 1UL))
+
+/**
+ * @brief Microseconds to system ticks.
+ * @details Converts from microseconds to system ticks number.
+ * @note The result is rounded upward to the next tick boundary.
+ *
+ * @param[in] usecs number of microseconds
+ * @return The number of ticks.
+ *
+ * @api
+ */
+#define OSAL_US2I(usecs) \
+ ((sysinterval_t)((((((uint32_t)(usecs)) * \
+ ((uint32_t)OSAL_ST_FREQUENCY)) - 1UL) / 1000000UL) + 1UL))
+/** @} */
+
+/**
+ * @name Time conversion utilities for the realtime counter
+ * @{
+ */
+/**
+ * @brief Seconds to realtime counter.
+ * @details Converts from seconds to realtime counter cycles.
+ * @note The macro assumes that @p freq >= @p 1.
+ *
+ * @param[in] freq clock frequency, in Hz, of the realtime counter
+ * @param[in] sec number of seconds
+ * @return The number of cycles.
+ *
+ * @api
+ */
+#define OSAL_S2RTC(freq, sec) ((freq) * (sec))
+
+/**
+ * @brief Milliseconds to realtime counter.
+ * @details Converts from milliseconds to realtime counter cycles.
+ * @note The result is rounded upward to the next millisecond boundary.
+ * @note The macro assumes that @p freq >= @p 1000.
+ *
+ * @param[in] freq clock frequency, in Hz, of the realtime counter
+ * @param[in] msec number of milliseconds
+ * @return The number of cycles.
+ *
+ * @api
+ */
+#define OSAL_MS2RTC(freq, msec) (rtcnt_t)((((freq) + 999UL) / 1000UL) * (msec))
+
+/**
+ * @brief Microseconds to realtime counter.
+ * @details Converts from microseconds to realtime counter cycles.
+ * @note The result is rounded upward to the next microsecond boundary.
+ * @note The macro assumes that @p freq >= @p 1000000.
+ *
+ * @param[in] freq clock frequency, in Hz, of the realtime counter
+ * @param[in] usec number of microseconds
+ * @return The number of cycles.
+ *
+ * @api
+ */
+#define OSAL_US2RTC(freq, usec) (rtcnt_t)((((freq) + 999999UL) / 1000000UL) * (usec))
+/** @} */
+
+/**
+ * @name Sleep macros using absolute time
+ * @{
+ */
+/**
+ * @brief Delays the invoking thread for the specified number of seconds.
+ * @note The specified time is rounded up to a value allowed by the real
+ * system tick clock.
+ * @note The maximum specifiable value is implementation dependent.
+ *
+ * @param[in] secs time in seconds, must be different from zero
+ *
+ * @api
+ */
+#define osalThreadSleepSeconds(secs) osalThreadSleep(OSAL_S2I(secs))
+
+/**
+ * @brief Delays the invoking thread for the specified number of
+ * milliseconds.
+ * @note The specified time is rounded up to a value allowed by the real
+ * system tick clock.
+ * @note The maximum specifiable value is implementation dependent.
+ *
+ * @param[in] msecs time in milliseconds, must be different from zero
+ *
+ * @api
+ */
+#define osalThreadSleepMilliseconds(msecs) osalThreadSleep(OSAL_MS2I(msecs))
+
+/**
+ * @brief Delays the invoking thread for the specified number of
+ * microseconds.
+ * @note The specified time is rounded up to a value allowed by the real
+ * system tick clock.
+ * @note The maximum specifiable value is implementation dependent.
+ *
+ * @param[in] usecs time in microseconds, must be different from zero
+ *
+ * @api
+ */
+#define osalThreadSleepMicroseconds(usecs) osalThreadSleep(OSAL_US2I(usecs))
+/** @} */
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+extern const char *osal_halt_msg;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void osalInit(void);
+ void osalSysHalt(const char *reason);
+ void osalSysPolledDelayX(rtcnt_t cycles);
+ void osalOsTimerHandlerI(void);
+ void osalOsRescheduleS(void);
+ systime_t osalOsGetSystemTimeX(void);
+ void osalThreadSleepS(sysinterval_t time);
+ void osalThreadSleep(sysinterval_t time);
+ msg_t osalThreadSuspendS(thread_reference_t *trp);
+ msg_t osalThreadSuspendTimeoutS(thread_reference_t *trp, sysinterval_t timeout);
+ void osalThreadResumeI(thread_reference_t *trp, msg_t msg);
+ void osalThreadResumeS(thread_reference_t *trp, msg_t msg);
+ msg_t osalThreadEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout);
+ void osalThreadDequeueNextI(threads_queue_t *tqp, msg_t msg);
+ void osalThreadDequeueAllI(threads_queue_t *tqp, msg_t msg);
+ void osalEventBroadcastFlagsI(event_source_t *esp, eventflags_t flags);
+ void osalEventBroadcastFlags(event_source_t *esp, eventflags_t flags);
+ void osalEventSetCallback(event_source_t *esp,
+ eventcallback_t cb,
+ void *param);
+ void osalMutexLock(mutex_t *mp);
+ void osalMutexUnlock(mutex_t *mp);
+#ifdef __cplusplus
+}
+#endif
+
+/*===========================================================================*/
+/* Module inline functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Disables interrupts globally.
+ *
+ * @special
+ */
+static inline void osalSysDisable(void) {
+
+ __disable_irq();
+}
+
+/**
+ * @brief Enables interrupts globally.
+ *
+ * @special
+ */
+static inline void osalSysEnable(void) {
+
+ __enable_irq();
+}
+
+/**
+ * @brief Enters a critical zone from thread context.
+ * @note This function cannot be used for reentrant critical zones.
+ *
+ * @special
+ */
+static inline void osalSysLock(void) {
+
+#if CORTEX_MODEL == 0
+ __disable_irq();
+#else
+ __set_BASEPRI(OSAL_BASEPRI(OSAL_IRQ_MAXIMUM_PRIORITY));
+#endif
+}
+
+/**
+ * @brief Leaves a critical zone from thread context.
+ * @note This function cannot be used for reentrant critical zones.
+ *
+ * @special
+ */
+static inline void osalSysUnlock(void) {
+
+#if CORTEX_MODEL == 0
+ __enable_irq();
+#else
+ __set_BASEPRI(0);
+#endif
+}
+
+/**
+ * @brief Enters a critical zone from ISR context.
+ * @note This function cannot be used for reentrant critical zones.
+ *
+ * @special
+ */
+static inline void osalSysLockFromISR(void) {
+
+#if CORTEX_MODEL == 0
+ __disable_irq();
+#else
+ __set_BASEPRI(OSAL_BASEPRI(OSAL_IRQ_MAXIMUM_PRIORITY));
+#endif
+}
+
+/**
+ * @brief Leaves a critical zone from ISR context.
+ * @note This function cannot be used for reentrant critical zones.
+ *
+ * @special
+ */
+static inline void osalSysUnlockFromISR(void) {
+
+#if CORTEX_MODEL == 0
+ __enable_irq();
+#else
+ __set_BASEPRI(0);
+#endif
+}
+
+/**
+ * @brief Returns the execution status and enters a critical zone.
+ * @details This functions enters into a critical zone and can be called
+ * from any context. Because its flexibility it is less efficient
+ * than @p chSysLock() which is preferable when the calling context
+ * is known.
+ * @post The system is in a critical zone.
+ *
+ * @return The previous system status, the encoding of this
+ * status word is architecture-dependent and opaque.
+ *
+ * @xclass
+ */
+static inline syssts_t osalSysGetStatusAndLockX(void) {
+ syssts_t sts;
+
+#if CORTEX_MODEL == 0
+ sts = (syssts_t)__get_PRIMASK();
+ __disable_irq();
+#else
+ sts = (syssts_t)__get_BASEPRI();
+ __set_BASEPRI(OSAL_BASEPRI(OSAL_IRQ_MAXIMUM_PRIORITY));
+#endif
+ return sts;
+}
+
+/**
+ * @brief Restores the specified execution status and leaves a critical zone.
+ * @note A call to @p chSchRescheduleS() is automatically performed
+ * if exiting the critical zone and if not in ISR context.
+ *
+ * @param[in] sts the system status to be restored.
+ *
+ * @xclass
+ */
+static inline void osalSysRestoreStatusX(syssts_t sts) {
+
+#if CORTEX_MODEL == 0
+ if ((sts & (syssts_t)1) == (syssts_t)0) {
+ __enable_irq();
+ }
+#else
+ __set_BASEPRI(sts);
+#endif
+}
+
+/**
+ * @brief Adds an interval to a system time returning a system time.
+ *
+ * @param[in] systime base system time
+ * @param[in] interval interval to be added
+ * @return The new system time.
+ *
+ * @xclass
+ */
+static inline systime_t osalTimeAddX(systime_t systime,
+ sysinterval_t interval) {
+
+ return systime + (systime_t)interval;
+}
+
+/**
+ * @brief Subtracts two system times returning an interval.
+ *
+ * @param[in] start first system time
+ * @param[in] end second system time
+ * @return The interval representing the time difference.
+ *
+ * @xclass
+ */
+static inline sysinterval_t osalTimeDiffX(systime_t start, systime_t end) {
+
+ return (sysinterval_t)((systime_t)(end - start));
+}
+
+/**
+ * @brief Checks if the specified time is within the specified time window.
+ * @note When start==end then the function returns always false because the
+ * time window has zero size.
+ * @note This function can be called from any context.
+ *
+ * @param[in] time the time to be verified
+ * @param[in] start the start of the time window (inclusive)
+ * @param[in] end the end of the time window (non inclusive)
+ * @retval true current time within the specified time window.
+ * @retval false current time not within the specified time window.
+ *
+ * @xclass
+ */
+static inline bool osalTimeIsInRangeX(systime_t time,
+ systime_t start,
+ systime_t end) {
+
+ return (bool)((systime_t)((systime_t)time - (systime_t)start) <
+ (systime_t)((systime_t)end - (systime_t)start));
+}
+
+/**
+ * @brief Initializes a threads queue object.
+ *
+ * @param[out] tqp pointer to the threads queue object
+ *
+ * @init
+ */
+static inline void osalThreadQueueObjectInit(threads_queue_t *tqp) {
+
+ osalDbgCheck(tqp != NULL);
+}
+
+/**
+ * @brief Initializes an event source object.
+ *
+ * @param[out] esp pointer to the event source object
+ *
+ * @init
+ */
+static inline void osalEventObjectInit(event_source_t *esp) {
+
+ osalDbgCheck(esp != NULL);
+
+ esp->flags = (eventflags_t)0;
+ esp->cb = NULL;
+ esp->param = NULL;
+}
+
+/**
+ * @brief Initializes s @p mutex_t object.
+ *
+ * @param[out] mp pointer to the @p mutex_t object
+ *
+ * @init
+ */
+static inline void osalMutexObjectInit(mutex_t *mp) {
+
+ osalDbgCheck(mp != NULL);
+
+ *mp = 0;
+}
+
+#endif /* OSAL_H */
+
+/** @} */