diff --git a/display/firmware/display.c b/display/firmware/display.c new file mode 100644 index 0000000..2b5f529 --- /dev/null +++ b/display/firmware/display.c @@ -0,0 +1,108 @@ +#include +#include +#include "display.h" + +void display_init() { + // PC5, PC6, PC7: output (VFD RS, RW, E), RS=0, RW=1, E=1 + // PD: output (VFD DB) - can be switched to input (pullup not needed) + PORTC |= _BV(PC6) | _BV(PC7); + DDRC |= _BV(PC5) | _BV(PC6) | _BV(PC7); + PORTD = 0x00; + DDRD = 0xff; +} + +uint8_t display_read_ir() { + // switch PD to input + DDRD = 0x00; + // E=0 (enable/clock) RS=0 (IR) RW=1 (read) + PORTC = (PORTC & ~(_BV(PC5) | _BV(PC7))) | _BV(PC6); + _delay_us(0.23); + // E=1 (enable/clock) + PORTC |= _BV(PC7); + _delay_us(0.16); + uint8_t data = PIND; + _delay_us(0.23 - 0.16); + // E=0 (enable/clock) + PORTC &= ~_BV(PC7); + _delay_us(0.01); + // reset to idle + PORTC = _BV(PC6) | _BV(PC7); + DDRD = 0xff; + return data; +} + +uint8_t display_read_dr() { + // switch PD to input + DDRD = 0x00; + // E=0 (enable/clock) RS=1 (IR) RW=1 (read) + PORTC = (PORTC & ~_BV(PC7)) | (_BV(PC5) | _BV(PC6)); + _delay_us(0.23); + // E=1 (enable/clock) + PORTC |= _BV(PC7); + _delay_us(0.16); + uint8_t data = PIND; + _delay_us(0.23 - 0.16); + // E=0 (enable/clock) + PORTC &= ~_BV(PC7); + _delay_us(0.01); + // reset to idle + PORTC = _BV(PC6) | _BV(PC7); + DDRD = 0xff; + return data; +} + +void display_write_ir(uint8_t data) { + // E=0 (enable/clock) RS=0 (IR) RW=0 (write) + PORTC &= ~(_BV(PC5) | _BV(PC6) | _BV(PC7)); + _delay_us(0.23); + // E=1 (enable/clock) + PORTC |= _BV(PC7); + // data + PORTD = data; + _delay_us(0.23); + // E=0 (enable/clock) + PORTC &= ~_BV(PC7); + _delay_us(0.01); + // reset to idle + PORTC = _BV(PC6) | _BV(PC7); + PORTD = 0x00; +} + +void display_write_dr(uint8_t data) { + // E=0 (enable/clock) RS=1 (IR) RW=0 (write) + PORTC = (PORTC & ~(_BV(PC6) | _BV(PC7))) | _BV(PC5); + _delay_us(0.23); + // E=1 (enable/clock) + PORTC |= _BV(PC7); + // data + PORTD = data; + _delay_us(0.23); + // E=0 (enable/clock) + PORTC &= ~_BV(PC7); + _delay_us(0.01); + // reset to idle + PORTC = _BV(PC6) | _BV(PC7); + PORTD = 0x00; +} + + +bool display_wait_ready() { + // if the busy flag never goes high, we'll run into a deadlock here. + // let's put a deadline on the wait loop. + for (uint8_t i = 0; i < 100; i++) { + // read_ir() takes about 500ns + if (display_read_ir() & _BV(7)) return true; + // wait 10us until the next status read + _delay_us(10 - 0.5); + } + return false; +} + +void display_write(uint8_t command, const uint8_t *data, uint8_t length) { + if (display_wait_ready()) { + display_write_ir(command); + for (uint8_t i = 0; i < length; i++) { + display_write_dr(data[i]); + } + } +} diff --git a/display/firmware/display.h b/display/firmware/display.h new file mode 100644 index 0000000..2169890 --- /dev/null +++ b/display/firmware/display.h @@ -0,0 +1,11 @@ +#include +#include + +void display_init(); +void display_write(uint8_t command, const uint8_t *data, uint8_t length); + +uint8_t display_read_ir(); +uint8_t display_read_dr(); +void display_write_ir(uint8_t data); +void display_write_dr(uint8_t data); +bool display_wait_ready();