use std::{error, fmt, io, thread, time};
mod commands {
pub const SETCOLADDR: u8 = 0x15;
pub const SETROWADDR: u8 = 0x75;
pub const SETCONTRAST: u8 = 0x81;
pub const SETCURRENT_FULL: u8 = 0x84 + 0x03;
pub const SETREMAP: u8 = 0xA0;
pub const SETSTARTLINE: u8 = 0xA1;
pub const SETOFFSET: u8 = 0xA2;
pub const NORMALDISPLAY: u8 = 0xA4;
pub const INVERTDISPLAY: u8 = 0xA7;
pub const SETMULTIPLEX: u8 = 0xA8;
pub const MASTERCONFIG: u8 = 0xAD;
pub const DISPLAYOFF: u8 = 0xAE;
pub const DISPLAYON: u8 = 0xAF;
pub const SETPRECHARGECOMPENABLE: u8 = 0xB0;
pub const SETPHASELEN: u8 = 0xB1;
pub const SETROWPERIOD: u8 = 0xB2;
pub const SETCLOCK: u8 = 0xB3;
pub const SETPRECHARGECOMP: u8 = 0xB4;
pub const SETGRAYTABLE: u8 = 0xB8;
pub const SETVCOMLEVEL: u8 = 0xBE;
pub const SETVSL: u8 = 0xBF;
pub const GFXACCEL: u8 = 0x23;
pub const DRAWRECT: u8 = 0x24;
}
#[derive(Copy,Clone,Debug,Eq,PartialEq)]
pub enum DisplayError {
WriteFailed,
}
impl fmt::Display for DisplayError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", (self as &error::Error).description())
}
}
impl error::Error for DisplayError {
fn description(&self) -> &str {
match self {
&DisplayError::WriteFailed =>
"write failed: unable to send complete sequence to display",
}
}
}
#[derive(Copy,Clone,Debug,Eq,PartialEq)]
pub enum DisplayMode {
Idle,
Reset,
Data,
Command,
}
pub trait ControlChannel {
fn run_in_mode(&mut self, mode: DisplayMode, f: &mut FnMut() -> Result<(),Box<error::Error>>) -> Result<(),Box<error::Error>>;
}
pub struct Ssd1325<'a> {
transport: &'a mut io::Write,
control_channel: &'a mut ControlChannel,
}
impl<'a> Ssd1325<'a> {
pub fn new(transport: &'a mut io::Write, control_channel: &'a mut ControlChannel) -> Self {
Ssd1325 {
transport: transport,
control_channel: control_channel,
}
}
pub fn init(&mut self) -> Result<(),Box<error::Error>> {
use commands::*;
const INIT_SEQUENCE: &'static [u8] = &[
DISPLAYOFF,
SETCLOCK, 0xF1,
SETMULTIPLEX, 0x3F,
SETOFFSET, 0x4C,
SETSTARTLINE, 0x00,
MASTERCONFIG, 0x02,
SETREMAP, 0x50,
SETCURRENT_FULL,
SETGRAYTABLE, 0x01, 0x11, 0x22, 0x32, 0x43, 0x54, 0x65, 0x76,
SETCONTRAST, 0x7F,
SETROWPERIOD, 0x51,
SETPHASELEN, 0x55,
SETPRECHARGECOMP, 0x02,
SETPRECHARGECOMPENABLE, 0x28,
SETVCOMLEVEL, 0x1C,
SETVSL, (0x0D | 0x02),
NORMALDISPLAY,
GFXACCEL, 0x01,
];
self.reset()?;
self.write_sequence(DisplayMode::Command, INIT_SEQUENCE)
}
pub fn clear(&mut self) -> Result<(),Box<error::Error>> {
use commands::*;
const CLEAR_SEQUENCE: &'static [u8] = &[
DRAWRECT, 0x00, 0x00, 0x3F, 0x3F, 0x00,
];
self.write_sequence(DisplayMode::Command, CLEAR_SEQUENCE)
}
pub fn set_on(&mut self, on: bool) -> Result<(),Box<error::Error>> {
match on {
true =>
self.write_sequence(DisplayMode::Command, &[commands::DISPLAYON]),
false =>
self.write_sequence(DisplayMode::Command, &[commands::DISPLAYOFF]),
}
}
pub fn set_inverted(&mut self, inverted: bool) -> Result<(),Box<error::Error>> {
match inverted {
true =>
self.write_sequence(DisplayMode::Command, &[commands::INVERTDISPLAY]),
false =>
self.write_sequence(DisplayMode::Command, &[commands::NORMALDISPLAY]),
}
}
pub fn blit_l1(&mut self, frame: &[[u8; 16]; 64]) -> Result<(),Box<error::Error>> {
use commands::*;
const BLIT_PREAMBLE_SEQUENCE: &'static [u8] = &[
SETCOLADDR, 0x00, 0x3F,
SETROWADDR, 0x00, 0x3F,
];
self.write_sequence(DisplayMode::Command, BLIT_PREAMBLE_SEQUENCE)?;
let mut sequence = [0u8; 64];
for line in frame.iter() {
unpack_line_for_display(line, &mut sequence);
self.write_sequence(DisplayMode::Data, &sequence)?;
}
Ok(())
}
fn reset(&mut self) -> Result<(),Box<error::Error>> {
self.control_channel.run_in_mode(DisplayMode::Reset, &mut move || {
thread::sleep(time::Duration::from_millis(10));
Ok(())
})?;
thread::sleep(time::Duration::from_millis(500));
Ok(())
}
fn write_sequence(&mut self, mode: DisplayMode, bytes: &[u8]) -> Result<(),Box<error::Error>> {
let mut transport = &mut self.transport;
self.control_channel.run_in_mode(mode, &mut move || {
let sent = transport.write(bytes).map_err(|e| Box::new(e))?;
if sent < bytes.len() {
Err(Box::new(DisplayError::WriteFailed))
} else {
Ok(())
}
})
}
}
fn unpack_pixels_for_display(packed: u8, unpacked: &mut [u8]) {
let mut pixel_group = packed;
for i in 0..4 {
let l = if (pixel_group & 0x80) != 0 { 0xF0 } else { 0x00 };
let r = if (pixel_group & 0x40) != 0 { 0x0F } else { 0x00 };
unpacked[i] = l | r;
pixel_group <<= 2;
}
}
fn unpack_line_for_display(line: &[u8; 16], unpacked: &mut [u8; 64]) {
for (index, pixel) in line.iter().enumerate() {
let range_start = index * 4;
let range_end = range_start + 4;
unpack_pixels_for_display(*pixel, &mut unpacked[range_start .. range_end]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unpack_pixels() {
let mut buffer = [0u8; 4];
unpack_pixels_for_display(0b00000000, &mut buffer);
assert_eq!(buffer, [0x00, 0x00, 0x00, 0x00]);
unpack_pixels_for_display(0b00001111, &mut buffer);
assert_eq!(buffer, [0x00, 0x00, 0xFF, 0xFF]);
unpack_pixels_for_display(0b11110000, &mut buffer);
assert_eq!(buffer, [0xFF, 0xFF, 0x00, 0x00]);
unpack_pixels_for_display(0b10100110, &mut buffer);
assert_eq!(buffer, [0xF0, 0xF0, 0x0F, 0xF0]);
unpack_pixels_for_display(0b01100101, &mut buffer);
assert_eq!(buffer, [0x0F, 0xF0, 0x0F, 0x0F]);
}
#[test]
fn test_unpack_line() {
let test_line: [u8; 16] = [
0b00000000, 0b00001111, 0b11110000, 0b10100110,
0b00000000, 0b00001111, 0b11110000, 0b10100110,
0b00000000, 0b00001111, 0b11110000, 0b10100110,
0b00000000, 0b00001111, 0b11110000, 0b10100110,
];
let mut result = [0u8; 64];
unpack_line_for_display(&test_line, &mut result);
for i in 0..4 {
let start = i * 16;
let sub_line = &result[start..start+16];
assert_eq!(&sub_line[ 0.. 4], [0x00, 0x00, 0x00, 0x00]);
assert_eq!(&sub_line[ 4.. 8], [0x00, 0x00, 0xFF, 0xFF]);
assert_eq!(&sub_line[ 8..12], [0xFF, 0xFF, 0x00, 0x00]);
assert_eq!(&sub_line[12..16], [0xF0, 0xF0, 0x0F, 0xF0]);
}
}
}