185 lines
3.7 KiB
C
185 lines
3.7 KiB
C
#include <avr/io.h>
|
|
#include <util/delay.h>
|
|
#include "display.h"
|
|
|
|
// VFDDB0 PB0
|
|
// VFDDB1 PB1
|
|
// VFDDB2 PB2
|
|
// VFDDB3 PB3
|
|
// VFDDB4 PB4
|
|
// VFDDB5 PB5
|
|
// VFDDB6 PB6
|
|
// VFDDB7 PB7
|
|
// VFDRS PC5
|
|
// VFDRW PC6
|
|
// VFDE PC7
|
|
// VFDRST (PC4)
|
|
|
|
// 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
|
|
// PC4: output, active low (VFD RESET), unused in display v2
|
|
// PB0..7: output (VFD DB) - can be switched to input (pullup not needed)
|
|
PORTB = 0x00;
|
|
DDRB = 0xff;
|
|
PORTC |= _BV(PC6) | _BV(PC7);
|
|
DDRC |= _BV(PC5) | _BV(PC6) | _BV(PC7);
|
|
|
|
display_wait_ready();
|
|
_delay_ms(100);
|
|
// 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_reset(bool on) {
|
|
if (on) {
|
|
PORTC |= _BV(PC4);
|
|
} else {
|
|
PORTC &= ~_BV(PC4);
|
|
}
|
|
}
|
|
|
|
static void display_set_data(uint8_t data) {
|
|
PORTB = data;
|
|
}
|
|
|
|
static uint8_t display_get_data() {
|
|
uint8_t data = PINB;
|
|
return data;
|
|
}
|
|
|
|
uint8_t display_read_ir() {
|
|
// switch PD to input
|
|
DDRB = 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);
|
|
// read inputs
|
|
uint8_t data = PINB;
|
|
_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 = 0xff;
|
|
return data;
|
|
}
|
|
|
|
uint8_t display_read_dr() {
|
|
// switch PD to input
|
|
DDRB = 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);
|
|
// read inputs
|
|
uint8_t data = PINB;
|
|
_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 = 0xff;
|
|
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]);
|
|
}
|
|
}
|
|
}
|