diff --git a/src/borg_hw/Makefile b/src/borg_hw/Makefile index 9be92ba..bf69192 100644 --- a/src/borg_hw/Makefile +++ b/src/borg_hw/Makefile @@ -67,6 +67,10 @@ ifeq ($(BORG_HW),HW_ANCIENTBORG) SRC = borg_hw_ancient.c endif +ifeq ($(BORG_HW),HW_LED_TESTER) + SRC = borg_hw_led_tester.c +endif + ifeq ($(SRC),'') $(error no valid hardware driver selected ) endif diff --git a/src/borg_hw/borg_hw_led_tester.c b/src/borg_hw/borg_hw_led_tester.c new file mode 100644 index 0000000..8795b83 --- /dev/null +++ b/src/borg_hw/borg_hw_led_tester.c @@ -0,0 +1,172 @@ +#include "../config.h" +#include "../makros.h" + +#include +#include +#include +#include "borg_hw.h" + + +#if defined(__AVR_ATmega48__) || \ + defined(__AVR_ATmega48P__) || \ + defined(__AVR_ATmega88__) || \ + defined(__AVR_ATmega88P__) || \ + defined(__AVR_ATmega168__) || \ + defined(__AVR_ATmega168P__) || \ + defined(__AVR_ATmega328__) || \ + defined(__AVR_ATmega328P__) +# define TIMER0_OFF() TCCR0A = 0; TCCR0B = 0 +# define TIMER0_MODE_CS256() TCCR0A = _BV(WGM01); TCCR0B = _BV(CS02) +# define TIMER0_RESET() TCNT0 = 0 +# define TIMER0_COMPARE(t) OCR0A = t +# define TIMER0_INT_ENABLE() TIMSK0 = _BV(OCIE0A) +# define TIMER0_ISR TIMER0_COMPA_vect +#else // ATmega8 +# define TIMER0_OFF() TCCR0 = 0 +# define TIMER0_MODE_CS256() TCCR0 = _BV(CS02) +# define TIMER0_RESET() TCNT0 = 0 +# define TIMER0_COMPARE(t) TCNT0 = (0xff - t) +# define TIMER0_INT_ENABLE() TIMSK = _BV(TOIE0) +# define TIMER0_ISR TIMER0_OVF_vect +#endif + + +/* Output data of the current row to the column drivers: + * Column: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * Pin: PD2 PD3 PC5 PC4 PC3 PC2 PC1 PC0 PD7 PD6 PB5 PB4 PB3 PB2 PB1 PB0 + */ +#define DISPLAY_PINS_B (_BV(PB5)|_BV(PB4)|_BV(PB3)|_BV(PB2)|_BV(PB1)|_BV(PB0)) +#define DISPLAY_PINS_C (_BV(PC5)|_BV(PC4)|_BV(PC3)|_BV(PC2)|_BV(PC1)|_BV(PC0)) +#define DISPLAY_PINS_D (_BV(PD7)|_BV(PD6)|_BV(PD3)|_BV(PD2)) + +#define ROW_DUMMY_PORT PORTB +#define ROW_DUMMY_DDR DDRB +#define ROW_DUMMY_PIN PB7 +#define ROW_LED_PORT PORTD +#define ROW_LED_DDR DDRD +#define ROW_LED_PIN PD5 + +// buffer which holds the currently shown frame +unsigned char pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; + + +// switch to next row +static void nextrow(uint8_t row) { + // switch off all columns for now + PORTB &= ~(DISPLAY_PINS_B); + PORTC &= ~(DISPLAY_PINS_C); + PORTD &= ~(DISPLAY_PINS_D); + + // short delay loop, to ensure proper deactivation of the drivers + unsigned char i; + for (i = 0; i < 10; i++) { + asm volatile("nop"); + } + + if (row == LED_TESTER_DISPLAYED_ROW) { + // configured row: unblock LED MOSFET, block resistor MOSFET + ROW_DUMMY_PORT &= ~_BV(ROW_DUMMY_PIN); // block resistor MOSFET + ROW_LED_PORT |= _BV(ROW_LED_PIN); // unblock LED MOSFET + } else { /* fake rows simulated by resistors */ + // remaining rows: unblock resistor MOSFET, block LED MOSFET + ROW_LED_PORT &= ~_BV(ROW_LED_PIN); // block LED MOSFET + ROW_DUMMY_PORT |= _BV(ROW_DUMMY_PIN); // block resistor MOSFET + } + + // another delay loop, to ensure that the drivers are ready + for (i = 0; i < 20; i++) { + asm volatile("nop"); + } +} + + +// show a row +static void rowshow(unsigned char row, unsigned char plane) { + // depending on the currently drawn plane, display the row for a specific + // amount of time +#ifdef HIGH_CONTRAST + static unsigned char const ocr_table[] = {2, 5, 22}; +#else + static unsigned char const ocr_table[] = {3, 4, 22}; +#endif + + TIMER0_COMPARE(ocr_table[plane]); + + uint8_t port_b = (PORTB & ~DISPLAY_PINS_B) | (0x3f & pixmap[plane][row][0]); + uint8_t port_c = (PORTC & ~DISPLAY_PINS_C) | (0x3f & pixmap[plane][row][1]); + uint8_t port_d = (PORTD & ~DISPLAY_PINS_D) | (0xc0 & pixmap[plane][row][0]); + if (0x40u & pixmap[plane][row][1]) port_d |= _BV(PD3); + if (0x80u & pixmap[plane][row][1]) port_d |= _BV(PD2); + + PORTB = port_b; + PORTC = port_c; + PORTD = port_d; +} + + +// interrupt handler +ISR(TIMER0_ISR) { + static unsigned char plane = 0; + static unsigned char row = 0; + + // increment both row and plane + if (++plane == NUMPLANE) { + plane = 0; + if (++row == NUM_ROWS) { + // reset watchdog + wdt_reset(); + + row = 0; + } + nextrow(row); + } + + // output current row according to current plane + rowshow(row, plane); +} + + +// disables timer, causing the watchdog to reset the MCU +void timer0_off() { + cli(); + // switch off all columns + PORTB &= ~(DISPLAY_PINS_B); + PORTC &= ~(DISPLAY_PINS_C); + PORTD &= ~(DISPLAY_PINS_D); + TIMER0_OFF(); + sei(); +} + + +// initialize timer which triggers the interrupt +static void timer0_on() { + TIMER0_MODE_CS256(); // CTC mode, prescaling conforms to clk/256 + TIMER0_RESET(); // set counter to 0 + TIMER0_COMPARE(20); // compare with this value first + TIMER0_INT_ENABLE(); // enable Timer/Counter0 Output Compare Match (A) Int. +} + + +void borg_hw_init() { + // switch column and row ports to output mode + DDRB |= DISPLAY_PINS_B; + DDRC |= DISPLAY_PINS_C; + DDRD |= DISPLAY_PINS_D; + ROW_DUMMY_DDR |= _BV(ROW_DUMMY_PIN); + ROW_LED_DDR |= _BV(ROW_LED_PIN); + + // switch off all columns for now + PORTB &= ~(DISPLAY_PINS_B); + PORTC &= ~(DISPLAY_PINS_C); + PORTD &= ~(DISPLAY_PINS_D); + + // switch off all rows + ROW_DUMMY_PORT &= ~_BV(ROW_DUMMY_PIN); + ROW_LED_PORT &= ~_BV(ROW_LED_PIN); + + timer0_on(); + + // activate watchdog timer + wdt_reset(); + wdt_enable(WDTO_15MS); // 15ms watchdog +} diff --git a/src/borg_hw/config.in b/src/borg_hw/config.in index 7c1d244..9b6abcb 100644 --- a/src/borg_hw/config.in +++ b/src/borg_hw/config.in @@ -22,7 +22,8 @@ choice 'Hardware Driver' \ 4xPD1165 HW_PD1165 \ Ping-Pong HW_PINGPONG \ Giga-Borg HW_GIGABORG \ - Ancient-Borg HW_ANCIENTBORG" \ + Ancient-Borg HW_ANCIENTBORG \ + LED-Tester HW_LED_TESTER" \ 'Borg-16' BORG_HW @@ -86,6 +87,10 @@ if [ "$BORG_HW" == "HW_ANCIENTBORG" ] ; then source src/borg_hw/config_ancient.in fi +if [ "$BORG_HW" == "HW_LED_TESTER" ] ; then + source src/borg_hw/config_borg_led_tester.in +fi + endmenu diff --git a/src/borg_hw/config_borg_led_tester.in b/src/borg_hw/config_borg_led_tester.in new file mode 100644 index 0000000..f30fa7e --- /dev/null +++ b/src/borg_hw/config_borg_led_tester.in @@ -0,0 +1,22 @@ +mainmenu_option next_comment +comment "LED Tester Setup" + +bool "Higher Contrast" HIGH_CONTRAST n + +bool "UART Support" UART_SUPPORT n +choice 'Baud Rate' \ + "2400 2400 \ + 4800 4800 \ + 9600 9600 \ + 14400 14400 \ + 19200 19200 \ + 28800 28800 \ + 38400 38400 \ + 57600 57600 \ + 76800 76800 \ + 115200 115200" \ + '19200' UART_BAUDRATE_SETTING + +int "Displayed row" LED_TESTER_DISPLAYED_ROW 0 + +endmenu diff --git a/src/uart/uart_commands.c b/src/uart/uart_commands.c index a17b72d..56dff1f 100644 --- a/src/uart/uart_commands.c +++ b/src/uart/uart_commands.c @@ -31,7 +31,12 @@ # define UART_PUTC uart1_putc #endif -#define UART_BUFFER_SIZE (SCROLLTEXT_BUFFER_SIZE + 8) +#ifdef SCROLLTEXT_BUFFER_SIZE +# define UART_BUFFER_SIZE (SCROLLTEXT_BUFFER_SIZE + 8) +#else +# define UART_BUFFER_SIZE 136 +#endif + char g_rx_buffer[UART_BUFFER_SIZE]; uint8_t g_rx_index; @@ -151,6 +156,7 @@ static void uartcmd_erase_eeprom(void) { * Displays a simple message without the need to prefix a scrolltext command. */ static void uartcmd_simple_message(void) { +#ifdef SCROLLTEXT_SUPPORT if (uartcmd_processing_allowed()) { uartcmd_forbid(); #ifdef JOYSTICK_SUPPORT @@ -172,6 +178,7 @@ static void uartcmd_simple_message(void) { #endif uartcmd_permit(); } +#endif } @@ -179,6 +186,7 @@ static void uartcmd_simple_message(void) { * Displays a message which may use the complete range of scrolltext commands. */ static void uartcmd_scroll_message(void) { +#ifdef SCROLLTEXT_SUPPORT if (uartcmd_processing_allowed()) { uartcmd_forbid(); #ifdef JOYSTICK_SUPPORT @@ -196,6 +204,7 @@ static void uartcmd_scroll_message(void) { #endif uartcmd_permit(); } +#endif }