#![no_std]
use core::fmt::Error as FmtError;
use core::fmt::Result as FmtResult;
use core::fmt::Write;
use embedded_hal::digital::v2::OutputPin;
pub const WIDTH: u8 = 84;
pub const HEIGHT: u8 = 48;
pub const ROWS: u8 = HEIGHT / 8;
#[repr(u8)]
pub enum TemperatureCoefficient {
TC0 = 0,
TC1 = 1,
TC2 = 2,
TC3 = 3,
}
#[repr(u8)]
pub enum BiasMode {
Bias1To100 = 0,
Bias1To80 = 1,
Bias1To65 = 2,
Bias1To48 = 3,
Bias1To40 = 4,
Bias1To24 = 5,
Bias1To18 = 6,
Bias1To10 = 7,
}
#[repr(u8)]
pub enum DisplayMode {
DisplayBlank = 0b000,
NormalMode = 0b100,
AllSegmentsOn = 0b001,
InverseVideoMode = 0b101,
}
pub struct PCD8544<CLK, DIN, DC, CE, RST, LIGHT>
where
CLK: OutputPin,
DIN: OutputPin,
DC: OutputPin,
CE: OutputPin,
RST: OutputPin,
LIGHT: OutputPin,
{
clk: CLK,
din: DIN,
dc: DC,
ce: CE,
rst: RST,
light: LIGHT,
power_down_control: bool,
entry_mode: bool,
extended_instruction_set: bool,
x: u8,
y: u8,
}
impl<CLK, DIN, DC, CE, RST, LIGHT, ERR> PCD8544<CLK, DIN, DC, CE, RST, LIGHT>
where
CLK: OutputPin<Error = ERR>,
DIN: OutputPin<Error = ERR>,
DC: OutputPin<Error = ERR>,
CE: OutputPin<Error = ERR>,
RST: OutputPin<Error = ERR>,
LIGHT: OutputPin<Error = ERR>,
{
pub fn new(
mut clk: CLK,
din: DIN,
dc: DC,
mut ce: CE,
mut rst: RST,
light: LIGHT,
) -> Result<PCD8544<CLK, DIN, DC, CE, RST, LIGHT>, ERR> {
clk.set_low()?;
rst.set_low()?;
ce.set_high()?;
Ok(PCD8544 {
clk,
din,
dc,
ce,
rst,
light,
power_down_control: false,
entry_mode: false,
extended_instruction_set: false,
x: 0,
y: 0,
})
}
pub fn reset(&mut self) -> Result<(), ERR> {
self.rst.set_low()?;
self.x = 0;
self.y = 0;
self.init()
}
pub fn init(&mut self) -> Result<(), ERR> {
self.rst.set_low()?;
self.rst.set_high()?;
self.power_down_control = false;
self.entry_mode = false;
self.extended_instruction_set = false;
self.enable_extended_commands(true)?;
self.set_contrast(56_u8)?;
self.set_temperature_coefficient(TemperatureCoefficient::TC3)?;
self.set_bias_mode(BiasMode::Bias1To40)?;
self.enable_extended_commands(false)?;
self.set_display_mode(DisplayMode::NormalMode)?;
self.clear()
}
pub fn clear(&mut self) -> Result<(), ERR> {
for _ in 0..(WIDTH as u16 * ROWS as u16) {
self.write_data(0x00)?;
}
self.set_x_position(0)?;
self.set_y_position(0)
}
pub fn set_power_down(&mut self, power_down: bool) -> Result<(), ERR> {
self.power_down_control = power_down;
self.write_current_function_set()
}
pub fn set_entry_mode(&mut self, entry_mode: bool) -> Result<(), ERR> {
self.entry_mode = entry_mode;
self.write_current_function_set()
}
pub fn x(&self) -> u8 {
self.x
}
pub fn y(&self) -> u8 {
self.y
}
pub fn set_x_position(&mut self, x: u8) -> Result<(), ERR> {
let x = x % WIDTH;
self.x = x;
self.write_command(0x80 | x)
}
pub fn set_y_position(&mut self, y: u8) -> Result<(), ERR> {
let y = y % ROWS;
self.y = y;
self.write_command(0x40 | y)
}
pub fn set_light(&mut self, enabled: bool) -> Result<(), ERR> {
if enabled {
self.light.set_low()
} else {
self.light.set_high()
}
}
pub fn set_display_mode(&mut self, mode: DisplayMode) -> Result<(), ERR> {
self.write_command(0x08 | mode as u8)
}
pub fn set_bias_mode(&mut self, bias: BiasMode) -> Result<(), ERR> {
self.write_command(0x10 | bias as u8)
}
pub fn set_temperature_coefficient(
&mut self,
coefficient: TemperatureCoefficient,
) -> Result<(), ERR> {
self.write_command(0x04 | coefficient as u8)
}
pub fn set_contrast(&mut self, contrast: u8) -> Result<(), ERR> {
self.write_command(0x80 | contrast)
}
pub fn enable_extended_commands(&mut self, enable: bool) -> Result<(), ERR> {
self.extended_instruction_set = enable;
self.write_current_function_set()
}
fn write_current_function_set(&mut self) -> Result<(), ERR> {
let power = self.power_down_control;
let entry = self.entry_mode;
let extended = self.extended_instruction_set;
self.write_function_set(power, entry, extended)
}
fn write_function_set(
&mut self,
power_down_control: bool,
entry_mode: bool,
extended_instruction_set: bool,
) -> Result<(), ERR> {
let mut val = 0x20;
if power_down_control {
val |= 0x04;
}
if entry_mode {
val |= 0x02;
}
if extended_instruction_set {
val |= 0x01;
}
self.write_command(val)
}
pub fn write_command(&mut self, value: u8) -> Result<(), ERR> {
self.write_byte(false, value)
}
pub fn write_data(&mut self, value: u8) -> Result<(), ERR> {
self.write_byte(true, value)
}
fn write_byte(&mut self, data: bool, value: u8) -> Result<(), ERR> {
let mut value = value;
if data {
self.dc.set_high()?;
self.increase_position();
} else {
self.dc.set_low()?;
}
self.ce.set_low()?;
for _ in 0..8 {
self.write_bit((value & 0x80) == 0x80)?;
value <<= 1;
}
self.ce.set_high()
}
fn increase_position(&mut self) {
self.x = (self.x + 1) % WIDTH;
if self.x == 0 {
self.y = (self.y + 1) & ROWS;
}
}
fn write_bit(&mut self, high: bool) -> Result<(), ERR> {
if high {
self.din.set_high()?;
} else {
self.din.set_low()?;
}
self.clk.set_high()?;
self.clk.set_low()
}
}
fn char_to_bytes(char: char) -> &'static [u8] {
match char {
' ' => &[0x00, 0x00, 0x00, 0x00, 0x00],
'!' => &[0x00, 0x00, 0x5f, 0x00, 0x00],
'"' => &[0x00, 0x07, 0x00, 0x07, 0x00],
'#' => &[0x14, 0x7f, 0x14, 0x7f, 0x14],
'$' => &[0x24, 0x2a, 0x7f, 0x2a, 0x12],
'%' => &[0x23, 0x13, 0x08, 0x64, 0x62],
'&' => &[0x36, 0x49, 0x55, 0x22, 0x50],
'\'' => &[0x00, 0x05, 0x03, 0x00, 0x00],
'(' => &[0x00, 0x1c, 0x22, 0x41, 0x00],
')' => &[0x00, 0x41, 0x22, 0x1c, 0x00],
'*' => &[0x14, 0x08, 0x3e, 0x08, 0x14],
'+' => &[0x08, 0x08, 0x3e, 0x08, 0x08],
',' => &[0x00, 0x50, 0x30, 0x00, 0x00],
'-' => &[0x08, 0x08, 0x08, 0x08, 0x08],
'.' => &[0x00, 0x60, 0x60, 0x00, 0x00],
'/' => &[0x20, 0x10, 0x08, 0x04, 0x02],
'0' => &[0x3e, 0x51, 0x49, 0x45, 0x3e],
'1' => &[0x00, 0x42, 0x7f, 0x40, 0x00],
'2' => &[0x42, 0x61, 0x51, 0x49, 0x46],
'3' => &[0x21, 0x41, 0x45, 0x4b, 0x31],
'4' => &[0x18, 0x14, 0x12, 0x7f, 0x10],
'5' => &[0x27, 0x45, 0x45, 0x45, 0x39],
'6' => &[0x3c, 0x4a, 0x49, 0x49, 0x30],
'7' => &[0x01, 0x71, 0x09, 0x05, 0x03],
'8' => &[0x36, 0x49, 0x49, 0x49, 0x36],
'9' => &[0x06, 0x49, 0x49, 0x29, 0x1e],
':' => &[0x00, 0x36, 0x36, 0x00, 0x00],
';' => &[0x00, 0x56, 0x36, 0x00, 0x00],
'<' => &[0x08, 0x14, 0x22, 0x41, 0x00],
'=' => &[0x14, 0x14, 0x14, 0x14, 0x14],
'>' => &[0x00, 0x41, 0x22, 0x14, 0x08],
'?' => &[0x02, 0x01, 0x51, 0x09, 0x06],
'@' => &[0x32, 0x49, 0x79, 0x41, 0x3e],
'A' => &[0x7e, 0x11, 0x11, 0x11, 0x7e],
'B' => &[0x7f, 0x49, 0x49, 0x49, 0x36],
'C' => &[0x3e, 0x41, 0x41, 0x41, 0x22],
'D' => &[0x7f, 0x41, 0x41, 0x22, 0x1c],
'E' => &[0x7f, 0x49, 0x49, 0x49, 0x41],
'F' => &[0x7f, 0x09, 0x09, 0x09, 0x01],
'G' => &[0x3e, 0x41, 0x49, 0x49, 0x7a],
'H' => &[0x7f, 0x08, 0x08, 0x08, 0x7f],
'I' => &[0x00, 0x41, 0x7f, 0x41, 0x00],
'J' => &[0x20, 0x40, 0x41, 0x3f, 0x01],
'K' => &[0x7f, 0x08, 0x14, 0x22, 0x41],
'L' => &[0x7f, 0x40, 0x40, 0x40, 0x40],
'M' => &[0x7f, 0x02, 0x0c, 0x02, 0x7f],
'N' => &[0x7f, 0x04, 0x08, 0x10, 0x7f],
'O' => &[0x3e, 0x41, 0x41, 0x41, 0x3e],
'P' => &[0x7f, 0x09, 0x09, 0x09, 0x06],
'Q' => &[0x3e, 0x41, 0x51, 0x21, 0x5e],
'R' => &[0x7f, 0x09, 0x19, 0x29, 0x46],
'S' => &[0x46, 0x49, 0x49, 0x49, 0x31],
'T' => &[0x01, 0x01, 0x7f, 0x01, 0x01],
'U' => &[0x3f, 0x40, 0x40, 0x40, 0x3f],
'V' => &[0x1f, 0x20, 0x40, 0x20, 0x1f],
'W' => &[0x3f, 0x40, 0x38, 0x40, 0x3f],
'X' => &[0x63, 0x14, 0x08, 0x14, 0x63],
'Y' => &[0x07, 0x08, 0x70, 0x08, 0x07],
'Z' => &[0x61, 0x51, 0x49, 0x45, 0x43],
'[' => &[0x00, 0x7f, 0x41, 0x41, 0x00],
'¥' => &[0x02, 0x04, 0x08, 0x10, 0x20],
']' => &[0x00, 0x41, 0x41, 0x7f, 0x00],
'^' => &[0x04, 0x02, 0x01, 0x02, 0x04],
'_' => &[0x40, 0x40, 0x40, 0x40, 0x40],
'`' => &[0x00, 0x01, 0x02, 0x04, 0x00],
'a' => &[0x20, 0x54, 0x54, 0x54, 0x78],
'b' => &[0x7f, 0x48, 0x44, 0x44, 0x38],
'c' => &[0x38, 0x44, 0x44, 0x44, 0x20],
'd' => &[0x38, 0x44, 0x44, 0x48, 0x7f],
'e' => &[0x38, 0x54, 0x54, 0x54, 0x18],
'f' => &[0x08, 0x7e, 0x09, 0x01, 0x02],
'g' => &[0x0c, 0x52, 0x52, 0x52, 0x3e],
'h' => &[0x7f, 0x08, 0x04, 0x04, 0x78],
'i' => &[0x00, 0x44, 0x7d, 0x40, 0x00],
'j' => &[0x20, 0x40, 0x44, 0x3d, 0x00],
'k' => &[0x7f, 0x10, 0x28, 0x44, 0x00],
'l' => &[0x00, 0x41, 0x7f, 0x40, 0x00],
'm' => &[0x7c, 0x04, 0x18, 0x04, 0x78],
'n' => &[0x7c, 0x08, 0x04, 0x04, 0x78],
'o' => &[0x38, 0x44, 0x44, 0x44, 0x38],
'p' => &[0x7c, 0x14, 0x14, 0x14, 0x08],
'q' => &[0x08, 0x14, 0x14, 0x18, 0x7c],
'r' => &[0x7c, 0x08, 0x04, 0x04, 0x08],
's' => &[0x48, 0x54, 0x54, 0x54, 0x20],
't' => &[0x04, 0x3f, 0x44, 0x40, 0x20],
'u' => &[0x3c, 0x40, 0x40, 0x20, 0x7c],
'v' => &[0x1c, 0x20, 0x40, 0x20, 0x1c],
'w' => &[0x3c, 0x40, 0x30, 0x40, 0x3c],
'x' => &[0x44, 0x28, 0x10, 0x28, 0x44],
'y' => &[0x0c, 0x50, 0x50, 0x50, 0x3c],
'z' => &[0x44, 0x64, 0x54, 0x4c, 0x44],
'{' => &[0x00, 0x08, 0x36, 0x41, 0x00],
'|' => &[0x00, 0x00, 0x7f, 0x00, 0x00],
'}' => &[0x00, 0x41, 0x36, 0x08, 0x00],
'←' => &[0x10, 0x08, 0x08, 0x10, 0x08],
'→' => &[0x78, 0x46, 0x41, 0x46, 0x78],
'°' => &[0x00, 0x02, 0x05, 0x02, 0x00],
_ => &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
}
}
impl<CLK, DIN, DC, CE, RST, LIGHT, ERR> Write for PCD8544<CLK, DIN, DC, CE, RST, LIGHT>
where
CLK: OutputPin<Error = ERR>,
DIN: OutputPin<Error = ERR>,
DC: OutputPin<Error = ERR>,
CE: OutputPin<Error = ERR>,
RST: OutputPin<Error = ERR>,
LIGHT: OutputPin<Error = ERR>,
{
fn write_str(&mut self, s: &str) -> FmtResult {
for char in s.chars() {
match char {
'\r' => {
self.set_x_position(0).map_err(|_| FmtError)?;
}
'\n' => {
for _ in 0..(WIDTH - self.x) {
self.write_data(0x00).map_err(|_| FmtError)?;
}
}
_ => {
for b in char_to_bytes(char) {
self.write_data(*b).map_err(|_| FmtError)?;
}
self.write_data(0x00).map_err(|_| FmtError)?;
}
}
}
Ok(())
}
}