#![no_std]
#![deny(unsafe_code)]
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;
#[repr(u8)]
pub enum Command {
SystemSet = 0x40,
SleepIn = 0x53,
DisplayOff = 0x58,
DisplayOn = 0x59,
Scroll = 0x44,
CsrForm = 0x5D,
CgRamAdr = 0x5C,
CsrDirRight = 0x4C,
CsrDirLeft = 0x4D,
CsrDirUp = 0x4E,
CsrDirDown = 0x4F,
HdotScr = 0x5A,
Ovlay = 0x5B,
Csrw = 0x46,
Csrr = 0x47,
Mwrite = 0x42,
Mread = 0x43,
}
#[derive(Debug, Clone, Copy)]
pub struct Config {
pub font_width: u8,
pub font_height: u8,
pub screen_width: u16,
pub screen_height: u16,
pub text_layer_start: u16,
pub graphics_layer_start: u16,
}
impl Config {
pub fn new(font_width: u8, font_height: u8, screen_width: u16, screen_height: u16) -> Result<Self, &'static str> {
let chars_per_line = screen_width / font_width as u16;
let bytes_per_char = ((font_width + 7) / 8) as u16;
let cr = chars_per_line * bytes_per_char;
if cr > 239 { return Err("CR exceeds maximum 239"); }
let lines = screen_height / font_height as u16;
let text_layer_size = chars_per_line * lines;
let text_layer_start = 0x0000;
let graphics_layer_start = text_layer_start + text_layer_size;
Ok(Self {
font_width, font_height,
screen_width, screen_height,
text_layer_start, graphics_layer_start,
})
}
}
pub struct RA8835A<DATA, A0, WR, RD, CS, RES, DELAY> {
data: DATA,
a0: A0,
wr: WR,
rd: RD,
cs: CS,
res: RES,
delay: DELAY,
pub config: Config,
}
impl<DATA, A0, WR, RD, CS, RES, DELAY, E> RA8835A<DATA, A0, WR, RD, CS, RES, DELAY>
where
DATA: ParallelBus<Error = E>,
A0: OutputPin,
WR: OutputPin,
RD: OutputPin,
CS: OutputPin,
RES: OutputPin,
DELAY: DelayNs,
{
pub fn new(data: DATA, a0: A0, wr: WR, rd: RD, cs: CS, res: RES, delay: DELAY, config: Config) -> Result<Self, E> {
let mut display = Self {
data,
a0,
wr,
rd,
cs,
res,
delay,
config,
};
display.cs.set_low();
display.hardware_reset()?;
display.initialize()?;
display.configure_layers()?;
display.clear_display()?;
display.enable_display()?;
Ok(display)
}
fn hardware_reset(&mut self) -> Result<(), E> {
self.res.set_low();
self.delay.delay_ms(10);
self.res.set_high();
self.delay.delay_ms(3);
Ok(())
}
fn initialize(&mut self) -> Result<(), E> {
self.write_command(Command::SystemSet)?;
let fx = self.config.font_width - 1;
let fy = self.config.font_height - 1;
let chars_per_line = self.config.screen_width / self.config.font_width as u16;
let bytes_per_char = ((self.config.font_width + 7) / 8) as u16;
let cr = chars_per_line * bytes_per_char;
let lf = self.config.screen_height - 1;
let params = [
0x30, (0x01 << 7) + fx, fy, cr as u8, cr as u8 + 4, lf as u8, cr as u8, 0x00, ];
for param in params {
self.write_data(param)?;
}
Ok(())
}
fn configure_layers(&mut self) -> Result<(), E> {
self.write_command(Command::Scroll)?;
let params = [
(self.config.text_layer_start & 0xFF) as u8,
(self.config.text_layer_start >> 8) as u8,
(self.config.screen_height) as u8,
(self.config.graphics_layer_start & 0xFF) as u8,
(self.config.graphics_layer_start >> 8) as u8,
(self.config.screen_height) as u8,
];
for param in params {
self.write_data(param)?;
}
Ok(())
}
pub fn clear_display(&mut self) -> Result<(), E> {
let total_bytes = (self.config.screen_width / 8) * self.config.screen_height;
self.set_cursor_address(0x00)?;
self.write_command(Command::CsrDirRight)?;
self.write_command(Command::Mwrite)?;
for _ in 0..total_bytes*3 {
self.write_data(0x00)?;
}
Ok(())
}
pub fn write_text(&mut self, text: &str) -> Result<(), E> {
self.write_command(Command::Mwrite)?;
for &char in text.as_bytes() {
self.write_data(char)?;
}
Ok(())
}
pub fn write_text_at(&mut self, text: &str, x: u16, y: u16) -> Result<(), E> {
let chars_per_line = self.config.screen_width / self.config.font_width as u16;
let char_x = x / self.config.font_width as u16;
let char_y = y / self.config.font_height as u16;
let byte_addr = self.config.text_layer_start + (char_y * chars_per_line) + char_x;
self.set_cursor_address(byte_addr)?;
self.write_command(Command::Mwrite)?;
for &char in text.as_bytes() {
self.write_data(char)?;
}
Ok(())
}
fn enable_display(&mut self) -> Result<(), E> {
self.write_command(Command::HdotScr)?;
self.write_data(0x00)?;
self.write_command(Command::DisplayOff)?;
self.write_data(0b00111111)?;
self.write_command(Command::Csrw)?;
self.write_data(0x00)?;
self.write_data(0x00)?;
self.write_command(Command::CsrForm)?;
self.write_data(0x05)?;
self.write_data((1 << 7) + self.config.font_height)?; self.write_command(Command::Ovlay)?;
self.write_data(0x00)?;
self.write_command(Command::DisplayOn)?;
Ok(())
}
pub fn set_pixel(&mut self, x: u16, y: u16, color: bool) -> Result<(), E> {
let bit_mask = 1 << 0x07 - (x % self.config.font_width as u16);
let bytes_per_line = self.config.screen_width / self.config.font_width as u16;
let byte_addr = self.config.graphics_layer_start + (y * bytes_per_line) + (x / self.config.font_width as u16);
self.set_cursor_address(byte_addr)?;
self.write_command(Command::Mread)?;
let current = self.read_data().unwrap_or(0);
let new_data = match color {
true => current | bit_mask,
false => current & !bit_mask
};
self.set_cursor_address(byte_addr)?;
self.write_command(Command::Mwrite)?;
self.write_data(new_data)?;
Ok(())
}
pub fn write_command(&mut self, cmd: Command) -> Result<(), E> {
self.a0.set_high();
self.data.write(cmd as u8);
self.delay.delay_ns(10);
self.wr.set_low();
self.delay.delay_ns(150);
self.wr.set_high();
Ok(())
}
pub fn write_data(&mut self, data: u8) -> Result<(), E> {
self.a0.set_low();
self.data.write(data);
self.delay.delay_ns(10);
self.wr.set_low();
self.delay.delay_ns(150);
self.wr.set_high();
Ok(())
}
pub fn read_data(&mut self) -> Result<u8, E> {
self.data.set_input();
self.a0.set_high();
self.rd.set_low();
self.delay.delay_ns(30);
let result = self.data.read()?;
self.delay.delay_ns(30);
self.rd.set_high();
self.data.set_output();
Ok(result)
}
pub fn set_cursor_address(&mut self, address: u16) -> Result<(), E> {
self.write_command(Command::Csrw)?;
self.write_data((address & 0xFF) as u8)?;
self.write_data((address >> 8) as u8)?;
Ok(())
}
}
pub trait ParallelBus {
type Error;
fn write(&mut self, value: u8) -> ();
fn read(&mut self) -> Result<u8, Self::Error>;
fn set_input(&mut self) -> ();
fn set_output(&mut self) -> ();
}