#include #include #include "display.h" // maximum wait time until busy should be deasserted // bail out if it takes longer than that // unit: 10us (roughly) // range: 0..255 #define DISPLAY_WAIT_MAX_10US 100 void display_init() { // PC5, PC6, PC7: output (VFD RS, RW, E), RS=0, RW=1, E=1 // PB0, PB1, PB7, PD0, PD1, PD5, PD6, PD7: output (VFD DB) - can be switched to input (pullup not needed) PORTB &= ~(_BV(PB0) | _BV(PB1) | _BV(PB7)); DDRB |= _BV(PB0) | _BV(PB1) | _BV(PB7); PORTC |= _BV(PC6) | _BV(PC7); DDRC |= _BV(PC5) | _BV(PC6) | _BV(PC7); PORTD &= ~(_BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7)); DDRD |= _BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7); display_wait_ready(); // initialize display: 8 bit, 2 lines, 50% brightness display_write_ir(0x3c); display_wait_ready(); } static void display_set_rs(bool on) { if (on) { PORTC |= _BV(PC5); } else { PORTC &= ~_BV(PC5); } } static void display_set_rw(bool on) { if (on) { PORTC |= _BV(PC6); } else { PORTC &= ~_BV(PC6); } } static void display_set_e(bool on) { if (on) { PORTC |= _BV(PC7); } else { PORTC &= ~_BV(PC7); } } static void display_set_data(uint8_t data) { uint8_t datab = PORTB & ~(_BV(PB0) | _BV(PB1) | _BV(PB7)); uint8_t datad = PORTD & ~(_BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7)); datab |= (data & _BV(2)) ? _BV(PB0) : 0; datab |= (data & _BV(3)) ? _BV(PB1) : 0; datab |= (data & _BV(4)) ? _BV(PB7) : 0; datad |= (data & _BV(0)) ? _BV(PD0) : 0; datad |= (data & _BV(1)) ? _BV(PD1) : 0; datad |= (data & _BV(5)) ? _BV(PD5) : 0; datad |= (data & _BV(6)) ? _BV(PD6) : 0; datad |= (data & _BV(7)) ? _BV(PD7) : 0; PORTB = datab; PORTD = datad; } static uint8_t display_get_data() { uint8_t datab = PINB; uint8_t datad = PIND; uint8_t data = 0; data |= (datad & _BV(PD0)) ? _BV(0) : 0; data |= (datad & _BV(PD1)) ? _BV(1) : 0; data |= (datad & _BV(PD5)) ? _BV(5) : 0; data |= (datad & _BV(PD6)) ? _BV(6) : 0; data |= (datad & _BV(PD7)) ? _BV(7) : 0; data |= (datab & _BV(PB0)) ? _BV(2) : 0; data |= (datab & _BV(PB1)) ? _BV(3) : 0; data |= (datab & _BV(PB7)) ? _BV(4) : 0; return data; } uint8_t display_read_ir() { // switch PD to input DDRB &= ~(_BV(PB0) | _BV(PB1) | _BV(PB7)); DDRD &= ~(_BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7)); // 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); // read inputs uint8_t datab = PINB; uint8_t datad = PIND; _delay_us(0.23 - 0.16); // E=0 (enable/clock) PORTC &= ~_BV(PC7); _delay_us(0.01); // reset to idle / output PORTC = _BV(PC6) | _BV(PC7); DDRB |= _BV(PB0) | _BV(PB1) | _BV(PB7); DDRD |= _BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7); // reassemble data uint8_t data = 0; data |= (datad & _BV(PD0)) ? _BV(0) : 0; data |= (datad & _BV(PD1)) ? _BV(1) : 0; data |= (datad & _BV(PD5)) ? _BV(5) : 0; data |= (datad & _BV(PD6)) ? _BV(6) : 0; data |= (datad & _BV(PD7)) ? _BV(7) : 0; data |= (datab & _BV(PB0)) ? _BV(2) : 0; data |= (datab & _BV(PB1)) ? _BV(3) : 0; data |= (datab & _BV(PB7)) ? _BV(4) : 0; return data; } uint8_t display_read_dr() { // switch PD to input DDRB &= ~(_BV(PB0) | _BV(PB1) | _BV(PB7)); DDRD &= ~(_BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7)); // 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); // read inputs uint8_t datab = PINB; uint8_t datad = PIND; _delay_us(0.23 - 0.16); // E=0 (enable/clock) PORTC &= ~_BV(PC7); _delay_us(0.01); // reset to idle / output PORTC = _BV(PC6) | _BV(PC7); DDRB |= _BV(PB0) | _BV(PB1) | _BV(PB7); DDRD |= _BV(PD0) | _BV(PD1) | _BV(PD5) | _BV(PD6) | _BV(PD7); // reassemble data uint8_t data = 0; data |= (datad & _BV(PD0)) ? _BV(0) : 0; data |= (datad & _BV(PD1)) ? _BV(1) : 0; data |= (datad & _BV(PD5)) ? _BV(5) : 0; data |= (datad & _BV(PD6)) ? _BV(6) : 0; data |= (datad & _BV(PD7)) ? _BV(7) : 0; data |= (datab & _BV(PB0)) ? _BV(2) : 0; data |= (datab & _BV(PB1)) ? _BV(3) : 0; data |= (datab & _BV(PB7)) ? _BV(4) : 0; return data; } void display_write_ir(uint8_t data) { // RS=0 (IR) RW=0 (write) display_set_rs(false); display_set_rw(false); _delay_us(0.23); // E=1 (enable/clock) display_set_e(true); // send data display_set_data(data); _delay_us(0.23); // E=0 (enable/clock) display_set_e(false); _delay_us(0.01); // reset to idle display_set_rs(true); display_set_rw(true); display_set_data(0); } void display_write_dr(uint8_t data) { // RS=1 (DR) RW=0 (write) display_set_rs(true); display_set_rw(false); _delay_us(0.23); // E=1 (enable/clock) display_set_e(true); // send data display_set_data(data); _delay_us(0.23); // E=0 (enable/clock) display_set_e(false); _delay_us(0.01); // reset to idle display_set_rw(true); display_set_data(0); } bool display_wait_ready() { // FIXME hack to avoid read mode _delay_ms(1); return true; // FIXME // 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 < DISPLAY_WAIT_MAX_10US; 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]); } } }