use core::marker::PhantomData;
use super::address::Address;
use super::{DisplayControlBuilder, EntryModeBuilder, FunctionSetBuilder, Home};
use hal::{Init, ReadMode, Receive, Send, WriteMode};
const LCD_WIDTH: usize = 16;
bitflags! {
struct Instructions: u8 {
const CLEAR_DISPLAY = 0b0000_0001;
const RETURN_HOME = 0b0000_0010;
const SHIFT = 0b0001_0000;
}
}
bitflags! {
struct ShiftTarget: u8 {
const CURSOR = 0b0000_0000;
const DISPLAY = 0b0000_1000;
}
}
bitflags! {
struct ShiftDirection: u8 {
const RIGHT = 0b0000_0100;
const LEFT = 0b0000_0000;
}
}
enum RamType {
DisplayData,
CharacterGenerator,
}
impl From<RamType> for u8 {
fn from(ram_type: RamType) -> Self {
match ram_type {
RamType::DisplayData => 0b1000_0000,
RamType::CharacterGenerator => 0b0100_0000,
}
}
}
pub enum ShiftTo {
Right(u8),
Left(u8),
}
impl ShiftTo {
fn as_offset_and_raw_direction(&self) -> (u8, ShiftDirection) {
match *self {
ShiftTo::Right(offset) => (offset, ShiftDirection::RIGHT),
ShiftTo::Left(offset) => (offset, ShiftDirection::LEFT),
}
}
}
pub enum SeekFrom<T: Into<Address>> {
Home(u8),
Current(u8),
Line { line: T, bytes: u8 },
}
pub struct Display<P, U>
where
U: Into<Address> + Home,
{
connection: P,
cursor_address: Address,
_line_marker: PhantomData<U>,
}
impl<P, U> Display<P, U>
where
P: Init + Send + Receive,
U: Into<Address> + Home,
{
const FIRST_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x33);
const SECOND_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x32);
pub fn new(connection: P) -> Self {
Display {
connection: connection,
cursor_address: Address::from(0),
_line_marker: PhantomData,
}
}
pub fn init(&self, builder: &FunctionSetBuilder) {
self.connection.init();
let cmd = builder.build_command();
let cmd = WriteMode::Command(cmd);
self.init_by_instruction(cmd);
}
fn init_by_instruction(&self, function_set: WriteMode) {
self.connection.send(Self::FIRST_4BIT_INIT_INSTRUCTION);
self.connection.send(Self::SECOND_4BIT_INIT_INSTRUCTION);
self.connection.send(function_set);
self.clear();
}
pub fn set_entry_mode(&self, builder: &EntryModeBuilder) {
let cmd = WriteMode::Command(builder.build_command());
self.connection.send(cmd);
}
pub fn set_display_control(&self, builder: &DisplayControlBuilder) {
let cmd = WriteMode::Command(builder.build_command());
self.connection.send(cmd);
}
pub fn shift_cursor(&mut self, direction: ShiftTo) {
let (offset, raw_direction) = direction.as_offset_and_raw_direction();
if offset == 0 {
return;
}
match direction {
ShiftTo::Right(offset) => self.cursor_address += offset.into(),
ShiftTo::Left(offset) => self.cursor_address -= offset.into(),
}
self.raw_shift(ShiftTarget::CURSOR, offset, raw_direction);
}
pub fn shift(&self, direction: ShiftTo) {
let (offset, raw_direction) = direction.as_offset_and_raw_direction();
self.raw_shift(ShiftTarget::DISPLAY, offset, raw_direction);
}
fn raw_shift(&self, shift_type: ShiftTarget, offset: u8, raw_direction: ShiftDirection) {
let mut cmd = Instructions::SHIFT.bits();
cmd |= shift_type.bits();
cmd |= raw_direction.bits();
for _ in 0..offset {
self.connection.send(WriteMode::Command(cmd));
}
}
pub fn clear(&self) {
let cmd = Instructions::CLEAR_DISPLAY.bits();
self.connection.send(WriteMode::Command(cmd));
}
fn generic_seek(&mut self, ram_type: RamType, pos: SeekFrom<U>) {
let mut cmd = ram_type.into();
let (start, addr) = match pos {
SeekFrom::Home(bytes) => (U::FIRST_LINE_ADDRESS.into(), bytes.into()),
SeekFrom::Current(bytes) => (self.cursor_address, bytes.into()),
SeekFrom::Line { line, bytes } => (line.into(), bytes.into()),
};
self.cursor_address = start + addr;
cmd |= u8::from(self.cursor_address);
self.connection.send(WriteMode::Command(cmd));
}
pub fn seek(&mut self, pos: SeekFrom<U>) {
self.generic_seek(RamType::DisplayData, pos);
}
pub fn seek_cgram(&mut self, pos: SeekFrom<U>) {
self.generic_seek(RamType::CharacterGenerator, pos);
}
pub fn write(&mut self, c: u8) {
self.cursor_address += Address::from(1);
self.connection.send(WriteMode::Data(c));
}
pub fn read_byte(&mut self) -> u8 {
self.cursor_address += Address::from(1);
self.connection.receive(ReadMode::Data)
}
pub fn read_busy_flag(&self) -> (bool, u8) {
let byte = self.connection.receive(ReadMode::BusyFlag);
let busy_flag = (byte & 0b1000_0000) != 0;
let address = byte & 0b0111_1111;
(busy_flag, address)
}
pub fn write_message(&mut self, msg: &str) {
for c in msg.as_bytes().iter().take(LCD_WIDTH) {
self.write(*c);
}
}
pub fn get_connection(self) -> P {
self.connection
}
}