summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/ao_as1107.c105
-rw-r--r--src/drivers/ao_as1107.h59
-rw-r--r--src/drivers/ao_button.c8
-rw-r--r--src/drivers/ao_cc115l.c2
-rw-r--r--src/drivers/ao_cc1200.c8
-rw-r--r--src/drivers/ao_console.c151
-rw-r--r--src/drivers/ao_console.h24
-rw-r--r--src/drivers/ao_event.h1
-rw-r--r--src/drivers/ao_fat.c4
-rw-r--r--src/drivers/ao_lco.c2
-rw-r--r--src/drivers/ao_lco_cmd.c51
-rw-r--r--src/drivers/ao_lco_func.c8
-rw-r--r--src/drivers/ao_lco_func.h2
-rw-r--r--src/drivers/ao_lco_two.c2
-rw-r--r--src/drivers/ao_matrix.c201
-rw-r--r--src/drivers/ao_matrix.h24
-rw-r--r--src/drivers/ao_pad.c29
-rw-r--r--src/drivers/ao_pad.h5
-rw-r--r--src/drivers/ao_ps2.c419
-rw-r--r--src/drivers/ao_ps2.h220
-rw-r--r--src/drivers/ao_sdcard.c6
-rw-r--r--src/drivers/ao_vga.c366
-rw-r--r--src/drivers/ao_vga.h39
23 files changed, 1718 insertions, 18 deletions
diff --git a/src/drivers/ao_as1107.c b/src/drivers/ao_as1107.c
new file mode 100644
index 00000000..e0172d95
--- /dev/null
+++ b/src/drivers/ao_as1107.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_as1107.h>
+
+static uint8_t as1107_configured;
+static uint8_t as1107_mutex;
+
+static void
+ao_as1107_start(void) {
+ ao_spi_get_bit(AO_AS1107_CS_PORT, AO_AS1107_CS_PIN, AO_AS1107_CS, AO_AS1107_SPI_INDEX, AO_AS1107_SPI_SPEED);
+}
+
+static void
+ao_as1107_stop(void) {
+ ao_spi_put_bit(AO_AS1107_CS_PORT, AO_AS1107_CS_PIN, AO_AS1107_CS, AO_AS1107_SPI_INDEX);
+}
+
+static void
+_ao_as1107_cmd(uint8_t addr, uint8_t value)
+{
+ uint8_t packet[2] = { addr, value };
+
+ ao_as1107_start();
+ ao_spi_send(packet, 2, AO_AS1107_SPI_INDEX);
+ ao_as1107_stop();
+}
+
+static void
+_ao_as1107_setup(void)
+{
+ if (!as1107_configured) {
+ as1107_configured = 1;
+ _ao_as1107_cmd(AO_AS1107_SHUTDOWN, AO_AS1107_SHUTDOWN_SHUTDOWN_RESET);
+ _ao_as1107_cmd(AO_AS1107_SHUTDOWN, AO_AS1107_SHUTDOWN_SHUTDOWN_NOP);
+ _ao_as1107_cmd(AO_AS1107_DECODE_MODE, AO_AS1107_DECODE);
+ _ao_as1107_cmd(AO_AS1107_SCAN_LIMIT, AO_AS1107_NUM_DIGITS - 1);
+ _ao_as1107_cmd(AO_AS1107_INTENSITY, 0x0f);
+ _ao_as1107_cmd(AO_AS1107_FEATURE,
+ (0 << AO_AS1107_FEATURE_CLK_EN) |
+ (0 << AO_AS1107_FEATURE_REG_RES) |
+ (1 << AO_AS1107_FEATURE_DECODE_SEL) |
+ (1 << AO_AS1107_FEATURE_SPI_EN) |
+ (0 << AO_AS1107_FEATURE_BLINK_EN) |
+ (0 << AO_AS1107_FEATURE_BLINK_FREQ) |
+ (0 << AO_AS1107_FEATURE_SYNC) |
+ (0 << AO_AS1107_FEATURE_BLINK_START));
+ _ao_as1107_cmd(AO_AS1107_SHUTDOWN, AO_AS1107_SHUTDOWN_NORMAL_NOP);
+ }
+}
+
+void
+ao_as1107_write(uint8_t start, uint8_t count, uint8_t *values)
+{
+ uint8_t i;
+ ao_mutex_get(&as1107_mutex);
+ _ao_as1107_setup();
+ for (i = 0; i < count; i++)
+ {
+ _ao_as1107_cmd(AO_AS1107_DIGIT(start + i),
+ values[i]);
+ }
+ ao_mutex_put(&as1107_mutex);
+}
+
+void
+ao_as1107_write_8(uint8_t start, uint8_t value)
+{
+ uint8_t values[2];
+
+ values[0] = (value >> 4);
+ values[1] = value & 0xf;
+ ao_as1107_write(start, 2, values);
+}
+
+void
+ao_as1107_write_16(uint8_t start, uint16_t value)
+{
+ uint8_t values[4];
+
+ values[0] = (value >> 12);
+ values[1] = (value >> 8) & 0xf;
+ values[2] = (value >> 4) & 0xf;
+ values[3] = (value) & 0xf;
+ ao_as1107_write(start, 4, values);
+}
+
+void
+ao_as1107_init(void)
+{
+ as1107_configured = 0;
+ ao_spi_init_cs(AO_AS1107_CS_PORT, (1 << AO_AS1107_CS_PIN));
+}
diff --git a/src/drivers/ao_as1107.h b/src/drivers/ao_as1107.h
new file mode 100644
index 00000000..a22b17db
--- /dev/null
+++ b/src/drivers/ao_as1107.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_AS1107_H_
+#define _AO_AS1107_H_
+
+#define AO_AS1107_NO_OP 0x00
+#define AO_AS1107_DIGIT(n) (0x01 + (n))
+#define AO_AS1107_DECODE_MODE 0x09
+#define AO_AS1107_INTENSITY 0x0a
+#define AO_AS1107_SCAN_LIMIT 0x0b
+#define AO_AS1107_SHUTDOWN 0x0c
+#define AO_AS1107_SHUTDOWN_SHUTDOWN_RESET 0x00
+#define AO_AS1107_SHUTDOWN_SHUTDOWN_NOP 0x80
+#define AO_AS1107_SHUTDOWN_NORMAL_RESET 0x01
+#define AO_AS1107_SHUTDOWN_NORMAL_NOP 0x81
+
+#define AO_AS1107_FEATURE 0x0e
+#define AO_AS1107_FEATURE_CLK_EN 0 /* external clock enable */
+#define AO_AS1107_FEATURE_REG_RES 1
+#define AO_AS1107_FEATURE_DECODE_SEL 2 /* select HEX decode */
+#define AO_AS1107_FEATURE_SPI_EN 3
+#define AO_AS1107_FEATURE_BLINK_EN 4
+#define AO_AS1107_FEATURE_BLINK_FREQ 5
+#define AO_AS1107_FEATURE_SYNC 6
+#define AO_AS1107_FEATURE_BLINK_START 7
+#define AO_AS1107_DISPLAY_TEST 0x0f
+
+void ao_as1107_init(void);
+
+void
+ao_as1107_write(uint8_t start, uint8_t count, uint8_t *values);
+
+void
+ao_as1107_write_8(uint8_t start, uint8_t value);
+
+void
+ao_as1107_write_16(uint8_t start, uint16_t value);
+
+#ifndef AO_AS1107_DECODE
+#error "must define AO_AS1107_DECODE"
+#endif
+
+#ifndef AO_AS1107_NUM_DIGITS
+#error "must define AO_AS1107_NUM_DIGITS"
+#endif
+
+#endif /* _AO_AS1107_H_ */
diff --git a/src/drivers/ao_button.c b/src/drivers/ao_button.c
index 725ac45a..07e92c67 100644
--- a/src/drivers/ao_button.c
+++ b/src/drivers/ao_button.c
@@ -39,8 +39,16 @@ static struct ao_button_state ao_button_state[AO_BUTTON_COUNT];
#define bit(q) AO_BUTTON_ ## q
#define pin(q) AO_BUTTON_ ## q ## _PIN
+#ifndef AO_BUTTON_INVERTED
+#define AO_BUTTON_INVERTED 1
+#endif
+
+#if AO_BUTTON_INVERTED
/* pins are inverted */
#define ao_button_value(b) !ao_gpio_get(port(b), bit(b), pin(b))
+#else
+#define ao_button_value(b) ao_gpio_get(port(b), bit(b), pin(b))
+#endif
static uint8_t
_ao_button_get(uint8_t b)
diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c
index a67071d2..c1c21e0d 100644
--- a/src/drivers/ao_cc115l.c
+++ b/src/drivers/ao_cc115l.c
@@ -39,7 +39,7 @@ static uint8_t ao_radio_abort; /* radio operation should abort */
#define FOSC 26000000
-#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_6MHz)
+#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_CC115L_SPI_SPEED)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c
index 2bc99734..de282000 100644
--- a/src/drivers/ao_cc1200.c
+++ b/src/drivers/ao_cc1200.c
@@ -51,7 +51,11 @@ extern const uint32_t ao_radio_cal;
#define FOSC 40000000
#endif
-#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_SPI_SPEED_FAST)
+#ifndef AO_CC1200_SPI_SPEED
+#error AO_CC1200_SPI_SPEED undefined
+#endif
+
+#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_CC1200_SPI_SPEED)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC1200_SPI_BUS)
#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1200_SPI_BUS)
@@ -1323,7 +1327,7 @@ static void ao_radio_packet(void) {
void
ao_radio_test_recv(void)
{
- uint8_t bytes[34];
+ static uint8_t bytes[34];
uint8_t b;
if (ao_radio_recv(bytes, 34, 0)) {
diff --git a/src/drivers/ao_console.c b/src/drivers/ao_console.c
new file mode 100644
index 00000000..cbde38c9
--- /dev/null
+++ b/src/drivers/ao_console.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_console.h"
+#include "ao_ps2.h"
+#include "ao_vga.h"
+
+static uint8_t console_row, console_col;
+
+#define ao_console_bitmap ao_vga_bitmap
+
+static uint8_t console_rows, console_cols;
+
+static void
+ao_console_scroll(void)
+{
+ ao_copy(&ao_console_bitmap,
+ 0, 0,
+ ao_console_bitmap.width,
+ ao_console_bitmap.height - ao_font.height,
+ &ao_console_bitmap,
+ 0, ao_font.height,
+ AO_COPY);
+ ao_rect(&ao_console_bitmap,
+ 0,
+ (console_rows - 1) * ao_font.height,
+ ao_console_bitmap.width,
+ ao_font.height,
+ 1,
+ AO_COPY);
+}
+
+static void
+ao_console_cursor(void)
+{
+ ao_rect(&ao_console_bitmap,
+ console_col * ao_font.width,
+ console_row * ao_font.height,
+ ao_font.width,
+ ao_font.height,
+ 1,
+ AO_XOR);
+}
+
+static void
+ao_console_clear(void)
+{
+ ao_rect(&ao_console_bitmap,
+ 0, 0,
+ ao_console_bitmap.width,
+ ao_console_bitmap.height,
+ 1,
+ AO_COPY);
+}
+
+static void
+ao_console_space(void)
+{
+ ao_rect(&ao_console_bitmap,
+ console_col * ao_font.width,
+ console_row * ao_font.height,
+ ao_font.width,
+ ao_font.height,
+ 1,
+ AO_COPY);
+}
+
+static void
+ao_console_newline(void)
+{
+ if (++console_row == console_rows) {
+ ao_console_scroll();
+ console_row--;
+ }
+}
+
+void
+ao_console_putchar(char c)
+{
+ if (' ' <= c && c < 0x7f) {
+ char text[2];
+ ao_console_space();
+ text[0] = c;
+ text[1] = '\0';
+ ao_text(&ao_console_bitmap,
+ console_col * ao_font.width,
+ console_row * ao_font.height + ao_font.ascent,
+ text,
+ 0,
+ AO_COPY);
+ if (++console_col == console_cols) {
+ console_col = 0;
+ ao_console_newline();
+ }
+ } else {
+ ao_console_cursor();
+ switch (c) {
+ case '\r':
+ console_col = 0;
+ break;
+ case '\t':
+ console_col += 8 - (console_col & 7);
+ if (console_col >= console_cols) {
+ console_col = 0;
+ ao_console_newline();
+ }
+ break;
+ case '\n':
+ ao_console_newline();
+ break;
+ case '\f':
+ console_col = console_row = 0;
+ ao_console_clear();
+ break;
+ case '\177':
+ case '\010':
+ if (console_col)
+ console_col--;
+ break;
+ }
+ }
+ ao_console_cursor();
+}
+
+void
+ao_console_init(void)
+{
+ console_cols = ao_console_bitmap.width / ao_font.width;
+ console_rows = ao_console_bitmap.height / ao_font.height;
+#if CONSOLE_STDIN
+ ao_ps2_stdin = 1;
+ ao_add_stdio(_ao_ps2_pollchar,
+ ao_console_putchar,
+ NULL);
+#endif
+ ao_console_clear();
+ ao_console_cursor();
+ ao_vga_enable(1);
+}
diff --git a/src/drivers/ao_console.h b/src/drivers/ao_console.h
new file mode 100644
index 00000000..33d3658a
--- /dev/null
+++ b/src/drivers/ao_console.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_CONSOLE_H_
+#define _AO_CONSOLE_H_
+
+void
+ao_console_putchar(char c);
+
+void
+ao_console_init(void);
+
+#endif /* _AO_CONSOLE_H_ */
diff --git a/src/drivers/ao_event.h b/src/drivers/ao_event.h
index d1c69d81..d1df6eac 100644
--- a/src/drivers/ao_event.h
+++ b/src/drivers/ao_event.h
@@ -22,6 +22,7 @@
#define AO_EVENT_NONE 0
#define AO_EVENT_QUADRATURE 1
#define AO_EVENT_BUTTON 2
+#define AO_EVENT_KEY 3
struct ao_event {
uint8_t type;
diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c
index fb8eecff..43e7df23 100644
--- a/src/drivers/ao_fat.c
+++ b/src/drivers/ao_fat.c
@@ -1615,7 +1615,7 @@ ao_fat_hexdump_cmd(void)
ao_cmd_status = ao_cmd_syntax_error;
return;
}
-
+
fd = ao_fat_open(name, AO_FAT_OPEN_READ);
if (fd < 0) {
printf ("Open failed: %d\n", fd);
@@ -1649,5 +1649,7 @@ void
ao_fat_init(void)
{
ao_bufio_init();
+#if FAT_COMMANDS
ao_cmd_register(&ao_fat_cmds[0]);
+#endif
}
diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c
index 00f10ecc..e1806ca3 100644
--- a/src/drivers/ao_lco.c
+++ b/src/drivers/ao_lco.c
@@ -661,7 +661,7 @@ ao_lco_monitor(void)
ao_lco_armed, ao_lco_firing);
if (ao_lco_armed && ao_lco_firing) {
- ao_lco_ignite();
+ ao_lco_ignite(AO_PAD_FIRE);
} else {
ao_lco_update();
if (ao_lco_armed) {
diff --git a/src/drivers/ao_lco_cmd.c b/src/drivers/ao_lco_cmd.c
index dcc0c6d0..8de21fb6 100644
--- a/src/drivers/ao_lco_cmd.c
+++ b/src/drivers/ao_lco_cmd.c
@@ -61,9 +61,9 @@ lco_arm(void)
}
static void
-lco_ignite(void)
+lco_ignite(uint8_t cmd)
{
- ao_lco_ignite();
+ ao_lco_ignite(cmd);
}
static void
@@ -145,7 +145,40 @@ lco_fire_cmd(void) __reentrant
secs = 100;
for (i = 0; i < secs; i++) {
printf("fire %d\n", i); flush();
- lco_ignite();
+ lco_ignite(AO_PAD_FIRE);
+ ao_delay(AO_MS_TO_TICKS(100));
+ }
+}
+
+static void
+lco_static_cmd(void) __reentrant
+{
+ uint8_t secs;
+ uint8_t i;
+ int8_t r;
+
+ lco_args();
+ ao_cmd_decimal();
+ secs = ao_cmd_lex_i;
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ r = lco_query();
+ if (r != AO_RADIO_CMAC_OK) {
+ printf("query failed %d\n", r);
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ printf("arm %d\n", i); flush();
+ lco_arm();
+ }
+
+ secs = secs * 10 - 5;
+ if (secs > 100)
+ secs = 100;
+ for (i = 0; i < secs; i++) {
+ printf("fire %d\n", i); flush();
+ lco_ignite(AO_PAD_STATIC);
ao_delay(AO_MS_TO_TICKS(100));
}
}
@@ -171,12 +204,22 @@ lco_ignite_cmd(void) __reentrant
uint8_t i;
lco_args();
for (i = 0; i < 4; i++)
- lco_ignite();
+ lco_ignite(AO_PAD_FIRE);
+}
+
+
+static void
+lco_endstatic_cmd(void) __reentrant
+{
+ lco_ignite(AO_PAD_ENDSTATIC);
}
static __code struct ao_cmds ao_lco_cmds[] = {
{ lco_report_cmd, "l <box> <channel>\0Get remote status" },
{ lco_fire_cmd, "F <box> <channel> <secs>\0Fire remote igniters" },
+ { lco_fire_cmd, "F <box> <channel> <secs>\0Fire remote igniters" },
+ { lco_static_cmd, "S <box> <channel> <secs>\0Initiate static test" },
+ { lco_endstatic_cmd, "D\0End static test (and download someday)" },
{ lco_arm_cmd, "a <box> <channel>\0Arm remote igniter" },
{ lco_ignite_cmd, "i <box> <channel>\0Pulse remote igniter" },
{ 0, NULL },
diff --git a/src/drivers/ao_lco_func.c b/src/drivers/ao_lco_func.c
index 862cb1be..92b344ed 100644
--- a/src/drivers/ao_lco_func.c
+++ b/src/drivers/ao_lco_func.c
@@ -47,7 +47,7 @@ ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset)
ao_mutex_get(&ao_lco_mutex);
command.tick = ao_time();
command.box = box;
- command.cmd = AO_LAUNCH_QUERY;
+ command.cmd = AO_PAD_QUERY;
command.channels = 0;
ao_radio_cmac_send(&command, sizeof (command));
sent_time = ao_time();
@@ -64,19 +64,19 @@ ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset)
ao_mutex_get(&ao_lco_mutex);
command.tick = ao_time() - tick_offset;
command.box = box;
- command.cmd = AO_LAUNCH_ARM;
+ command.cmd = AO_PAD_ARM;
command.channels = channels;
ao_radio_cmac_send(&command, sizeof (command));
ao_mutex_put(&ao_lco_mutex);
}
void
-ao_lco_ignite(void)
+ao_lco_ignite(uint8_t cmd)
{
ao_mutex_get(&ao_lco_mutex);
command.tick = 0;
command.box = 0;
- command.cmd = AO_LAUNCH_FIRE;
+ command.cmd = cmd;
command.channels = 0;
ao_radio_cmac_send(&command, sizeof (command));
ao_mutex_put(&ao_lco_mutex);
diff --git a/src/drivers/ao_lco_func.h b/src/drivers/ao_lco_func.h
index 6b06f928..9d4a27ba 100644
--- a/src/drivers/ao_lco_func.h
+++ b/src/drivers/ao_lco_func.h
@@ -28,6 +28,6 @@ void
ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset);
void
-ao_lco_ignite(void);
+ao_lco_ignite(uint8_t cmd);
#endif /* _AO_LCO_FUNC_H_ */
diff --git a/src/drivers/ao_lco_two.c b/src/drivers/ao_lco_two.c
index 1cb0546c..e2f86745 100644
--- a/src/drivers/ao_lco_two.c
+++ b/src/drivers/ao_lco_two.c
@@ -287,7 +287,7 @@ ao_lco_monitor(void)
ao_lco_armed, ao_lco_firing);
if (ao_lco_armed && ao_lco_firing) {
- ao_lco_ignite();
+ ao_lco_ignite(AO_PAD_FIRE);
} else {
ao_lco_get_channels();
if (ao_lco_armed) {
diff --git a/src/drivers/ao_matrix.c b/src/drivers/ao_matrix.c
new file mode 100644
index 00000000..fa2d0c57
--- /dev/null
+++ b/src/drivers/ao_matrix.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_matrix.h>
+#include <ao_event.h>
+#include <ao_exti.h>
+
+#define row_port(q) AO_MATRIX_ROW_ ## q ## _PORT
+#define row_bit(q) AO_MATRIX_ROW_ ## q ## _PIN
+#define row_pin(q) AO_MATRIX_ROW_ ## q ## _PIN
+
+#define col_port(q) AO_MATRIX_COL_ ## q ## _PORT
+#define col_bit(q) AO_MATRIX_COL_ ## q ## _PIN
+#define col_pin(q) AO_MATRIX_COL_ ## q ## _PIN
+
+static void
+_ao_matrix_drive_row(uint8_t row, uint8_t val)
+{
+ switch (row) {
+#define drive(n) case n: ao_gpio_set(row_port(n), row_bit(n), row_pin(n), val); break
+ drive(0);
+#if AO_MATRIX_ROWS > 1
+ drive(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+ drive(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+ drive(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+ drive(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+ drive(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+ drive(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+ drive(7);
+#endif
+ }
+}
+
+static uint8_t
+_ao_matrix_read_cols(void)
+{
+ uint8_t v = 0;
+#define read(n) (v |= ao_gpio_get(col_port(n), col_bit(n), col_pin(n)) << n)
+
+ read(0);
+#if AO_MATRIX_ROWS > 1
+ read(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+ read(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+ read(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+ read(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+ read(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+ read(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+ read(7);
+#endif
+ return v;
+}
+
+static uint8_t
+_ao_matrix_read(uint8_t row) {
+ uint8_t state;
+ _ao_matrix_drive_row(row, 0);
+ state = _ao_matrix_read_cols();
+ _ao_matrix_drive_row(row, 1);
+ return state;
+}
+
+#define AO_MATRIX_DEBOUNCE_INTERVAL AO_MS_TO_TICKS(50)
+
+static uint8_t ao_matrix_keymap[AO_MATRIX_ROWS][AO_MATRIX_COLS] = AO_MATRIX_KEYCODES;
+
+static uint8_t ao_matrix_state[AO_MATRIX_ROWS];
+static AO_TICK_TYPE ao_matrix_tick[AO_MATRIX_ROWS];
+
+static void
+_ao_matrix_poll_one(uint8_t row) {
+ uint8_t state = _ao_matrix_read(row);
+
+ if (state != ao_matrix_state[row]) {
+ AO_TICK_TYPE now = ao_time();
+
+ if ((now - ao_matrix_tick[row]) >= AO_MATRIX_DEBOUNCE_INTERVAL) {
+ uint8_t col;
+ uint8_t changes = state ^ ao_matrix_state[row];
+
+ for (col = 0; col < AO_MATRIX_COLS; col++) {
+ if (changes & (1 << col)) {
+ ao_event_put_isr(AO_EVENT_KEY,
+ ao_matrix_keymap[row][col],
+ ((state >> col) & 1) == 0);
+ }
+ }
+ ao_matrix_state[row] = state;
+ }
+ ao_matrix_tick[row] = now;
+ }
+}
+
+void
+ao_matrix_poll(void)
+{
+ uint8_t row;
+
+ for (row = 0; row < AO_MATRIX_ROWS; row++)
+ _ao_matrix_poll_one(row);
+}
+
+#define init_row(b) do { \
+ ao_enable_output(row_port(b), row_bit(b), row_pin(v), 1); \
+ ao_gpio_set_output_mode(row_port(b), row_bit(b), row_pin(b), AO_OUTPUT_OPEN_DRAIN); \
+ } while (0)
+
+#define init_col(b) do { \
+ ao_enable_input(col_port(b), col_bit(b), AO_EXTI_MODE_PULL_UP); \
+ } while(0)
+
+void
+ao_matrix_init(void)
+{
+ uint8_t row;
+
+ init_row(0);
+#if AO_MATRIX_ROWS > 1
+ init_row(1);
+#endif
+#if AO_MATRIX_ROWS > 2
+ init_row(2);
+#endif
+#if AO_MATRIX_ROWS > 3
+ init_row(3);
+#endif
+#if AO_MATRIX_ROWS > 4
+ init_row(4);
+#endif
+#if AO_MATRIX_ROWS > 5
+ init_row(5);
+#endif
+#if AO_MATRIX_ROWS > 6
+ init_row(6);
+#endif
+#if AO_MATRIX_ROWS > 7
+ init_row(7);
+#endif
+
+ init_col(0);
+#if AO_MATRIX_COLS > 1
+ init_col(1);
+#endif
+#if AO_MATRIX_COLS > 2
+ init_col(2);
+#endif
+#if AO_MATRIX_COLS > 3
+ init_col(3);
+#endif
+#if AO_MATRIX_COLS > 4
+ init_col(4);
+#endif
+#if AO_MATRIX_COLS > 5
+ init_col(5);
+#endif
+#if AO_MATRIX_COLS > 6
+ init_col(6);
+#endif
+#if AO_MATRIX_COLS > 7
+ init_col(7);
+#endif
+ for (row = 0; row < AO_MATRIX_ROWS; row++) {
+ ao_matrix_state[row] = _ao_matrix_read(row);
+ ao_matrix_tick[row] = ao_time();
+ }
+}
diff --git a/src/drivers/ao_matrix.h b/src/drivers/ao_matrix.h
new file mode 100644
index 00000000..ab5a1c51
--- /dev/null
+++ b/src/drivers/ao_matrix.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_MATRIX_H_
+#define _AO_MATRIX_H_
+
+void
+ao_matrix_poll(void);
+
+void
+ao_matrix_init(void);
+
+#endif /* _AO_MATRIX_H_ */
diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c
index ffa833fe..16b4ae60 100644
--- a/src/drivers/ao_pad.c
+++ b/src/drivers/ao_pad.c
@@ -316,7 +316,7 @@ ao_pad(void)
command.tick, command.box, ao_pad_box, command.cmd, command.channels);
switch (command.cmd) {
- case AO_LAUNCH_ARM:
+ case AO_PAD_ARM:
if (command.box != ao_pad_box) {
PRINTD ("box number mismatch\n");
break;
@@ -338,7 +338,7 @@ ao_pad(void)
ao_pad_arm_time = ao_time();
break;
- case AO_LAUNCH_QUERY:
+ case AO_PAD_QUERY:
if (command.box != ao_pad_box) {
PRINTD ("box number mismatch\n");
break;
@@ -357,7 +357,7 @@ ao_pad(void)
query.igniter_status[3]);
ao_radio_cmac_send(&query, sizeof (query));
break;
- case AO_LAUNCH_FIRE:
+ case AO_PAD_FIRE:
if (!ao_pad_armed) {
PRINTD ("not armed\n");
break;
@@ -372,6 +372,29 @@ ao_pad(void)
ao_pad_arm_time = ao_time();
ao_wakeup(&ao_pad_ignite);
break;
+ case AO_PAD_STATIC:
+ if (!ao_pad_armed) {
+ PRINTD ("not armed\n");
+ break;
+ }
+#if HAS_LOG
+ if (!ao_log_running) ao_log_start();
+#endif
+ if ((uint16_t) (ao_time() - ao_pad_arm_time) > AO_SEC_TO_TICKS(20)) {
+ PRINTD ("late pad arm_time %d time %d\n",
+ ao_pad_arm_time, ao_time());
+ break;
+ }
+ PRINTD ("ignite\n");
+ ao_pad_ignite = ao_pad_armed;
+ ao_pad_arm_time = ao_time();
+ ao_wakeup(&ao_pad_ignite);
+ break;
+ case AO_PAD_ENDSTATIC:
+#if HAS_LOG
+ ao_log_stop();
+#endif
+ break;
}
}
}
diff --git a/src/drivers/ao_pad.h b/src/drivers/ao_pad.h
index 648d3005..e4115222 100644
--- a/src/drivers/ao_pad.h
+++ b/src/drivers/ao_pad.h
@@ -54,6 +54,11 @@ struct ao_pad_query {
*/
#define AO_PAD_FIRE 3
+/* Fire current armed pads for 200ms, no report, logging test stand sensors
+ */
+#define AO_PAD_STATIC 4
+#define AO_PAD_ENDSTATIC 5
+
#define AO_PAD_FIRE_TIME AO_MS_TO_TICKS(200)
#define AO_PAD_ARM_STATUS_DISARMED 0
diff --git a/src/drivers/ao_ps2.c b/src/drivers/ao_ps2.c
new file mode 100644
index 00000000..29eecea8
--- /dev/null
+++ b/src/drivers/ao_ps2.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_ps2.h"
+#include "ao_exti.h"
+
+static struct ao_fifo ao_ps2_rx_fifo;
+
+static uint16_t ao_ps2_tx;
+static uint8_t ao_ps2_tx_count;
+
+static AO_TICK_TYPE ao_ps2_tick;
+static uint16_t ao_ps2_value;
+static uint8_t ao_ps2_count;
+
+uint8_t ao_ps2_stdin;
+
+uint8_t ao_ps2_scancode_set;
+
+#define AO_PS2_CLOCK_MODE(pull) ((pull) | AO_EXTI_MODE_FALLING | AO_EXTI_PRIORITY_MED)
+
+static void
+ao_ps2_isr(void);
+
+static uint8_t
+_ao_ps2_parity(uint8_t value)
+{
+ uint8_t parity = 1;
+ uint8_t b;
+
+ for (b = 0; b < 8; b++) {
+ parity ^= (value & 1);
+ value >>= 1;
+ }
+ return parity;
+}
+
+static int
+_ao_ps2_poll(void)
+{
+ uint8_t u;
+ if (ao_fifo_empty(ao_ps2_rx_fifo)) {
+ return AO_READ_AGAIN;
+ }
+ ao_fifo_remove(ao_ps2_rx_fifo, u);
+
+ return (int) u;
+}
+
+uint8_t
+ao_ps2_get(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_ps2_poll()) == AO_READ_AGAIN)
+ ao_sleep(&ao_ps2_rx_fifo);
+ ao_arch_release_interrupts();
+ return (uint8_t) c;
+}
+
+
+int
+ao_ps2_poll(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ c = _ao_ps2_poll();
+ ao_arch_release_interrupts();
+ return (uint8_t) c;
+}
+
+void
+ao_ps2_put(uint8_t c)
+{
+ ao_arch_block_interrupts();
+ ao_ps2_tx = ((uint16_t) c) | (_ao_ps2_parity(c) << 8) | (3 << 9);
+ ao_ps2_tx_count = 11;
+ ao_exti_disable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+ ao_arch_release_interrupts();
+
+ /* pull the clock pin down */
+ ao_enable_output(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT, AO_PS2_CLOCK_PIN, 0);
+ ao_delay(0);
+
+ /* pull the data pin down for the start bit */
+ ao_enable_output(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, 0);
+ ao_delay(0);
+
+ /* switch back to input mode for the interrupt to work */
+ ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
+ AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
+ ao_ps2_isr);
+ ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+
+ /* wait for the bits to drain */
+ while (ao_ps2_tx_count)
+ ao_sleep(&ao_ps2_tx_count);
+
+}
+
+static uint8_t ao_ps2_down[128 / 8];
+
+static void
+ao_ps2_set_down(uint8_t code, uint8_t value)
+{
+ uint8_t shift = (code & 0x07);
+ uint8_t byte = code >> 3;
+
+ ao_ps2_down[byte] = (ao_ps2_down[byte] & ~(1 << shift)) | (value << shift);
+}
+
+uint8_t
+ao_ps2_is_down(uint8_t code)
+{
+ uint8_t shift = (code & 0x07);
+ uint8_t byte = code >> 3;
+
+ return (ao_ps2_down[byte] >> shift) & 1;
+}
+
+static void
+_ao_ps2_set_leds(void)
+{
+ uint8_t led = 0;
+ if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
+ led |= AO_PS2_SET_LEDS_CAPS;
+ if (ao_ps2_is_down(AO_PS2_NUM_LOCK))
+ led |= AO_PS2_SET_LEDS_NUM;
+ if (ao_ps2_is_down(AO_PS2_SCROLL_LOCK))
+ led |= AO_PS2_SET_LEDS_SCROLL;
+ ao_arch_release_interrupts();
+ ao_ps2_put(AO_PS2_SET_LEDS);
+ while (ao_ps2_get() != 0xfa);
+ ao_ps2_put(led);
+ ao_arch_block_interrupts();
+}
+
+static uint8_t
+ao_ps2_is_lock(uint8_t code) {
+ switch (code) {
+ case AO_PS2_CAPS_LOCK:
+ case AO_PS2_NUM_LOCK:
+ case AO_PS2_SCROLL_LOCK:
+ return 1;
+ }
+ return 0;
+}
+
+static void
+_ao_ps2_set_scancode_set(uint8_t set)
+{
+ ao_ps2_scancode_set = set;
+ ao_arch_release_interrupts();
+ ao_ps2_put(AO_PS2_SET_SCAN_CODE_SET);
+ while (ao_ps2_get() != 0xfa);
+ ao_ps2_put(set);
+ ao_ps2_put(AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK);
+ while (ao_ps2_get() != 0xfa);
+ ao_arch_block_interrupts();
+}
+
+static int
+_ao_ps2_poll_key(void)
+{
+ int c;
+ uint8_t set_led = 0;
+ static uint8_t saw_break;
+
+ c = _ao_ps2_poll();
+ if (c < 0) {
+ if (ao_ps2_scancode_set != 3) {
+ _ao_ps2_set_scancode_set(3);
+ }
+ return c;
+ }
+
+ if (c == AO_PS2_BREAK) {
+ saw_break = 1;
+ return AO_READ_AGAIN;
+ }
+ if (c & 0x80)
+ return AO_READ_AGAIN;
+
+ if (ao_ps2_is_lock(c)) {
+ if (saw_break) {
+ saw_break = 0;
+ return AO_READ_AGAIN;
+ }
+ if (ao_ps2_is_down(c))
+ saw_break = 1;
+ set_led = 1;
+ }
+ if (saw_break) {
+ saw_break = 0;
+ ao_ps2_set_down(c, 0);
+ c |= 0x80;
+ } else
+ ao_ps2_set_down(c, 1);
+ if (set_led)
+ _ao_ps2_set_leds();
+
+ if (ao_ps2_scancode_set != 3)
+ _ao_ps2_set_scancode_set(3);
+
+ return c;
+}
+
+int
+ao_ps2_poll_key(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ c = _ao_ps2_poll_key();
+ ao_arch_release_interrupts();
+ return c;
+}
+
+uint8_t
+ao_ps2_get_key(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_ps2_poll_key()) == AO_READ_AGAIN)
+ ao_sleep(&ao_ps2_rx_fifo);
+ ao_arch_release_interrupts();
+ return (uint8_t) c;
+}
+
+static const uint8_t ao_ps2_asciimap[128][2] = {
+ [AO_PS2_A] = { 'a', 'A' },
+ [AO_PS2_B] = { 'b', 'B' },
+ [AO_PS2_C] = { 'c', 'C' },
+ [AO_PS2_D] = { 'd', 'D' },
+ [AO_PS2_E] = { 'e', 'E' },
+ [AO_PS2_F] = { 'f', 'F' },
+ [AO_PS2_G] = { 'g', 'G' },
+ [AO_PS2_H] = { 'h', 'H' },
+ [AO_PS2_I] = { 'i', 'I' },
+ [AO_PS2_J] = { 'j', 'J' },
+ [AO_PS2_K] = { 'k', 'K' },
+ [AO_PS2_L] = { 'l', 'L' },
+ [AO_PS2_M] = { 'm', 'M' },
+ [AO_PS2_N] = { 'n', 'N' },
+ [AO_PS2_O] = { 'o', 'O' },
+ [AO_PS2_P] = { 'p', 'P' },
+ [AO_PS2_Q] = { 'q', 'Q' },
+ [AO_PS2_R] = { 'r', 'R' },
+ [AO_PS2_S] = { 's', 'S' },
+ [AO_PS2_T] = { 't', 'T' },
+ [AO_PS2_U] = { 'u', 'U' },
+ [AO_PS2_V] = { 'v', 'V' },
+ [AO_PS2_W] = { 'w', 'W' },
+ [AO_PS2_X] = { 'x', 'X' },
+ [AO_PS2_Y] = { 'y', 'Y' },
+ [AO_PS2_Z] = { 'z', 'Z' },
+
+ [AO_PS2_0] = { '0', ')' },
+ [AO_PS2_1] = { '1', '!' },
+ [AO_PS2_2] = { '2', '@' },
+ [AO_PS2_3] = { '3', '#' },
+ [AO_PS2_4] = { '4', '$' },
+ [AO_PS2_5] = { '5', '%' },
+ [AO_PS2_6] = { '6', '^' },
+ [AO_PS2_7] = { '7', '&' },
+ [AO_PS2_8] = { '8', '*' },
+ [AO_PS2_9] = { '9', '(' },
+
+ [AO_PS2_GRAVE] = { '`', '~' },
+ [AO_PS2_HYPHEN] = { '-', '_' },
+ [AO_PS2_EQUAL] = { '=', '+' },
+ [AO_PS2_BACKSLASH] = { '\\', '|' },
+ [AO_PS2_BACKSPACE] = { '\010', '\010' },
+ [AO_PS2_SPACE] = { ' ', ' ' },
+ [AO_PS2_TAB] = { '\t', '\t' },
+
+ [AO_PS2_ENTER] = { '\r', '\r' },
+ [AO_PS2_ESC] = { '\033', '\033' },
+
+ [AO_PS2_OPEN_SQ] = { '[', '{' },
+ [AO_PS2_DELETE] = { '\177', '\177' },
+
+ [AO_PS2_KP_TIMES] = { '*', '*' },
+ [AO_PS2_KP_PLUS] = { '+', '+' },
+ [AO_PS2_KP_ENTER] = { '\r', '\r' },
+ [AO_PS2_KP_DECIMAL] = { '.', '.' },
+ [AO_PS2_KP_0] = { '0', '0' },
+ [AO_PS2_KP_1] = { '1', '1' },
+ [AO_PS2_KP_2] = { '2', '2' },
+ [AO_PS2_KP_3] = { '3', '3' },
+ [AO_PS2_KP_4] = { '4', '4' },
+ [AO_PS2_KP_5] = { '5', '5' },
+ [AO_PS2_KP_6] = { '6', '6' },
+ [AO_PS2_KP_7] = { '7', '7' },
+ [AO_PS2_KP_8] = { '8', '8' },
+ [AO_PS2_KP_9] = { '9', '9' },
+ [AO_PS2_CLOSE_SQ] = { ']', '}' },
+ [AO_PS2_SEMICOLON] = { ';', ':' },
+ [AO_PS2_ACUTE] = { '\'', '"' },
+ [AO_PS2_COMMA] = { ',', '<' },
+ [AO_PS2_PERIOD] = { '.', '>' },
+ [AO_PS2_SLASH] = { '/', '?' },
+};
+
+int
+ao_ps2_ascii(uint8_t key)
+{
+ uint8_t col;
+ char a;
+
+ /* Skip key releases */
+ if (key & 0x80)
+ return AO_READ_AGAIN;
+
+ col = 0;
+ if (ao_ps2_is_down(AO_PS2_L_SHIFT) || ao_ps2_is_down(AO_PS2_R_SHIFT))
+ col = 1;
+
+ /* caps lock */
+ a = ao_ps2_asciimap[key][0];
+ if (!a)
+ return AO_READ_AGAIN;
+
+ if ('a' <= a && a <= 'z')
+ if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
+ col ^= 1;
+ a = ao_ps2_asciimap[key][col];
+ if ('@' <= a && a <= 0x7f && (ao_ps2_is_down(AO_PS2_L_CTRL) || ao_ps2_is_down(AO_PS2_R_CTRL)))
+ a &= 0x1f;
+ return a;
+}
+
+int
+_ao_ps2_pollchar(void)
+{
+ int key;
+
+ key = _ao_ps2_poll_key();
+ if (key < 0)
+ return key;
+ return ao_ps2_ascii(key);
+}
+
+char
+ao_ps2_getchar(void)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_ps2_pollchar()) == AO_READ_AGAIN)
+ ao_sleep(&ao_ps2_rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static void
+ao_ps2_isr(void)
+{
+ uint8_t bit;
+
+ if (ao_ps2_tx_count) {
+ ao_gpio_set(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, ao_ps2_tx&1);
+ ao_ps2_tx >>= 1;
+ ao_ps2_tx_count--;
+ if (!ao_ps2_tx_count) {
+ ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_EXTI_MODE_PULL_UP);
+ ao_wakeup(&ao_ps2_tx_count);
+ }
+ return;
+ }
+ /* reset if its been a while */
+ if ((ao_tick_count - ao_ps2_tick) > AO_MS_TO_TICKS(100))
+ ao_ps2_count = 0;
+ ao_ps2_tick = ao_tick_count;
+
+ bit = ao_gpio_get(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN);
+ if (ao_ps2_count == 0) {
+ /* check for start bit, ignore if not zero */
+ if (bit)
+ return;
+ ao_ps2_value = 0;
+ } else if (ao_ps2_count < 9) {
+ ao_ps2_value |= (bit << (ao_ps2_count - 1));
+ } else if (ao_ps2_count == 10) {
+ ao_fifo_insert(ao_ps2_rx_fifo, ao_ps2_value);
+ ao_wakeup(&ao_ps2_rx_fifo);
+ if (ao_ps2_stdin)
+ ao_wakeup(&ao_stdin_ready);
+ ao_ps2_count = 0;
+ return;
+ }
+ ao_ps2_count++;
+}
+
+void
+ao_ps2_init(void)
+{
+ ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT,
+ AO_EXTI_MODE_PULL_UP);
+
+ ao_enable_port(AO_PS2_CLOCK_PORT);
+
+ ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
+ AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
+ ao_ps2_isr);
+ ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+
+ ao_ps2_scancode_set = 2;
+}
diff --git a/src/drivers/ao_ps2.h b/src/drivers/ao_ps2.h
new file mode 100644
index 00000000..f1f05ee5
--- /dev/null
+++ b/src/drivers/ao_ps2.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_PS2_H_
+#define _AO_PS2_H_
+
+extern uint8_t ao_ps2_stdin;
+
+int
+ao_ps2_poll(void);
+
+uint8_t
+ao_ps2_get(void);
+
+void
+ao_ps2_put(uint8_t b);
+
+uint8_t
+ao_ps2_is_down(uint8_t code);
+
+int
+ao_ps2_poll_key(void);
+
+uint8_t
+ao_ps2_get_key(void);
+
+int
+ao_ps2_ascii(uint8_t key);
+
+int
+_ao_ps2_pollchar(void);
+
+char
+ao_ps2_getchar(void);
+
+void
+ao_ps2_init(void);
+
+/* From http://computer-engineering.org/ps2keyboard/ */
+
+/* Device responds with ACK and then resets */
+#define AO_PS2_RESET 0xff
+
+/* Device retransmits last byte */
+#define AO_PS2_RESEND 0xfe
+
+/* Setting key report only works in mode 3 */
+
+/* Disable break and typematic for specified mode 3 keys. Terminate with invalid key */
+#define AO_PS2_SET_KEY_MAKE 0xfd
+
+/* Disable typematic for keys */
+#define AO_PS2_SET_KEY_MAKE_BREAK 0xfc
+
+/* Disable break code for keys */
+#define AO_PS2_SET_KEY_TYPEMATIC 0xfb
+
+/* Enable make, break and typematic */
+#define AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK 0xfa
+
+/* Disable break and typematic for all */
+#define AO_PS2_SET_ALL_MAKE 0xf9
+
+/* Disable typematic for all */
+#define AO_PS2_SET_ALL_MAKE_BREAK 0xf8
+
+/* Disable break for all */
+#define AO_PS2_SET_ALL_TYPEMATIC 0xf7
+
+/* Set keyboard to default (repeat, report and scan code set 2) */
+#define AO_PS2_SET_DEFAULT 0xf6
+
+/* Disable and reset to default */
+#define AO_PS2_DISABLE 0xf5
+
+/* Enable */
+#define AO_PS2_ENABLE 0xf4
+
+/* Set repeat rate. Bytes 5-6 are the start delay, bits 0-4 are the rate */
+#define AO_PS2_SET_REPEAT_RATE 0xf3
+
+/* Read keyboard id. Returns two bytes */
+#define AO_PS2_GETID 0xf2
+
+/* Set scan code (1, 2, or 3) */
+#define AO_PS2_SET_SCAN_CODE_SET 0xf0
+
+/* Echo. Keyboard replies with Echo */
+#define AO_PS2_ECHO 0xee
+
+/* Set LEDs */
+#define AO_PS2_SET_LEDS 0xed
+# define AO_PS2_SET_LEDS_SCROLL 0x01
+# define AO_PS2_SET_LEDS_NUM 0x02
+# define AO_PS2_SET_LEDS_CAPS 0x04
+
+#define AO_PS2_BREAK 0xf0
+#define AO_PS2_ACK 0xfa
+#define AO_PS2_ERROR 0xfc
+#define AO_PS2_NAK 0xfe
+
+/* Scan code set 3 */
+
+#define AO_PS2_A 0x1c
+#define AO_PS2_B 0x32
+#define AO_PS2_C 0x21
+#define AO_PS2_D 0x23
+#define AO_PS2_E 0x24
+#define AO_PS2_F 0x2b
+#define AO_PS2_G 0x34
+#define AO_PS2_H 0x33
+#define AO_PS2_I 0x43
+#define AO_PS2_J 0x3b
+#define AO_PS2_K 0x42
+#define AO_PS2_L 0x4b
+#define AO_PS2_M 0x3a
+#define AO_PS2_N 0x31
+#define AO_PS2_O 0x44
+#define AO_PS2_P 0x4d
+#define AO_PS2_Q 0x15
+#define AO_PS2_R 0x2d
+#define AO_PS2_S 0x1b
+#define AO_PS2_T 0x2c
+#define AO_PS2_U 0x3c
+#define AO_PS2_V 0x2a
+#define AO_PS2_W 0x1d
+#define AO_PS2_X 0x22
+#define AO_PS2_Y 0x35
+#define AO_PS2_Z 0x1a
+#define AO_PS2_0 0x45
+#define AO_PS2_1 0x16
+#define AO_PS2_2 0x1e
+#define AO_PS2_3 0x26
+#define AO_PS2_4 0x25
+#define AO_PS2_5 0x2e
+#define AO_PS2_6 0x36
+#define AO_PS2_7 0x3d
+#define AO_PS2_8 0x3e
+#define AO_PS2_9 0x46
+#define AO_PS2_GRAVE 0x0e
+#define AO_PS2_HYPHEN 0x4e
+#define AO_PS2_EQUAL 0x55
+#define AO_PS2_BACKSLASH 0x5c
+#define AO_PS2_BACKSPACE 0x66
+#define AO_PS2_SPACE 0x29
+#define AO_PS2_TAB 0x0d
+#define AO_PS2_CAPS_LOCK 0x14
+#define AO_PS2_L_SHIFT 0x12
+#define AO_PS2_L_CTRL 0x11
+#define AO_PS2_L_WIN 0x8b
+#define AO_PS2_L_ALT 0x19
+#define AO_PS2_R_SHIFT 0x59
+#define AO_PS2_R_CTRL 0x58
+#define AO_PS2_R_WIN 0x8c
+#define AO_PS2_R_ALT 0x39
+#define AO_PS2_APPS 0x8d
+#define AO_PS2_ENTER 0x5a
+#define AO_PS2_ESC 0x08
+#define AO_PS2_F1 0x07
+#define AO_PS2_F2 0x0f
+#define AO_PS2_F3 0x17
+#define AO_PS2_F4 0x1f
+#define AO_PS2_F5 0x27
+#define AO_PS2_F6 0x2f
+#define AO_PS2_F7 0x37
+#define AO_PS2_F8 0x3f
+#define AO_PS2_F9 0x47
+#define AO_PS2_F10 0x4f
+#define AO_PS2_F11 0x56
+#define AO_PS2_F12 0x5e
+#define AO_PS2_PRNT_SCRN 0x57
+#define AO_PS2_SCROLL_LOCK 0x5f
+#define AO_PS2_PAUSE 0x62
+#define AO_PS2_OPEN_SQ 0x54
+#define AO_PS2_INSERT 0x67
+#define AO_PS2_HOME 0x6e
+#define AO_PS2_PG_UP 0x6f
+#define AO_PS2_DELETE 0x64
+#define AO_PS2_END 0x65
+#define AO_PS2_PG_DN 0x6d
+#define AO_PS2_UP 0x63
+#define AO_PS2_LEFT 0x61
+#define AO_PS2_DOWN 0x60
+#define AO_PS2_RIGHT 0x6a
+#define AO_PS2_NUM_LOCK 0x76
+#define AO_PS2_KP_TIMES 0x7e
+#define AO_PS2_KP_PLUS 0x7c
+#define AO_PS2_KP_ENTER 0x79
+#define AO_PS2_KP_DECIMAL 0x71
+#define AO_PS2_KP_0 0x70
+#define AO_PS2_KP_1 0x69
+#define AO_PS2_KP_2 0x72
+#define AO_PS2_KP_3 0x7a
+#define AO_PS2_KP_4 0x6b
+#define AO_PS2_KP_5 0x73
+#define AO_PS2_KP_6 0x74
+#define AO_PS2_KP_7 0x6c
+#define AO_PS2_KP_8 0x75
+#define AO_PS2_KP_9 0x7d
+#define AO_PS2_CLOSE_SQ 0x5b
+#define AO_PS2_SEMICOLON 0x4c
+#define AO_PS2_ACUTE 0x52
+#define AO_PS2_COMMA 0x41
+#define AO_PS2_PERIOD 0x49
+#define AO_PS2_SLASH 0x4a
+
+#define AO_PS2_RELEASE_FLAG 0x80
+
+#endif /* _AO_PS2_H_ */
diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c
index 4b17c5e3..45454000 100644
--- a/src/drivers/ao_sdcard.c
+++ b/src/drivers/ao_sdcard.c
@@ -38,13 +38,19 @@ extern uint8_t ao_radio_mutex;
#define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1)
/* Include SD card commands */
+#ifndef SDCARD_DEBUG
#define SDCARD_DEBUG 0
+#endif
/* Spew SD tracing */
+#ifndef SDCARD_TRACE
#define SDCARD_TRACE 0
+#endif
/* Emit error and warning messages */
+#ifndef SDCARD_WARN
#define SDCARD_WARN 0
+#endif
static uint8_t initialized;
static uint8_t present;
diff --git a/src/drivers/ao_vga.c b/src/drivers/ao_vga.c
new file mode 100644
index 00000000..909e3109
--- /dev/null
+++ b/src/drivers/ao_vga.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_vga.h"
+
+/* VGA output from the SPI port
+ *
+ * Connections:
+ *
+ * STM VGA
+ * GND 4,6,7,8,9,10
+ * HSYNC PA5 13
+ * VSYNC PB5 14
+ * RGB PB4 1,2,3
+ *
+ * pixel clock PA8 -> PB3
+ * pixel enable PA1 -> PA15
+ */
+
+/* GRF formula for 640x480 yields a pixel clock very close to 24MHz. Pad by
+ * three scanlines to hit exactly that value
+ */
+
+#define HACTIVE (640)
+#define HSYNC_START (656)
+#define HSYNC_END (720)
+#define HTOTAL (800)
+
+#define VACTIVE 480
+#define VSYNC_START 481
+#define VSYNC_END 484
+#define VTOTAL 500
+
+/*
+ * The horizontal counter is set so that the end of hsync is reached
+ * at the maximum counter value. That means that the hblank interval
+ * is offset by HSYNC_END.
+ */
+
+#define HSYNC (HSYNC_END - HSYNC_START)
+#define HBLANK_END (HTOTAL - HSYNC_END)
+#define HBLANK_START (HBLANK_END + HACTIVE)
+
+/*
+ * The vertical counter is set so that the end of vsync is reached at
+ * the maximum counter value. That means that the vblank interval is
+ * offset by VSYNC_END. We send a blank line at the start of the
+ * frame, so each of these is off by one
+ */
+#define VSYNC (VSYNC_END - VSYNC_START)
+#define VBLANK_END (VTOTAL - VSYNC_END)
+#define VBLANK_START (VBLANK_END + VACTIVE)
+
+#define WIDTH_BYTES (AO_VGA_WIDTH >> 3)
+#define SCANOUT ((WIDTH_BYTES+2) >> 1)
+
+uint32_t ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
+
+const struct ao_bitmap ao_vga_bitmap = {
+ .base = ao_vga_fb,
+ .stride = AO_VGA_STRIDE,
+ .width = AO_VGA_WIDTH,
+ .height = AO_VGA_HEIGHT
+};
+
+static uint32_t *scanline;
+
+#define DMA_INDEX STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX)
+
+#define DMA_CCR(en) ((0 << STM_DMA_CCR_MEM2MEM) | \
+ (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) | \
+ (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) | \
+ (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) | \
+ (1 << STM_DMA_CCR_MINC) | \
+ (0 << STM_DMA_CCR_PINC) | \
+ (0 << STM_DMA_CCR_CIRC) | \
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR) | \
+ (0 << STM_DMA_CCR_TCIE) | \
+ (en << STM_DMA_CCR_EN))
+
+
+void stm_tim2_isr(void)
+{
+ int16_t line = stm_tim3.cnt;
+
+ if (VBLANK_END <= line && line < VBLANK_START) {
+ /* Disable */
+ stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(0);
+ /* Reset DMA engine for the next scanline */
+ stm_dma.channel[DMA_INDEX].cmar = scanline;
+ stm_dma.channel[DMA_INDEX].cndtr = SCANOUT;
+
+ /* reset SPI */
+ (void) stm_spi1.dr;
+ (void) stm_spi1.sr;
+
+ /* Enable */
+ stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
+ if (((line - VBLANK_END) & 1))
+ scanline += AO_VGA_STRIDE;
+ } else {
+ scanline = ao_vga_fb;
+ }
+ stm_tim2.sr = 0;
+}
+
+
+void
+ao_vga_init(void)
+{
+ uint32_t cfgr;
+
+ /* Initialize spi1 using MISO PB4 for output */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+ stm_ospeedr_set(&stm_gpiob, 4, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioa, 15, STM_AFR_AF5);
+
+ /* turn on SPI */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+
+ stm_spi1.cr1 = ((1 << STM_SPI_CR1_BIDIMODE) | /* Two wire mode */
+ (1 << STM_SPI_CR1_BIDIOE) |
+ (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
+ (0 << STM_SPI_CR1_CRCNEXT) |
+ (1 << STM_SPI_CR1_DFF) |
+ (0 << STM_SPI_CR1_RXONLY) | /* transmit, not receive */
+ (0 << STM_SPI_CR1_SSM) | /* Software SS handling */
+ (1 << STM_SPI_CR1_SSI) | /* ... */
+ (1 << STM_SPI_CR1_LSBFIRST) | /* Little endian */
+ (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
+ (0 << STM_SPI_CR1_BR) | /* baud rate to pclk/2 */
+ (0 << STM_SPI_CR1_MSTR) | /* slave */
+ (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
+ (0 << STM_SPI_CR1_CPHA));
+ stm_spi1.cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+
+ (void) stm_spi1.dr;
+ (void) stm_spi1.sr;
+
+ /* Grab the DMA channel for SPI1 MOSI */
+ stm_dma.channel[DMA_INDEX].cpar = &stm_spi1.dr;
+ stm_dma.channel[DMA_INDEX].cmar = ao_vga_fb;
+
+ /*
+ * Hsync Configuration
+ */
+ /* Turn on timer 2 */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
+
+ /* tim2 runs at full speed */
+ stm_tim2.psc = 0;
+
+ /* Disable channels while modifying */
+ stm_tim2.ccer = 0;
+
+ /* Channel 1 hsync PWM values */
+ stm_tim2.ccr1 = HSYNC;
+
+ /* Channel 2 trigger scanout */
+ /* wait for the time to start scanout */
+ stm_tim2.ccr2 = HBLANK_END;
+
+ stm_tim2.ccr3 = 32;
+
+ /* Configure channel 1 to output on the pin and
+ * channel 2 to to set the trigger for the vsync timer
+ */
+ stm_tim2.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+ (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+ (1 << STM_TIM234_CCMR1_OC2PE) |
+ (0 << STM_TIM234_CCMR1_OC2FE) |
+ (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+ (0 << STM_TIM234_CCMR1_OC1CE) |
+ (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+ (1 << STM_TIM234_CCMR1_OC1PE) |
+ (0 << STM_TIM234_CCMR1_OC1FE) |
+ (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+ stm_tim2.ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+ (0 << STM_TIM234_CCMR2_OC4M) |
+ (0 << STM_TIM234_CCMR2_OC4PE) |
+ (0 << STM_TIM234_CCMR2_OC4FE) |
+ (0 << STM_TIM234_CCMR2_CC4S) |
+
+ (0 << STM_TIM234_CCMR2_OC3CE) |
+ (STM_TIM234_CCMR2_OC3M_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
+ (1 << STM_TIM234_CCMR2_OC3PE) |
+ (0 << STM_TIM234_CCMR2_OC3FE) |
+ (0 << STM_TIM234_CCMR2_CC3S));
+
+ /* One scanline */
+ stm_tim2.arr = HTOTAL;
+
+ stm_tim2.cnt = 0;
+
+ /* Update the register contents */
+ stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
+
+ /* Enable the timer */
+
+ /* Enable the output */
+ stm_tim2.ccer = ((0 << STM_TIM234_CCER_CC2NP) |
+ (STM_TIM234_CCER_CC2P_ACTIVE_HIGH << STM_TIM234_CCER_CC2P) |
+ (1 << STM_TIM234_CCER_CC2E) |
+ (0 << STM_TIM234_CCER_CC1NP) |
+ (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
+ (1 << STM_TIM234_CCER_CC1E));
+
+ stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+ (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
+ (0 << STM_TIM234_CR2_CCDS));
+
+ /* hsync is not a slave timer */
+ stm_tim2.smcr = 0;
+
+ /* Send an interrupt on channel 3 */
+ stm_tim2.dier = ((1 << STM_TIM234_DIER_CC3IE));
+
+ stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (1 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (1 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (0 << STM_TIM234_CR1_CEN));
+
+ /* Hsync is on PA5 which is Timer 2 CH1 output */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+ stm_ospeedr_set(&stm_gpioa, 5, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpioa, 5, STM_AFR_AF1);
+
+ /* pixel transmit enable is on PA1 */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+ stm_ospeedr_set(&stm_gpioa, 1, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpioa, 1, STM_AFR_AF1);
+
+ /*
+ * Vsync configuration
+ */
+
+ /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
+
+ /* No prescale */
+ stm_tim3.psc = 0;
+
+ /* Channel 1 or 2 vsync PWM values */
+ stm_tim3.ccr1 = VSYNC;
+ stm_tim3.ccr2 = VSYNC;
+
+ stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+ (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+ (1 << STM_TIM234_CCMR1_OC2PE) |
+ (0 << STM_TIM234_CCMR1_OC2FE) |
+ (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+ (0 << STM_TIM234_CCMR1_OC1CE) |
+ (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+ (1 << STM_TIM234_CCMR1_OC1PE) |
+ (0 << STM_TIM234_CCMR1_OC1FE) |
+ (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+ stm_tim3.arr = VTOTAL;
+ stm_tim3.cnt = 0;
+
+ /* Update the register contents */
+ stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
+
+ /* Enable the timer */
+
+ /* Enable the output */
+ stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
+ (STM_TIM234_CCER_CC2P_ACTIVE_LOW << STM_TIM234_CCER_CC2P) |
+ (1 << STM_TIM234_CCER_CC2E) |
+ (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
+ (1 << STM_TIM234_CCER_CC1E));
+
+ stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+ (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
+ (0 << STM_TIM234_CR2_CCDS));
+
+ stm_tim3.smcr = 0;
+ stm_tim3.smcr = ((0 << STM_TIM234_SMCR_ETP) |
+ (0 << STM_TIM234_SMCR_ECE) |
+ (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
+ (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
+ (0 << STM_TIM234_SMCR_MSM) |
+ (STM_TIM234_SMCR_TS_ITR1 << STM_TIM234_SMCR_TS) |
+ (0 << STM_TIM234_SMCR_OCCS) |
+ (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
+
+ stm_tim3.dier = 0;
+
+ stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+ (1 << STM_TIM234_CR1_ARPE) |
+ (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+ (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+ (0 << STM_TIM234_CR1_OPM) |
+ (1 << STM_TIM234_CR1_URS) |
+ (0 << STM_TIM234_CR1_UDIS) |
+ (1 << STM_TIM234_CR1_CEN));
+
+ /* Vsync is on PB5 which is is Timer 3 CH2 output */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+ stm_ospeedr_set(&stm_gpiob, 5, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpiob, 5, STM_AFR_AF2);
+
+ /* Use MCO for the pixel clock, that appears on PA8 */
+ cfgr = stm_rcc.cfgr & ~((STM_RCC_CFGR_MCOPRE_MASK << STM_RCC_CFGR_MCOPRE) |
+ (STM_RCC_CFGR_MCOSEL_MASK << STM_RCC_CFGR_MCOSEL));
+
+ cfgr |= ((STM_RCC_CFGR_MCOPRE_DIV_2 << STM_RCC_CFGR_MCOPRE) |
+ (STM_RCC_CFGR_MCOSEL_SYSCLK << STM_RCC_CFGR_MCOSEL));
+
+ stm_rcc.cfgr = cfgr;
+
+ stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+ stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+
+ /* Enable the scanline interrupt */
+ stm_nvic_set_priority(STM_ISR_TIM2_POS, AO_STM_NVIC_NONMASK_PRIORITY);
+ stm_nvic_set_enable(STM_ISR_TIM2_POS);
+}
+
+uint8_t enabled;
+
+void
+ao_vga_enable(int enable)
+{
+ if (enable) {
+ if (!enabled) {
+ ++ao_task_minimize_latency;
+ enabled = 1;
+ }
+ stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
+ } else {
+ if (enabled) {
+ --ao_task_minimize_latency;
+ enabled = 0;
+ }
+ stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);
+ }
+}
diff --git a/src/drivers/ao_vga.h b/src/drivers/ao_vga.h
new file mode 100644
index 00000000..7d9d6b39
--- /dev/null
+++ b/src/drivers/ao_vga.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_VGA_H_
+#define _AO_VGA_H_
+
+#include "ao_draw.h"
+
+void
+ao_vga_init(void);
+
+void
+ao_vga_enable(int active);
+
+/* Active frame buffer */
+#define AO_VGA_WIDTH 320
+#define AO_VGA_HEIGHT 240
+
+/* Pad on the right so that there are zeros on the output after the line */
+#define AO_VGA_HPAD 32
+
+#define AO_VGA_STRIDE ((AO_VGA_WIDTH + AO_VGA_HPAD) >> AO_SHIFT)
+
+extern uint32_t ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
+
+extern const struct ao_bitmap ao_vga_bitmap;
+
+#endif /* _AO_VGA_H_ */