diff options
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_ */ | 
