diff options
author | Bdale Garbee <bdale@gag.com> | 2017-04-24 18:22:03 -0600 |
---|---|---|
committer | Bdale Garbee <bdale@gag.com> | 2017-04-24 18:22:03 -0600 |
commit | b91f67005709cb7f65e0a461b49b5cb0952cb391 (patch) | |
tree | e9f6c0f30a81cf30a9cfd52887171168f7830f85 /src/drivers | |
parent | 1e956f93e0c9f8ed6180490f80e8aead5538f818 (diff) | |
parent | 8a10ddb0bca7d6f6aa4aedda171899abd165fd74 (diff) |
Merge branch 'branch-1.7' into debian
Diffstat (limited to 'src/drivers')
-rw-r--r-- | src/drivers/ao_as1107.c | 105 | ||||
-rw-r--r-- | src/drivers/ao_as1107.h | 59 | ||||
-rw-r--r-- | src/drivers/ao_button.c | 8 | ||||
-rw-r--r-- | src/drivers/ao_cc115l.c | 2 | ||||
-rw-r--r-- | src/drivers/ao_cc1200.c | 8 | ||||
-rw-r--r-- | src/drivers/ao_console.c | 151 | ||||
-rw-r--r-- | src/drivers/ao_console.h | 24 | ||||
-rw-r--r-- | src/drivers/ao_event.h | 1 | ||||
-rw-r--r-- | src/drivers/ao_fat.c | 4 | ||||
-rw-r--r-- | src/drivers/ao_lco.c | 2 | ||||
-rw-r--r-- | src/drivers/ao_lco_cmd.c | 51 | ||||
-rw-r--r-- | src/drivers/ao_lco_func.c | 8 | ||||
-rw-r--r-- | src/drivers/ao_lco_func.h | 2 | ||||
-rw-r--r-- | src/drivers/ao_lco_two.c | 2 | ||||
-rw-r--r-- | src/drivers/ao_matrix.c | 201 | ||||
-rw-r--r-- | src/drivers/ao_matrix.h | 24 | ||||
-rw-r--r-- | src/drivers/ao_pad.c | 29 | ||||
-rw-r--r-- | src/drivers/ao_pad.h | 5 | ||||
-rw-r--r-- | src/drivers/ao_ps2.c | 419 | ||||
-rw-r--r-- | src/drivers/ao_ps2.h | 220 | ||||
-rw-r--r-- | src/drivers/ao_sdcard.c | 6 | ||||
-rw-r--r-- | src/drivers/ao_vga.c | 366 | ||||
-rw-r--r-- | src/drivers/ao_vga.h | 39 |
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_ */ |