diff --git a/examples/embedded_linux_epd4in2/src/main.rs b/examples/embedded_linux_epd4in2/src/main.rs index 0b9d169..16ca13f 100644 --- a/examples/embedded_linux_epd4in2/src/main.rs +++ b/examples/embedded_linux_epd4in2/src/main.rs @@ -9,7 +9,6 @@ use eink_waveshare_rs::{ EPD4in2, drawing::{Graphics, color::Color}, WaveshareInterface, - ConnectionInterface }; use lin_hal::spidev::{self, SpidevOptions}; @@ -104,8 +103,7 @@ fn main() { //TODO: wait for Digital::InputPin //fixed currently with the HackInputPin, see further above - let connection_interface = ConnectionInterface::new(spi, cs, busy_in, dc, rst, delay); - let mut epd4in2 = EPD4in2::new(connection_interface).expect("eink inialize error"); + let mut epd4in2 = EPD4in2::new(spi, cs, busy_in, dc, rst, delay).expect("eink inialize error"); //let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; let mut buffer = [0u8; 15000]; diff --git a/src/drawing/color.rs b/src/drawing/color.rs index 1ee507d..81d1aee 100644 --- a/src/drawing/color.rs +++ b/src/drawing/color.rs @@ -1,7 +1,8 @@ /// Only for the B/W Displays atm +#[derive(Clone, Copy)] pub enum Color { Black, - White + White, } impl Color { @@ -9,7 +10,7 @@ impl Color { pub fn get_bit_value(&self) -> u8 { match self { Color::White => 1u8, - Color::Black => 0u8, + Color::Black => 0u8, } } @@ -21,9 +22,8 @@ impl Color { } } - /// Get the color encoding of a specific bit in a byte - /// + /// /// input is the byte where one bit is gonna be selected /// pos is counted from the left (highest value) from 0 to 7 /// remember: 1 is white, 0 is black @@ -61,7 +61,7 @@ impl Color { //TODO: does basically the same as get_color, so remove one of them? pub(crate) fn convert_color(input: u8, pos: u8, foreground_color: &Color) -> Color { - //match color: + //match color: // - white for "nothing to draw"/background drawing // - black for pixel to draw // @@ -72,4 +72,4 @@ impl Color { Color::inverse_color(foreground_color) } } -} \ No newline at end of file +} diff --git a/src/drawing/font.rs b/src/drawing/font.rs index 92d18cc..a63bf37 100644 --- a/src/drawing/font.rs +++ b/src/drawing/font.rs @@ -11,20 +11,33 @@ pub struct Font<'a> { first_char: u8, last_char: u8, bitmap: &'a [u8], - widthmap: &'a [u8] + widthmap: &'a [u8], } impl<'a> Font<'a> { /// Panics if either Bitmap or Widthmap of the Font are to small for the amount and size of chars - pub fn new(width: u8, height: u8, first_char: u8, last_char: u8, bitmap: &'a [u8], widthmap: &'a [u8]) -> Font<'a> { + pub fn new( + width: u8, + height: u8, + first_char: u8, + last_char: u8, + bitmap: &'a [u8], + widthmap: &'a [u8], + ) -> Font<'a> { //Assertion so it shouldn't be able to panic later let length_of_char = width as usize / 8 * height as usize; let amount_of_chars = last_char as usize - first_char as usize + 1; assert!(bitmap.len() >= amount_of_chars * length_of_char); assert!(widthmap.len() >= amount_of_chars); - Font {width, height, first_char, last_char, bitmap, widthmap } - + Font { + width, + height, + first_char, + last_char, + bitmap, + widthmap, + } } fn get_length_of_char(&self) -> usize { @@ -40,7 +53,7 @@ impl<'a> Font<'a> { let start_pos = self.get_char_pos(input) * self.get_length_of_char(); let end_pos = start_pos + self.get_length_of_char(); - &self.bitmap[start_pos .. end_pos] + &self.bitmap[start_pos..end_pos] } /// Can panic, when get_char_pos > widthmap.len(), should be caught in Font::new already @@ -49,7 +62,6 @@ impl<'a> Font<'a> { } } - #[cfg(test)] mod tests { use super::*; @@ -64,9 +76,10 @@ mod tests { 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, // '!' 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, // '"' 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x00, 0x00, // '#' - 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x00, 0x00]; // '$' + 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x00, 0x00, // '$' + ]; - let widthmap = [8,8,8,8]; + let widthmap = [8, 8, 8, 8]; let font = Font::new(8, 8, '!' as u8, '$' as u8, &bitmap, &widthmap); @@ -80,9 +93,6 @@ mod tests { assert_eq!(font.get_char_width('$'), widthmap[3]); } - - - #[test] fn bitmap_8x8_test() { let and = [0x36, 0x49, 0x55, 0x22, 0x50, 0x00, 0x00, 0x00]; @@ -90,7 +100,7 @@ mod tests { let first_value = [0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00]; let last_value = [0x00, 0x41, 0x36, 0x08, 0x00, 0x00, 0x00, 0x00]; - assert_eq!(bitmap_8x8('&'), and); + assert_eq!(bitmap_8x8('&'), and); assert_eq!(bitmap_8x8('ß'), zero); assert_eq!(bitmap_8x8('°'), zero); @@ -98,13 +108,10 @@ mod tests { assert_eq!(bitmap_8x8('!'), first_value); assert_eq!(bitmap_8x8('}'), last_value); - assert_eq!(bitmap_8x8('0')[1], 0x3E); + assert_eq!(bitmap_8x8('0')[1], 0x3E); } } - - - //bad font as the order is not the one we want to use //goes from bottom left -> up -> right pub(crate) fn bitmap_8x8(input: char) -> [u8; 8] { diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs index 08763cd..672010d 100644 --- a/src/drawing/mod.rs +++ b/src/drawing/mod.rs @@ -1,11 +1,9 @@ - pub mod font; use self::font::Font; pub mod color; use self::color::Color; - #[derive(Clone, Copy)] pub enum Displayorientation { /// No rotation @@ -42,28 +40,30 @@ impl Display { /// - Neccessary Buffersize pub fn get_dimensions(&self) -> (u16, u16, u16) { match self { - Display::Eink42BlackWhite => (400, 300, 15000) + Display::Eink42BlackWhite => (400, 300, 15000), } } } - - #[allow(dead_code)] pub struct Graphics<'a> { width: u16, height: u16, rotation: Displayorientation, - buffer: &'a mut [u8] - //buffer: Box//[u8; 15000], + buffer: &'a mut [u8], //buffer: Box//[u8; 15000] } impl<'a> Graphics<'a> { /// width needs to be a multiple of 8! - pub fn new(width: u16, height: u16, buffer: &'a mut [u8]) -> Graphics<'a>{ + pub fn new(width: u16, height: u16, buffer: &'a mut [u8]) -> Graphics<'a> { let len = buffer.len(); assert!(width / 8 * height >= len as u16); - Graphics {width, height, rotation: Displayorientation::Rotate0, buffer} + Graphics { + width, + height, + rotation: Displayorientation::Rotate0, + buffer, + } } /// Clears/Fills the full buffer with `color` @@ -78,16 +78,18 @@ impl<'a> Graphics<'a> { } /// Draw a single Pixel with `color` - /// + /// /// limited to i16::max images (buffer_size) at the moment pub fn draw_pixel(&mut self, x: u16, y: u16, color: &Color) { let (idx, bit) = match self.rotation { - Displayorientation::Rotate0 | Displayorientation::Rotate180 - => ((x as usize / 8 + (self.width as usize / 8) * y as usize) , - 0x80 >> (x % 8)), - Displayorientation::Rotate90 | Displayorientation::Rotate270 - => (y as usize / 8 * self.width as usize + x as usize, - 0x80 >> (y % 8)), + Displayorientation::Rotate0 | Displayorientation::Rotate180 => ( + (x as usize / 8 + (self.width as usize / 8) * y as usize), + 0x80 >> (x % 8), + ), + Displayorientation::Rotate90 | Displayorientation::Rotate270 => ( + y as usize / 8 * self.width as usize + x as usize, + 0x80 >> (y % 8), + ), }; if idx >= self.buffer.len() { @@ -96,8 +98,8 @@ impl<'a> Graphics<'a> { match color { Color::Black => { - self.buffer[idx] &= !bit; - }, + self.buffer[idx] &= !bit; + } Color::White => { self.buffer[idx] |= bit; } @@ -105,15 +107,17 @@ impl<'a> Graphics<'a> { } /// Draw a single Pixel with `color` - /// + /// /// limited to i16::max images (buffer_size) at the moment #[allow(dead_code)] fn draw_byte(&mut self, x: u16, y: u16, filling: u8, color: &Color) { let idx = match self.rotation { - Displayorientation::Rotate0 | Displayorientation::Rotate180 - => x as usize / 8 + (self.width as usize / 8) * y as usize, - Displayorientation::Rotate90 | Displayorientation::Rotate270 - => y as usize / 8 + (self.width as usize / 8) * x as usize, + Displayorientation::Rotate0 | Displayorientation::Rotate180 => { + x as usize / 8 + (self.width as usize / 8) * y as usize + }, + Displayorientation::Rotate90 | Displayorientation::Rotate270 => { + y as usize / 8 + (self.width as usize / 8) * x as usize + }, }; if idx >= self.buffer.len() { @@ -122,7 +126,7 @@ impl<'a> Graphics<'a> { match color { Color::Black => { - self.buffer[idx] = !filling; + self.buffer[idx] = !filling; }, Color::White => { self.buffer[idx] = filling; @@ -145,7 +149,6 @@ impl<'a> Graphics<'a> { } } - //TODO: add support for font_height = 0 //TODO: add support for char offset in y direction to reduce font file size fn draw_char_helper(&mut self, x0: u16, y0: u16, input: char, font: &Font, color: &Color) { @@ -156,18 +159,17 @@ impl<'a> Graphics<'a> { let buff = font.get_char(input); let char_width = font.get_char_width(input); - let mut row_counter = 0; let mut width_counter = 0u8; for &elem in buff.iter() { for _ in 0..8 { + self.draw_pixel( + x0 + u16::from(width_counter), + y0 + row_counter, + &Color::get_color(elem, width_counter % 8, color), + ); - self.draw_pixel( - x0 + u16::from(width_counter), - y0 + row_counter, - &Color::get_color(elem, width_counter % 8, color)); - - //Widthcounter shows how far we are in x direction + //Widthcounter shows how far we are in x direction width_counter += 1; // if we have reached if width_counter >= char_width { @@ -185,39 +187,48 @@ impl<'a> Graphics<'a> { // includes special draw_char instructions as this one is ordered columnwise and not rowwise (first byte == first 8 pixel columnwise) for &elem in (&font::bitmap_8x8(input)).iter() { for i in 0..8u8 { - self.draw_pixel(x0 + counter, y0 + 7 - u16::from(i), &Color::convert_color(elem, i, color)) + self.draw_pixel( + x0 + counter, + y0 + 7 - u16::from(i), + &Color::convert_color(elem, i, color), + ) } counter += 1; } } /// Draws Strings with 8x8 Chars (1 pixel padding included) - /// + /// /// Is quite small for the 400x300 E-Ink - /// + /// /// no autobreak line yet pub fn draw_string_8x8(&mut self, x0: u16, y0: u16, input: &str, color: &Color) { for (counter, input_char) in input.chars().enumerate() { - self.draw_char_8x8(x0 + counter as u16*8, y0, input_char, color); + self.draw_char_8x8( + x0 + counter as u16 * 8, + y0, + input_char, + color, + ); } } -// void plotLine(int x0, int y0, int x1, int y1) -// { -// int dx = abs(x1-x0), sx = x0= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ -// if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ -// } -// } + // void plotLine(int x0, int y0, int x1, int y1) + // { + // int dx = abs(x1-x0), sx = x0= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ + // if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ + // } + // } //bresenham algorithm for lines - /// draw line + /// draw line pub fn draw_line(&mut self, x0: u16, y0: u16, x1: u16, y1: u16, color: &Color) { let mut x0 = x0 as i16; let x1 = x1 as i16; @@ -227,9 +238,9 @@ impl<'a> Graphics<'a> { let dx = i16::abs(x1 - x0); let sx = if x0 < x1 { 1 } else { -1 }; - let dy = - i16::abs(y1 - y0); + let dy = -i16::abs(y1 - y0); let sy = if y0 < y1 { 1 } else { -1 }; - + let mut err = dx + dy; loop { @@ -239,7 +250,7 @@ impl<'a> Graphics<'a> { break; } - let e2 = 2*err; + let e2 = 2 * err; if e2 >= dy { err += dy; @@ -253,7 +264,7 @@ impl<'a> Graphics<'a> { } } - /// Draw a horizontal line + /// Draw a horizontal line /// TODO: maybe optimize by grouping up the bytes? But is it worth the longer and more complicated function? is it even faster? pub fn draw_horizontal_line(&mut self, x: u16, y: u16, length: u16, color: &Color) { for i in 0..length { @@ -297,69 +308,63 @@ impl<'a> Graphics<'a> { } } + fn draw_circle_helper(&mut self, x0: u16, y0: u16, radius: u16, filled: bool, color: &Color) { + let mut x = radius - 1; + let mut y = 0; + let mut dx = 1; + let mut dy = 1; + let mut err: i16 = dx - 2 * radius as i16; -fn draw_circle_helper(&mut self, x0: u16, y0: u16, radius: u16, filled: bool, color: &Color) { - let mut x = radius - 1; - let mut y = 0; - let mut dx = 1; - let mut dy = 1; - let mut err: i16 = dx - 2 * radius as i16; - - while x >= y { - if filled { - self.circle_helper_filled_putpixel(x0, y0, x, y, color); - } else { - self.circle_helper_putpixel(x0, y0, x, y, color); - } + while x >= y { + if filled { + self.circle_helper_filled_putpixel(x0, y0, x, y, color); + } else { + self.circle_helper_putpixel(x0, y0, x, y, color); + } - if err <= 0 { - y += 1; - err += dy; - dy += 2; - } + if err <= 0 { + y += 1; + err += dy; + dy += 2; + } - if err > 0 { - x -= 1; - dx += 2; - err += dx - 2 * radius as i16; + if err > 0 { + x -= 1; + dx += 2; + err += dx - 2 * radius as i16; + } } } -} - -fn circle_helper_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, color: &Color) { - self.draw_horizontal_line(x0 - x, y0 + y, 2*x, color); - // self.draw_pixel(buffer, x0 + x, y0 + y, color); - // self.draw_pixel(buffer, x0 - x, y0 + y, color); - - self.draw_horizontal_line(x0 - y, y0 + x, 2*y, color); - // self.draw_pixel(buffer, x0 + y, y0 + x, color); - // self.draw_pixel(buffer, x0 - y, y0 + x, color); - - self.draw_horizontal_line(x0 - x, y0 - y, 2*x, color); - // self.draw_pixel(buffer, x0 - x, y0 - y, color); - // self.draw_pixel(buffer, x0 + x, y0 - y, color); - - self.draw_horizontal_line(x0 - y, y0 - y, 2*y, color); - // self.draw_pixel(buffer, x0 - y, y0 - x, color); - // self.draw_pixel(buffer, x0 + y, y0 - x, color); - -} + fn circle_helper_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, color: &Color) { + self.draw_horizontal_line(x0 - x, y0 + y, 2 * x, color); + // self.draw_pixel(buffer, x0 + x, y0 + y, color); + // self.draw_pixel(buffer, x0 - x, y0 + y, color); -//TODO: Test -fn circle_helper_filled_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, color: &Color) { - self.draw_pixel(x0 + x, y0 + y, color); - self.draw_pixel(x0 + y, y0 + x, color); - self.draw_pixel(x0 - y, y0 + x, color); - self.draw_pixel(x0 - x, y0 + y, color); - self.draw_pixel(x0 - x, y0 - y, color); - self.draw_pixel(x0 - y, y0 - x, color); - self.draw_pixel(x0 + y, y0 - x, color); - self.draw_pixel(x0 + x, y0 - y, color); -} + self.draw_horizontal_line(x0 - y, y0 + x, 2 * y, color); + // self.draw_pixel(buffer, x0 + y, y0 + x, color); + // self.draw_pixel(buffer, x0 - y, y0 + x, color); + self.draw_horizontal_line(x0 - x, y0 - y, 2 * x, color); + // self.draw_pixel(buffer, x0 - x, y0 - y, color); + // self.draw_pixel(buffer, x0 + x, y0 - y, color); + self.draw_horizontal_line(x0 - y, y0 - y, 2 * y, color); + // self.draw_pixel(buffer, x0 - y, y0 - x, color); + // self.draw_pixel(buffer, x0 + y, y0 - x, color); + } + //TODO: Test + fn circle_helper_filled_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, color: &Color) { + self.draw_pixel(x0 + x, y0 + y, color); + self.draw_pixel(x0 + y, y0 + x, color); + self.draw_pixel(x0 - y, y0 + x, color); + self.draw_pixel(x0 - x, y0 + y, color); + self.draw_pixel(x0 - x, y0 - y, color); + self.draw_pixel(x0 - y, y0 - x, color); + self.draw_pixel(x0 + y, y0 - x, color); + self.draw_pixel(x0 + x, y0 - y, color); + } ///TODO: test if circle looks good /// Draws a circle @@ -373,7 +378,7 @@ fn circle_helper_filled_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, c let radius = radius as i16; let x_mid = x as i16; let y_mid = y as i16; - let mut x_pos: i16 = 0 - radius; + let mut x_pos: i16 = 0 - radius; let mut y_pos = 0; let mut err: i16 = 2 - 2 * radius; @@ -387,12 +392,12 @@ fn circle_helper_filled_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, c if radius <= y_pos { y_pos += 1; - err += y_pos*2 + 1; + err += y_pos * 2 + 1; } if radius > x_pos || err > y_pos { x_pos += 1; - err += x_pos*2 + 1; + err += x_pos * 2 + 1; } if x_pos >= 0 { @@ -401,16 +406,12 @@ fn circle_helper_filled_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, c } } - ///TODO: test! pub fn draw_filled_circle(&mut self, x0: u16, y0: u16, radius: u16, color: &Color) { self.draw_circle_helper(x0, y0, radius, true, color); } - - } - /* ############ ############ ############ ############ @@ -424,8 +425,6 @@ fn circle_helper_filled_putpixel(&mut self, x0: u16, y0: u16, x: u16, y: u16, c */ - - #[cfg(test)] mod graphics { use super::*; @@ -435,15 +434,12 @@ mod graphics { let mut buffer = [Color::White.get_byte_value(); 150]; let mut graphics = Graphics::new(40, 30, &mut buffer); graphics.draw_filled_rectangle(0, 0, 40, 30, &Color::Black); - + assert_eq!(graphics.buffer[0], Color::Black.get_byte_value()); for &elem in graphics.buffer.iter() { - assert_eq!(elem, Color::Black.get_byte_value()); } - - } /// draw a 4x4 in the top left corner @@ -452,12 +448,12 @@ mod graphics { let mut buffer = [Color::White.get_byte_value(); 8]; let mut graphics = Graphics::new(8, 8, &mut buffer); graphics.draw_filled_rectangle(0, 0, 4, 4, &Color::Black); - + assert_eq!(graphics.buffer[0], 0x0f); let mut counter = 0; for &elem in graphics.buffer.iter() { - counter += 1; + counter += 1; if counter <= 4 { assert_eq!(elem, 0x0f); @@ -465,8 +461,6 @@ mod graphics { assert_eq!(elem, Color::White.get_byte_value()); } } - - } #[test] @@ -474,7 +468,7 @@ mod graphics { let mut buffer = [Color::White.get_byte_value(); 4]; let mut graphics = Graphics::new(16, 2, &mut buffer); graphics.draw_horizontal_line(1, 0, 14, &Color::Black); - + assert_eq!(graphics.buffer[0], 0x80); assert_eq!(graphics.buffer[1], 0x01); assert_eq!(graphics.buffer[2], Color::White.get_byte_value()); @@ -488,12 +482,10 @@ mod graphics { graphics.draw_vertical_line(0, 0, 8, &Color::Black); graphics.draw_vertical_line(5, 0, 8, &Color::Black); - - + assert_eq!(graphics.buffer[0], 0x7b); for &elem in graphics.buffer.iter() { - assert_eq!(elem, 0x7bu8); } } @@ -509,9 +501,9 @@ mod graphics { let mut buffer2 = [Color::White.get_byte_value(); 8]; let mut graphics2 = Graphics::new(8, 8, &mut buffer2); - graphics2.draw_line(5, 0, 5, 8, &Color::Black); + graphics2.draw_line(5, 0, 5, 8, &Color::Black); - for i in 0..graphics.buffer.len() { + for i in 0..graphics.buffer.len() { assert_eq!(graphics.buffer[i], graphics2.buffer[i]); } } @@ -525,9 +517,9 @@ mod graphics { let mut buffer2 = [Color::White.get_byte_value(); 4]; let mut graphics2 = Graphics::new(16, 2, &mut buffer2); - graphics2.draw_line(1, 0, 14, 0, &Color::Black); + graphics2.draw_line(1, 0, 14, 0, &Color::Black); - for i in 0..graphics.buffer.len() { + for i in 0..graphics.buffer.len() { assert_eq!(graphics.buffer[i], graphics2.buffer[i]); } } @@ -538,15 +530,13 @@ mod graphics { let mut buffer = [Color::White.get_byte_value(); 8]; let mut graphics = Graphics::new(8, 8, &mut buffer); - graphics.draw_line(0, 0, 16, 16, &Color::Black); + graphics.draw_line(0, 0, 16, 16, &Color::Black); - for i in 0..graphics.buffer.len() { + for i in 0..graphics.buffer.len() { assert_eq!(graphics.buffer[i], !(0x80 >> i % 8)); } } - - #[test] fn test_pixel() { let mut buffer = [Color::White.get_byte_value(); 8]; @@ -555,7 +545,6 @@ mod graphics { assert_eq!(graphics.buffer[0], !0x40); - let mut buffer = [Color::White.get_byte_value(); 16]; let mut graphics = Graphics::new(16, 8, &mut buffer); graphics.draw_pixel(9, 0, &Color::Black); @@ -573,15 +562,14 @@ mod graphics { for i in 1..graphics.buffer.len() { assert_eq!(graphics.buffer[i], Color::White.get_byte_value()); - } + } - graphics.draw_byte(0, 0, 0x5A, &Color::Black) ; + graphics.draw_byte(0, 0, 0x5A, &Color::Black); assert_eq!(graphics.buffer[0], !0x5A); } #[test] fn test_char_with_8x8_font() { - // Test ! let mut buffer = [Color::White.get_byte_value(); 8]; let mut graphics = Graphics::new(8, 8, &mut buffer); @@ -592,8 +580,7 @@ mod graphics { } assert_eq!(graphics.buffer[5], Color::White.get_byte_value()); assert_eq!(graphics.buffer[6], !0x20); - assert_eq!(graphics.buffer[7], Color::White.get_byte_value()); - + assert_eq!(graphics.buffer[7], Color::White.get_byte_value()); // Test H let mut buffer = [Color::White.get_byte_value(); 8]; @@ -603,36 +590,34 @@ mod graphics { for i in 0..3 { assert_eq!(graphics.buffer[i], !0x88); } - assert_eq!(graphics.buffer[3], !0xF8); + assert_eq!(graphics.buffer[3], !0xF8); for i in 4..7 { assert_eq!(graphics.buffer[i], !0x88); } - assert_eq!(graphics.buffer[7], Color::White.get_byte_value()); + assert_eq!(graphics.buffer[7], Color::White.get_byte_value()); } #[test] fn test_string_with_8x8_font() { - // Test !H let mut buffer = [Color::White.get_byte_value(); 16]; let mut graphics = Graphics::new(16, 8, &mut buffer); graphics.draw_string_8x8(0, 0, "!H", &Color::Black); for i in 0..5 { - assert_eq!(graphics.buffer[i*2], !0x20); + assert_eq!(graphics.buffer[i * 2], !0x20); } - assert_eq!(graphics.buffer[5*2], Color::White.get_byte_value()); - assert_eq!(graphics.buffer[6*2], !0x20); - assert_eq!(graphics.buffer[7*2], Color::White.get_byte_value()); - + assert_eq!(graphics.buffer[5 * 2], Color::White.get_byte_value()); + assert_eq!(graphics.buffer[6 * 2], !0x20); + assert_eq!(graphics.buffer[7 * 2], Color::White.get_byte_value()); for i in 0..3 { - assert_eq!(graphics.buffer[i*2 + 1], !0x88); + assert_eq!(graphics.buffer[i * 2 + 1], !0x88); } - assert_eq!(graphics.buffer[3*2 + 1], !0xF8); + assert_eq!(graphics.buffer[3 * 2 + 1], !0xF8); for i in 4..7 { - assert_eq!(graphics.buffer[i*2 + 1], !0x88); + assert_eq!(graphics.buffer[i * 2 + 1], !0x88); } - assert_eq!(graphics.buffer[7*2 + 1], Color::White.get_byte_value()); + assert_eq!(graphics.buffer[7 * 2 + 1], Color::White.get_byte_value()); } -} \ No newline at end of file +} diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index ccaf7c5..ce743c6 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -1,5 +1,5 @@ //! A simple Driver for the Waveshare 1.54" E-Ink Display via SPI -//! +//! //! //! # Examples from the 4.2" Display. It should work the same for the 1.54" one. //! @@ -7,15 +7,15 @@ //! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap(); //! //! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; -//! +//! //! // draw something into the buffer -//! +//! //! epd4in2.display_and_transfer_buffer(buffer, None); -//! +//! //! // wait and look at the image -//! +//! //! epd4in2.clear_frame(None); -//! +//! //! epd4in2.sleep(); //! ``` @@ -25,210 +25,185 @@ const HEIGHT: u16 = 200; const DEFAULT_BACKGROUND_COLOR: Color = Color::White; use hal::{ - blocking::{ - spi::Write, - delay::* - }, - digital::* + blocking::{delay::*, spi::Write}, + digital::*, }; -use type_a::{ - LUT_FULL_UPDATE, - LUT_PARTIAL_UPDATE, - command::Command -}; +use type_a::{command::Command, LUT_FULL_UPDATE, LUT_PARTIAL_UPDATE}; use drawing::color::Color; - - - use interface::*; use interface::connection_interface::ConnectionInterface; - - - /// EPD1in54 driver /// -pub struct EPD1in54 { +pub struct EPD1in54 { /// SPI - interface: ConnectionInterface, + interface: ConnectionInterface, /// EPD (width, height) //epd: EPD, /// Color - background_color: Color, + background_color: Color, } -impl EPD1in54 -where +impl EPD1in54 +where SPI: Write, CS: OutputPin, BUSY: InputPin, - DataCommand: OutputPin, - RST: OutputPin, - Delay: DelayUs + DelayMs -{ - -} - - -impl WaveshareInterface - for EPD1in54 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DataCommand: OutputPin, + DC: OutputPin, RST: OutputPin, Delay: DelayUs + DelayMs, -{ - - fn get_width(&self) -> u16 { - WIDTH - } - - fn get_height(&self) -> u16 { - HEIGHT - } - - - fn new( - interface: ConnectionInterface - ) -> Result { - - let mut epd = EPD1in54 {interface, background_color: DEFAULT_BACKGROUND_COLOR}; - - epd.init()?; - - Ok(epd) - } - - - +{ fn init(&mut self) -> Result<(), E> { - - - self.reset(); + self.interface.reset(); // 3 Databytes: // A[7:0] // 0.. A[8] // 0.. B[2:0] // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) - self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; - self.interface.send_data(HEIGHT as u8)?; - self.interface.send_data((HEIGHT >> 8) as u8)?; - self.interface.send_data(0x00)?; + self.interface.command(Command::DRIVER_OUTPUT_CONTROL)?; + self.interface.data(HEIGHT as u8)?; + self.interface.data((HEIGHT >> 8) as u8)?; + self.interface.data(0x00)?; // 3 Databytes: (and default values from datasheet and arduino) // 1 .. A[6:0] = 0xCF | 0xD7 // 1 .. B[6:0] = 0xCE | 0xD6 // 1 .. C[6:0] = 0x8D | 0x9D //TODO: test - self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; - self.interface.send_data(0xD7)?; - self.interface.send_data(0xD6)?; - self.interface.send_data(0x9D)?; + self.interface.command(Command::BOOSTER_SOFT_START_CONTROL)?; + self.interface.data(0xD7)?; + self.interface.data(0xD6)?; + self.interface.data(0x9D)?; // One Databyte with value 0xA8 for 7V VCOM - self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; - self.interface.send_data(0xA8)?; + self.interface.command_with_data(Command::WRITE_VCOM_REGISTER, &[0xA8])?; // One Databyte with default value 0x1A for 4 dummy lines per gate - self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; - self.interface.send_data(0x1A)?; + self.interface.command_with_data(Command::SET_DUMMY_LINE_PERIOD, &[0x1A])?; // One Databyte with default value 0x08 for 2us per line - self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; - self.interface.send_data(0x08)?; + self.interface.command_with_data(Command::SET_GATE_LINE_WIDTH, &[0x08])?; // One Databyte with default value 0x03 // -> address: x increment, y increment, address counter is updated in x direction - self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; - self.interface.send_data(0x03)?; + self.interface.command_with_data(Command::DATA_ENTRY_MODE_SETTING, &[0x03])?; self.set_lut() } - fn sleep(&mut self) -> Result<(), E> { +} - self.interface.send_command(Command::DEEP_SLEEP_MODE)?; - // 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode - //TODO: is 0x00 needed here? - self.interface.send_data(0x00)?; +impl WaveshareInterface + for EPD1in54 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + fn get_width(&self) -> u16 { + WIDTH + } - self.wait_until_idle(); - Ok(()) + fn get_height(&self) -> u16 { + HEIGHT } + fn new( + spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: Delay, + ) -> Result { + let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); + + let mut epd = EPD1in54 { + interface, + background_color: DEFAULT_BACKGROUND_COLOR, + }; + + epd.init()?; - fn reset(&mut self) { - self.interface.reset() + Ok(epd) } - fn delay_ms(&mut self, delay: u16) { - self.interface.delay_ms(delay) + fn wake_up(&mut self) -> Result<(), E> { + self.init() } - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ - self.use_full_frame()?; + fn sleep(&mut self) -> Result<(), E> { + // 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode + //TODO: is 0x00 needed here or would 0x01 be even more efficient? + self.interface.command_with_data(Command::DEEP_SLEEP_MODE, &[0x00])?; - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) + self.wait_until_idle(); + Ok(()) + } + + fn delay_ms(&mut self, delay: u16) { + self.interface.delay_ms(delay) + } + + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E> { + self.use_full_frame()?; + self.interface.command_with_data(Command::WRITE_RAM, buffer) } //TODO: update description: last 3 bits will be ignored for width and x_pos - fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ + fn update_partial_frame( + &mut self, + buffer: &[u8], + x: u16, + y: u16, + width: u16, + height: u16, + ) -> Result<(), E> { self.set_ram_area(x, y, x + width, y + height)?; self.set_ram_counter(x, y)?; - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) + self.interface.command_with_data(Command::WRITE_RAM, buffer) } - - fn display_frame(&mut self) -> Result<(), E>{ + fn display_frame(&mut self) -> Result<(), E> { // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) - self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; - self.interface.send_data(0xC4)?; + self.interface.command_with_data(Command::DISPLAY_UPDATE_CONTROL_2, &[0xC4])?; - self.interface.send_command(Command::MASTER_ACTIVATION)?; + self.interface.command(Command::MASTER_ACTIVATION)?; // MASTER Activation should not be interupted to avoid currption of panel images // therefore a terminate command is send - self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) - } - - - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ - self.update_frame(buffer)?; - self.display_frame() + self.interface.command(Command::NOP) } - - fn clear_frame(&mut self) -> Result<(), E>{ + fn clear_frame(&mut self) -> Result<(), E> { self.use_full_frame()?; // clear the ram with the background color let color = self.background_color.get_byte_value(); - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) + self.interface.command(Command::WRITE_RAM)?; + self.interface.data_x_times(color, WIDTH / 8 * HEIGHT) } - /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) - fn set_background_color(&mut self, background_color: Color){ + + fn set_background_color(&mut self, background_color: Color) { self.background_color = background_color; } + + fn background_color(&self) -> &Color { + &self.background_color + } } impl EPD1in54 -where +where SPI: Write, CS: OutputPin, BUSY: InputPin, @@ -239,54 +214,62 @@ where fn wait_until_idle(&mut self) { self.interface.wait_until_idle(false); } - + pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { // choose full frame/ram self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; // start from the beginning - self.set_ram_counter(0,0) + self.set_ram_counter(0, 0) } - - pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { + + pub(crate) fn set_ram_area( + &mut self, + start_x: u16, + start_y: u16, + end_x: u16, + end_y: u16, + ) -> Result<(), E> { assert!(start_x < end_x); assert!(start_y < end_y); // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram - // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; - self.interface.send_data((start_x >> 3) as u8)?; - self.interface.send_data((end_x >> 3) as u8)?; + // aren't relevant + self.interface.command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; + self.interface.data((start_x >> 3) as u8)?; + self.interface.data((end_x >> 3) as u8)?; // 2 Databytes: A[7:0] & 0..A[8] for each - start and end - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; - self.interface.send_data(start_y as u8)?; - self.interface.send_data((start_y >> 8) as u8)?; - self.interface.send_data(end_y as u8)?; - self.interface.send_data((end_y >> 8) as u8) + self.interface.command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; + self.interface.data(start_y as u8)?; + self.interface.data((start_y >> 8) as u8)?; + self.interface.data(end_y as u8)?; + self.interface.data((end_y >> 8) as u8) } pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; - self.interface.send_data((x >> 3) as u8)?; + self.interface.command_with_data(Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?; // 2 Databytes: A[7:0] & 0..A[8] - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; - self.interface.send_data(y as u8)?; - self.interface.send_data((y >> 8) as u8)?; + self.interface.command_with_data( + Command::SET_RAM_Y_ADDRESS_COUNTER, + &[ + y as u8, + (y >> 8) as u8 + ])?; self.wait_until_idle(); Ok(()) } - /// Uses the slower full update + /// Uses the slower full update pub fn set_lut(&mut self) -> Result<(), E> { self.set_lut_helper(&LUT_FULL_UPDATE) } - /// Uses the quick partial refresh + /// Uses the quick partial refresh pub fn set_lut_quick(&mut self) -> Result<(), E> { self.set_lut_helper(&LUT_PARTIAL_UPDATE) } @@ -296,11 +279,8 @@ where // self.set_lut_helper(buffer) //} - fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { assert!(buffer.len() == 30); - self.interface.send_command(Command::WRITE_LUT_REGISTER)?; - self.interface.send_multiple_data(buffer) + self.interface.command_with_data(Command::WRITE_LUT_REGISTER, buffer) } - -} \ No newline at end of file +} diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index d6e74e4..bc6e0e7 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -1,5 +1,5 @@ //! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI -//! +//! //! //! # Examples from the 4.2" Display. It should work the same for the 2.9" one. //! @@ -7,15 +7,15 @@ //! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap(); //! //! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; -//! +//! //! // draw something into the buffer -//! +//! //! epd4in2.display_and_transfer_buffer(buffer, None); -//! +//! //! // wait and look at the image -//! +//! //! epd4in2.clear_frame(None); -//! +//! //! epd4in2.sleep(); //! ``` @@ -24,214 +24,208 @@ const HEIGHT: u16 = 296; const DEFAULT_BACKGROUND_COLOR: Color = Color::White; use hal::{ - blocking::{ - spi::Write, - delay::* - }, - digital::* + blocking::{delay::*, spi::Write}, + digital::*, }; - -use type_a::{ - LUT_FULL_UPDATE, - LUT_PARTIAL_UPDATE, - command::Command -}; +use type_a::{command::Command, LUT_FULL_UPDATE, LUT_PARTIAL_UPDATE}; use drawing::color::Color; - - - use interface::*; use interface::connection_interface::ConnectionInterface; - - - /// EPD2in9 driver /// -pub struct EPD2in9 { +pub struct EPD2in9 { /// SPI - interface: ConnectionInterface, + interface: ConnectionInterface, /// EPD (width, height) //epd: EPD, /// Color - background_color: Color, + background_color: Color, } -impl EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DataCommand: OutputPin, - RST: OutputPin, - Delay: DelayUs + DelayMs -{ - -} - - -impl WaveshareInterface - for EPD2in9 -where +impl EPD2in9 +where SPI: Write, CS: OutputPin, BUSY: InputPin, - DataCommand: OutputPin, + DC: OutputPin, RST: OutputPin, Delay: DelayUs + DelayMs, -{ - - fn get_width(&self) -> u16 { - WIDTH - } - - fn get_height(&self) -> u16 { - HEIGHT - } - - - fn new( - interface: ConnectionInterface - ) -> Result { - //let epd = EPD::new(WIDTH, HEIGHT); - //let background_color = Color::White; - - let mut epd = EPD2in9 {interface, background_color: DEFAULT_BACKGROUND_COLOR}; - - - epd.init()?; - - Ok(epd) - } - - - +{ fn init(&mut self) -> Result<(), E> { - - - self.reset(); + self.interface.reset(); // 3 Databytes: // A[7:0] // 0.. A[8] // 0.. B[2:0] // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) - self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; - self.interface.send_data(HEIGHT as u8)?; - self.interface.send_data((HEIGHT >> 8) as u8)?; - self.interface.send_data(0x00)?; + self.interface.command(Command::DRIVER_OUTPUT_CONTROL)?; + self.interface.data(HEIGHT as u8)?; + self.interface.data((HEIGHT >> 8) as u8)?; + self.interface.data(0x00)?; // 3 Databytes: (and default values from datasheet and arduino) // 1 .. A[6:0] = 0xCF | 0xD7 // 1 .. B[6:0] = 0xCE | 0xD6 // 1 .. C[6:0] = 0x8D | 0x9D //TODO: test - self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; - self.interface.send_data(0xD7)?; - self.interface.send_data(0xD6)?; - self.interface.send_data(0x9D)?; + self.interface.command(Command::BOOSTER_SOFT_START_CONTROL)?; + self.interface.data(0xD7)?; + self.interface.data(0xD6)?; + self.interface.data(0x9D)?; // One Databyte with value 0xA8 for 7V VCOM - self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; - self.interface.send_data(0xA8)?; + self.interface.command(Command::WRITE_VCOM_REGISTER)?; + self.interface.data(0xA8)?; // One Databyte with default value 0x1A for 4 dummy lines per gate - self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; - self.interface.send_data(0x1A)?; + self.interface.command(Command::SET_DUMMY_LINE_PERIOD)?; + self.interface.data(0x1A)?; // One Databyte with default value 0x08 for 2us per line - self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; - self.interface.send_data(0x08)?; + self.interface.command(Command::SET_GATE_LINE_WIDTH)?; + self.interface.data(0x08)?; // One Databyte with default value 0x03 // -> address: x increment, y increment, address counter is updated in x direction - self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; - self.interface.send_data(0x03)?; + self.interface.command(Command::DATA_ENTRY_MODE_SETTING)?; + self.interface.data(0x03)?; self.set_lut() } - fn sleep(&mut self) -> Result<(), E> { - self.interface.send_command(Command::DEEP_SLEEP_MODE)?; +} + +impl + WaveshareInterface + for EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + fn get_width(&self) -> u16 { + WIDTH + } + + fn get_height(&self) -> u16 { + HEIGHT + } + + fn new( + spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: Delay, + ) -> Result { + let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); + + let mut epd = EPD2in9 { + interface, + background_color: DEFAULT_BACKGROUND_COLOR, + }; + + epd.init()?; + + Ok(epd) + } + + + + fn sleep(&mut self) -> Result<(), ERR> { // 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode - //TODO: is 0x00 needed here? - self.interface.send_data(0x00)?; + //TODO: is 0x00 needed here? (see also epd1in54) + self.interface.command_with_data(Command::DEEP_SLEEP_MODE, &[0x00])?; self.wait_until_idle(); Ok(()) } - - fn reset(&mut self) { - self.interface.reset() + fn wake_up(&mut self) -> Result<(), ERR> { + self.init() } fn delay_ms(&mut self, delay: u16) { self.interface.delay_ms(delay) } - - - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), ERR> { self.use_full_frame()?; - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) + self.interface.command_with_data(Command::WRITE_RAM, buffer) } //TODO: update description: last 3 bits will be ignored for width and x_pos - fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ + fn update_partial_frame( + &mut self, + buffer: &[u8], + x: u16, + y: u16, + width: u16, + height: u16, + ) -> Result<(), ERR> { self.set_ram_area(x, y, x + width, y + height)?; self.set_ram_counter(x, y)?; - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) + self.interface.command_with_data(Command::WRITE_RAM, buffer) } - - fn display_frame(&mut self) -> Result<(), E>{ + fn display_frame(&mut self) -> Result<(), ERR> { // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) - self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; - self.interface.send_data(0xC4)?; + self.interface.command_with_data(Command::DISPLAY_UPDATE_CONTROL_2, &[0xC4])?; - self.interface.send_command(Command::MASTER_ACTIVATION)?; + self.interface.command(Command::MASTER_ACTIVATION)?; // MASTER Activation should not be interupted to avoid currption of panel images // therefore a terminate command is send - self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) + self.interface.command(Command::NOP) } - - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), ERR> { self.update_frame(buffer)?; self.display_frame() } - - fn clear_frame(&mut self) -> Result<(), E>{ + fn update_and_display_partial_frame( + &mut self, + buffer: &[u8], + x: u16, + y: u16, + width: u16, + height: u16, + ) -> Result<(), ERR> { + self.update_partial_frame(buffer, x, y, width, height)?; + self.display_frame() + } + + fn clear_frame(&mut self) -> Result<(), ERR> { self.use_full_frame()?; // clear the ram with the background color let color = self.background_color.get_byte_value(); - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) + self.interface.command(Command::WRITE_RAM)?; + self.interface.data_x_times(color, WIDTH / 8 * HEIGHT) } /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) - fn set_background_color(&mut self, background_color: Color){ + fn set_background_color(&mut self, background_color: Color) { self.background_color = background_color; } + fn background_color(&self) -> &Color { + &self.background_color + } } impl EPD2in9 -where +where SPI: Write, CS: OutputPin, BUSY: InputPin, @@ -242,54 +236,59 @@ where fn wait_until_idle(&mut self) { self.interface.wait_until_idle(false); } - + pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { // choose full frame/ram self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; // start from the beginning - self.set_ram_counter(0,0) + self.set_ram_counter(0, 0) } - - pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { + + pub(crate) fn set_ram_area( + &mut self, + start_x: u16, + start_y: u16, + end_x: u16, + end_y: u16, + ) -> Result<(), E> { assert!(start_x < end_x); assert!(start_y < end_y); // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram - // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; - self.interface.send_data((start_x >> 3) as u8)?; - self.interface.send_data((end_x >> 3) as u8)?; + // aren't relevant + self.interface.command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; + self.interface.data((start_x >> 3) as u8)?; + self.interface.data((end_x >> 3) as u8)?; // 2 Databytes: A[7:0] & 0..A[8] for each - start and end - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; - self.interface.send_data(start_y as u8)?; - self.interface.send_data((start_y >> 8) as u8)?; - self.interface.send_data(end_y as u8)?; - self.interface.send_data((end_y >> 8) as u8) + self.interface.command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; + self.interface.data(start_y as u8)?; + self.interface.data((start_y >> 8) as u8)?; + self.interface.data(end_y as u8)?; + self.interface.data((end_y >> 8) as u8) } pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; - self.interface.send_data((x >> 3) as u8)?; + self.interface.command_with_data(Command::SET_RAM_X_ADDRESS_COUNTER, &[(x >> 3) as u8])?; // 2 Databytes: A[7:0] & 0..A[8] - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; - self.interface.send_data(y as u8)?; - self.interface.send_data((y >> 8) as u8)?; + self.interface.command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; + self.interface.data(y as u8)?; + self.interface.data((y >> 8) as u8)?; self.wait_until_idle(); Ok(()) } - /// Uses the slower full update + /// Uses the slower full update pub fn set_lut(&mut self) -> Result<(), E> { self.set_lut_helper(&LUT_FULL_UPDATE) } - /// Uses the quick partial refresh + /// Uses the quick partial refresh pub fn set_lut_quick(&mut self) -> Result<(), E> { self.set_lut_helper(&LUT_PARTIAL_UPDATE) } @@ -299,11 +298,8 @@ where // self.set_lut_helper(buffer) //} - fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { assert!(buffer.len() == 30); - self.interface.send_command(Command::WRITE_LUT_REGISTER)?; - self.interface.send_multiple_data(buffer) + self.interface.command_with_data(Command::WRITE_LUT_REGISTER, buffer) } - -} \ No newline at end of file +} diff --git a/src/epd4in2/command.rs b/src/epd4in2/command.rs index c41b951..75800cf 100644 --- a/src/epd4in2/command.rs +++ b/src/epd4in2/command.rs @@ -1,170 +1,167 @@ //! SPI Commands for the Waveshare 4.2" E-Ink Display use interface; /// EPD4IN2 commands -/// +/// /// Should rarely (never?) be needed directly. -/// -/// For more infos about the addresses and what they are doing look into the pdfs -/// +/// +/// For more infos about the addresses and what they are doing look into the pdfs +/// /// The description of the single commands is mostly taken from IL0398.pdf #[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] pub(crate) enum Command { /// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift direction, booster switch, soft reset - PANEL_SETTING = 0x00, - /// selecting internal and external power - POWER_SETTING = 0x01, - /// After the Power Off command, the driver will power off following the Power Off Sequence. This command will turn off charge - /// pump, T-con, source driver, gate driver, VCOM, and temperature sensor, but register data will be kept until VDD becomes OFF. - /// Source Driver output and Vcom will remain as previous condition, which may have 2 conditions: floating. - POWER_OFF = 0x02, - /// Setting Power OFF sequence - POWER_OFF_SEQUENCE_SETTING = 0x03, - /// Turning On the Power - POWER_ON = 0x04, - /// This command enables the internal bandgap, which will be cleared by the next POF. - POWER_ON_MEASURE = 0x05, - /// Starting data transmission - BOOSTER_SOFT_START = 0x06, - /// After this command is transmitted, the chip would enter the deep-sleep mode to save power. - /// - /// The deep sleep mode would return to standby by hardware reset. - /// - /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. - DEEP_SLEEP = 0x07, - /// This command starts transmitting data and write them into SRAM. To complete data transmission, command DSP (Data - /// transmission Stop) must be issued. Then the chip will start to send data/VCOM for panel. - /// - /// - In B/W mode, this command writes “OLD” data to SRAM. - /// - In B/W/Red mode, this command writes “B/W” data to SRAM. - /// - In Program mode, this command writes “OTP” data to SRAM for programming. - DATA_START_TRANSMISSION_1 = 0x10, - /// Stopping data transmission - DATA_STOP = 0x11, - /// While user sent this command, driver will refresh display (data/VCOM) according to SRAM data and LUT. - /// - /// After Display Refresh command, BUSY_N signal will become “0” and the refreshing of panel starts. - DISPLAY_REFRESH = 0x12, - /// This command starts transmitting data and write them into SRAM. To complete data transmission, command DSP (Data - /// transmission Stop) must be issued. Then the chip will start to send data/VCOM for panel. - /// - In B/W mode, this command writes “NEW” data to SRAM. - /// - In B/W/Red mode, this command writes “RED” data to SRAM. - DATA_START_TRANSMISSION_2 = 0x13, + PANEL_SETTING = 0x00, + /// selecting internal and external power + POWER_SETTING = 0x01, + /// After the Power Off command, the driver will power off following the Power Off Sequence. This command will turn off charge + /// pump, T-con, source driver, gate driver, VCOM, and temperature sensor, but register data will be kept until VDD becomes OFF. + /// Source Driver output and Vcom will remain as previous condition, which may have 2 conditions: floating. + POWER_OFF = 0x02, + /// Setting Power OFF sequence + POWER_OFF_SEQUENCE_SETTING = 0x03, + /// Turning On the Power + POWER_ON = 0x04, + /// This command enables the internal bandgap, which will be cleared by the next POF. + POWER_ON_MEASURE = 0x05, + /// Starting data transmission + BOOSTER_SOFT_START = 0x06, + /// After this command is transmitted, the chip would enter the deep-sleep mode to save power. + /// + /// The deep sleep mode would return to standby by hardware reset. + /// + /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. + DEEP_SLEEP = 0x07, + /// This command starts transmitting data and write them into SRAM. To complete data transmission, command DSP (Data + /// transmission Stop) must be issued. Then the chip will start to send data/VCOM for panel. + /// + /// - In B/W mode, this command writes “OLD” data to SRAM. + /// - In B/W/Red mode, this command writes “B/W” data to SRAM. + /// - In Program mode, this command writes “OTP” data to SRAM for programming. + DATA_START_TRANSMISSION_1 = 0x10, + /// Stopping data transmission + DATA_STOP = 0x11, + /// While user sent this command, driver will refresh display (data/VCOM) according to SRAM data and LUT. + /// + /// After Display Refresh command, BUSY_N signal will become “0” and the refreshing of panel starts. + DISPLAY_REFRESH = 0x12, + /// This command starts transmitting data and write them into SRAM. To complete data transmission, command DSP (Data + /// transmission Stop) must be issued. Then the chip will start to send data/VCOM for panel. + /// - In B/W mode, this command writes “NEW” data to SRAM. + /// - In B/W/Red mode, this command writes “RED” data to SRAM. + DATA_START_TRANSMISSION_2 = 0x13, - /// This command stores VCOM Look-Up Table with 7 groups of data. Each group contains information for one state and is stored - /// with 6 bytes, while the sixth byte indicates how many times that phase will repeat. - /// - /// from IL0373 - LUT_FOR_VCOM = 0x20, - /// This command stores White-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is - /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. - /// - /// from IL0373 - LUT_WHITE_TO_WHITE = 0x21, - /// This command stores Black-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is - /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. - /// - /// from IL0373 - LUT_BLACK_TO_WHITE = 0x22, - /// This command stores White-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is - /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. - /// - /// from IL0373 - LUT_WHITE_TO_BLACK = 0x23, - /// This command stores Black-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is - /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. - /// - /// from IL0373 - LUT_BLACK_TO_BLACK = 0x24, - /// The command controls the PLL clock frequency. - PLL_CONTROL = 0x30, - /// This command reads the temperature sensed by the temperature sensor. - /// - /// Doesn't work! Waveshare doesn't connect the read pin - TEMPERATURE_SENSOR_COMMAND = 0x40, - /// Selects the Internal or External temperature sensor and offset - TEMPERATURE_SENSOR_SELECTION = 0x41, - /// Write External Temperature Sensor - TEMPERATURE_SENSOR_WRITE = 0x42, - /// Read External Temperature Sensor - /// - /// Doesn't work! Waveshare doesn't connect the read pin - TEMPERATURE_SENSOR_READ = 0x43, - /// This command indicates the interval of Vcom and data output. When setting the vertical back porch, the total blanking will be kept (20 Hsync) - VCOM_AND_DATA_INTERVAL_SETTING = 0x50, - /// This command indicates the input power condition. Host can read this flag to learn the battery condition. - LOW_POWER_DETECTION = 0x51, - /// This command defines non-overlap period of Gate and Source. - TCON_SETTING = 0x60, - /// This command defines alternative resolution and this setting is of higher priority than the RES\[1:0\] in R00H (PSR). - RESOLUTION_SETTING = 0x61, - /// This command defines the Fist Active Gate and First Active Source of active channels. - GSST_SETTING = 0x65, - /// The LUT_REV / Chip Revision is read from OTP address = 0x001. - /// - /// Doesn't work! Waveshare doesn't connect the read pin - REVISION = 0x70, - /// Read Flags. This command reads the IC status - /// PTL, I2C_ERR, I2C_BUSY, DATA, PON, POF, BUSY - /// - /// Doesn't work! Waveshare doesn't connect the read pin - GET_STATUS = 0x71, - /// Automatically measure VCOM. This command reads the IC status - AUTO_MEASUREMENT_VCOM = 0x80, - /// This command gets the VCOM value - /// - /// Doesn't work! Waveshare doesn't connect the read pin - READ_VCOM_VALUE = 0x81, - /// Set VCM_DC - VCM_DC_SETTING = 0x82, - /// This command sets partial window - PARTIAL_WINDOW = 0x90, - /// This command makes the display enter partial mode - PARTIAL_IN = 0x91, - /// This command makes the display exit partial mode and enter normal mode - PARTIAL_OUT = 0x92, - /// After this command is issued, the chip would enter the program mode. - /// - /// After the programming procedure completed, a hardware reset is necessary for leaving program mode. - /// - /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. - PROGRAM_MODE = 0xA0, - /// After this command is transmitted, the programming state machine would be activated. - /// - /// The BUSY flag would fall to 0 until the programming is completed. - ACTIVE_PROGRAMMING = 0xA1, - /// The command is used for reading the content of OTP for checking the data of programming. - /// - /// The value of (n) is depending on the amount of programmed data, tha max address = 0xFFF. - READ_OTP = 0xA2, - /// This command is set for saving power during fresh period. If the output voltage of VCOM / Source is from negative to positive or - /// from positive to negative, the power saving mechanism will be activated. The active period width is defined by the following two - /// parameters. - POWER_SAVING = 0xE3, + /// This command stores VCOM Look-Up Table with 7 groups of data. Each group contains information for one state and is stored + /// with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LUT_FOR_VCOM = 0x20, + /// This command stores White-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LUT_WHITE_TO_WHITE = 0x21, + /// This command stores Black-to-White Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LUT_BLACK_TO_WHITE = 0x22, + /// This command stores White-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LUT_WHITE_TO_BLACK = 0x23, + /// This command stores Black-to-Black Look-Up Table with 7 groups of data. Each group contains information for one state and is + /// stored with 6 bytes, while the sixth byte indicates how many times that phase will repeat. + /// + /// from IL0373 + LUT_BLACK_TO_BLACK = 0x24, + /// The command controls the PLL clock frequency. + PLL_CONTROL = 0x30, + /// This command reads the temperature sensed by the temperature sensor. + /// + /// Doesn't work! Waveshare doesn't connect the read pin + TEMPERATURE_SENSOR_COMMAND = 0x40, + /// Selects the Internal or External temperature sensor and offset + TEMPERATURE_SENSOR_SELECTION = 0x41, + /// Write External Temperature Sensor + TEMPERATURE_SENSOR_WRITE = 0x42, + /// Read External Temperature Sensor + /// + /// Doesn't work! Waveshare doesn't connect the read pin + TEMPERATURE_SENSOR_READ = 0x43, + /// This command indicates the interval of Vcom and data output. When setting the vertical back porch, the total blanking will be kept (20 Hsync) + VCOM_AND_DATA_INTERVAL_SETTING = 0x50, + /// This command indicates the input power condition. Host can read this flag to learn the battery condition. + LOW_POWER_DETECTION = 0x51, + /// This command defines non-overlap period of Gate and Source. + TCON_SETTING = 0x60, + /// This command defines alternative resolution and this setting is of higher priority than the RES\[1:0\] in R00H (PSR). + RESOLUTION_SETTING = 0x61, + /// This command defines the Fist Active Gate and First Active Source of active channels. + GSST_SETTING = 0x65, + /// The LUT_REV / Chip Revision is read from OTP address = 0x001. + /// + /// Doesn't work! Waveshare doesn't connect the read pin + REVISION = 0x70, + /// Read Flags. This command reads the IC status + /// PTL, I2C_ERR, I2C_BUSY, DATA, PON, POF, BUSY + /// + /// Doesn't work! Waveshare doesn't connect the read pin + GET_STATUS = 0x71, + /// Automatically measure VCOM. This command reads the IC status + AUTO_MEASUREMENT_VCOM = 0x80, + /// This command gets the VCOM value + /// + /// Doesn't work! Waveshare doesn't connect the read pin + READ_VCOM_VALUE = 0x81, + /// Set VCM_DC + VCM_DC_SETTING = 0x82, + /// This command sets partial window + PARTIAL_WINDOW = 0x90, + /// This command makes the display enter partial mode + PARTIAL_IN = 0x91, + /// This command makes the display exit partial mode and enter normal mode + PARTIAL_OUT = 0x92, + /// After this command is issued, the chip would enter the program mode. + /// + /// After the programming procedure completed, a hardware reset is necessary for leaving program mode. + /// + /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. + PROGRAM_MODE = 0xA0, + /// After this command is transmitted, the programming state machine would be activated. + /// + /// The BUSY flag would fall to 0 until the programming is completed. + ACTIVE_PROGRAMMING = 0xA1, + /// The command is used for reading the content of OTP for checking the data of programming. + /// + /// The value of (n) is depending on the amount of programmed data, tha max address = 0xFFF. + READ_OTP = 0xA2, + /// This command is set for saving power during fresh period. If the output voltage of VCOM / Source is from negative to positive or + /// from positive to negative, the power saving mechanism will be activated. The active period width is defined by the following two + /// parameters. + POWER_SAVING = 0xE3, } - - impl interface::Command for Command { - /// Returns the address of the command - fn address(self) -> u8 { - self as u8 - } + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } } - #[cfg(test)] mod tests { use super::*; - use interface::Command as CommandTrait; + use interface::Command as CommandTrait; #[test] fn command_addr() { - assert_eq!(Command::POWER_SAVING.address(), 0xE3); + assert_eq!(Command::POWER_SAVING.address(), 0xE3); - assert_eq!(Command::PANEL_SETTING.address(), 0x00); + assert_eq!(Command::PANEL_SETTING.address(), 0x00); - assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12); + assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12); } -} \ No newline at end of file +} diff --git a/src/epd4in2/constants.rs b/src/epd4in2/constants.rs index cc866cf..47ec396 100644 --- a/src/epd4in2/constants.rs +++ b/src/epd4in2/constants.rs @@ -108,4 +108,4 @@ pub(crate) const LUT_WB_QUICK: [u8; 42] =[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; \ No newline at end of file +]; diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index e3f5b78..24ee3a0 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -51,7 +51,7 @@ use hal::{ digital::*, }; -use interface::{connection_interface::ConnectionInterface, WaveshareInterface}; +use interface::{connection_interface::ConnectionInterface, WaveshareInterface, InternalWiAdditions}; //The Lookup Tables for the Display mod constants; @@ -62,80 +62,35 @@ use drawing::color::Color; pub mod command; use self::command::Command; - /// EPD4in2 driver /// -pub struct EPD4in2 - { +pub struct EPD4in2 { /// Connection Interface interface: ConnectionInterface, - /// Width - width: u16, - /// Height - height: u16, /// Background Color color: Color, } -impl WaveshareInterface - for EPD4in2 -where - SPI: Write, + +impl + InternalWiAdditions + for EPD4in2 +where + SPI: Write, CS: OutputPin, BUSY: InputPin, - DataCommand: OutputPin, + DC: OutputPin, RST: OutputPin, Delay: DelayUs + DelayMs, { - fn get_width(&self) -> u16 { - self.width - } - - fn get_height(&self) -> u16 { - self.height - } - - /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC - /// - /// This already initialises the device. That means [init()](init()) isn't needed directly afterwards - /// - /// # Example - /// - /// ```ignore - /// //buffer = some image data; - /// - /// let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay); - /// - /// epd4in2.display_and_transfer_frame(buffer, None); - /// - /// epd4in2.sleep(); - /// ``` - fn new(interface: ConnectionInterface) -> Result { - let width = WIDTH as u16; - let height = HEIGHT as u16; - - - let color = Color::White; - let mut epd = EPD4in2 { - interface, - width, - height, - color, - }; - - epd.init()?; - - Ok(epd) - } - - fn init(&mut self) -> Result<(), SpiError> { + fn init(&mut self) -> Result<(), ERR> { // reset the device - self.reset(); + self.interface.reset(); // set the power settings - self.send_command(Command::POWER_SETTING)?; + self.command(Command::POWER_SETTING)?; self.send_data(0x03)?; //VDS_EN, VDG_EN self.send_data(0x00)?; //VCOM_HV, VGHL_LV[1], VGHL_LV[0] self.send_data(0x2b)?; //VDH @@ -143,17 +98,17 @@ where self.send_data(0xff)?; //VDHR // start the booster - self.send_command(Command::BOOSTER_SOFT_START)?; + self.command(Command::BOOSTER_SOFT_START)?; for _ in 0..3 { self.send_data(0x17)?; //07 0f 17 1f 27 2F 37 2f } // power on - self.send_command(Command::POWER_ON)?; + self.command(Command::POWER_ON)?; self.wait_until_idle(); // set the panel settings - self.send_command(Command::PANEL_SETTING)?; + self.command(Command::PANEL_SETTING)?; // 0x0F Red Mode, LUT from OTP // 0x1F B/W Mode, LUT from OTP // 0x2F Red Mode, LUT set by registers @@ -169,70 +124,95 @@ where // 150Hz and 171Hz wasn't tested yet // TODO: Test these other frequencies // 3A 100HZ 29 150Hz 39 200HZ 31 171HZ DEFAULT: 3c 50Hz - self.send_command(Command::PLL_CONTROL)?; + self.command(Command::PLL_CONTROL)?; self.send_data(0x3A)?; self.set_lut()?; Ok(()) } +} + +impl + WaveshareInterface + for EPD4in2 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC + /// + /// This already initialises the device. That means [init()](init()) isn't needed directly afterwards + /// + /// # Example + /// + /// ```ignore + /// //buffer = some image data; + /// + /// let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay); + /// + /// epd4in2.display_and_transfer_frame(buffer, None); + /// + /// epd4in2.sleep(); + /// ``` + fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: Delay) -> Result { + let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); + let color = Color::White; + + let mut epd = EPD4in2 { + interface, + color, + }; + + epd.init()?; + + Ok(epd) + } - fn sleep(&mut self) -> Result<(), SpiError> { - self.send_command(Command::VCOM_AND_DATA_INTERVAL_SETTING)?; - self.send_data(0x17)?; //border floating - self.send_command(Command::VCM_DC_SETTING)?; // VCOM to 0V - self.send_command(Command::PANEL_SETTING)?; + fn wake_up(&mut self) -> Result<(), ERR> { + self.init() + } + + //TODO: is such a long delay really needed inbetween? + fn sleep(&mut self) -> Result<(), ERR> { + self.interface.command_with_data(Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x17])?; //border floating + self.command(Command::VCM_DC_SETTING)?; // VCOM to 0V + self.command(Command::PANEL_SETTING)?; self.delay_ms(100); - self.send_command(Command::POWER_SETTING)?; //VG&VS to 0V fast + self.command(Command::POWER_SETTING)?; //VG&VS to 0V fast for _ in 0..4 { self.send_data(0x00)?; } self.delay_ms(100); - self.send_command(Command::POWER_OFF)?; + self.command(Command::POWER_OFF)?; self.wait_until_idle(); - self.send_command(Command::DEEP_SLEEP)?; - self.send_data(0xA5)?; - - Ok(()) + self.interface.command_with_data(Command::DEEP_SLEEP, &[0xA5]) } - fn reset(&mut self) { - self.interface.reset() - } - - fn delay_ms(&mut self, delay: u16) { - self.interface.delay_ms(delay) - } - - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError> { + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), ERR> { let color_value = self.color.get_byte_value(); self.send_resolution()?; - self.send_command(Command::VCM_DC_SETTING)?; - self.send_data(0x12)?; + self.interface.command_with_data(Command::VCM_DC_SETTING, &[0x12])?; - self.send_command(Command::VCOM_AND_DATA_INTERVAL_SETTING)?; //TODO: this was a send_command instead of a send_data. check if it's alright and doing what it should do (setting the default values) //self.send_command_u8(0x97)?; //VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 - self.send_data(0x97)?; + self.interface.command_with_data(Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x97])?; - self.send_command(Command::DATA_START_TRANSMISSION_1)?; - for _ in 0..(buffer.len()) { - self.send_data(color_value)?; - } - self.delay_ms(2); - self.send_command(Command::DATA_START_TRANSMISSION_2)?; - //self.send_multiple_data(buffer)?; - - for &elem in buffer.iter() { - self.send_data(elem)?; - } + self.command(Command::DATA_START_TRANSMISSION_1)?; + self.interface.data_x_times(color_value, buffer.len() as u16)?; - Ok(()) + self.delay_ms(2); + + self.interface.command_with_data(Command::DATA_START_TRANSMISSION_2, buffer) } fn update_partial_frame( @@ -242,15 +222,14 @@ where y: u16, width: u16, height: u16, - ) -> Result<(), SpiError> { - + ) -> Result<(), ERR> { if buffer.len() as u16 != width / 8 * height { //TODO: panic!! or sth like that //return Err("Wrong buffersize"); } - self.send_command(Command::PARTIAL_IN)?; - self.send_command(Command::PARTIAL_WINDOW)?; + self.command(Command::PARTIAL_IN)?; + self.command(Command::PARTIAL_WINDOW)?; self.send_data((x >> 8) as u8)?; let tmp = x & 0xf8; self.send_data(tmp as u8)?; // x should be the multiple of 8, the last 3 bit will always be ignored @@ -269,90 +248,93 @@ where //TODO: handle dtm somehow let is_dtm1 = false; if is_dtm1 { - self.send_command(Command::DATA_START_TRANSMISSION_1)? + self.command(Command::DATA_START_TRANSMISSION_1)? } else { - self.send_command(Command::DATA_START_TRANSMISSION_2)? + self.command(Command::DATA_START_TRANSMISSION_2)? } self.send_multiple_data(buffer)?; - self.send_command(Command::PARTIAL_OUT) + self.command(Command::PARTIAL_OUT) } - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError>{ - self.update_frame(buffer)?; - self.display_frame() - } - fn display_frame(&mut self) -> Result<(), SpiError> { - self.send_command(Command::DISPLAY_REFRESH)?; + fn display_frame(&mut self) -> Result<(), ERR> { + self.command(Command::DISPLAY_REFRESH)?; self.wait_until_idle(); Ok(()) } - // TODO: add this abstraction function - // fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; - - fn clear_frame(&mut self) -> Result<(), SpiError> { + fn clear_frame(&mut self) -> Result<(), ERR> { self.send_resolution()?; - let size = self.width / 8 * self.height; + let size = WIDTH / 8 * HEIGHT; let color_value = self.color.get_byte_value(); - self.send_command(Command::DATA_START_TRANSMISSION_1)?; - self.delay_ms(2); - for _ in 0..size { - self.send_data(color_value)?; - } + self.command(Command::DATA_START_TRANSMISSION_1)?; + self.interface.data_x_times(color_value, size)?; self.delay_ms(2); - self.send_command(Command::DATA_START_TRANSMISSION_2)?; - self.delay_ms(2); - for _ in 0..size { - self.send_data(color_value)?; - } - Ok(()) + self.command(Command::DATA_START_TRANSMISSION_2)?; + self.interface.data_x_times(color_value, size) } /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) fn set_background_color(&mut self, color: Color) { self.color = color; } + + fn background_color(&self) -> &Color { + &self.color + } + + fn get_width(&self) -> u16 { + WIDTH + } + + fn get_height(&self) -> u16 { + HEIGHT + } + + + fn delay_ms(&mut self, delay: u16) { + self.interface.delay_ms(delay) + } } -impl EPD4in2 +impl EPD4in2 where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, DC: OutputPin, RST: OutputPin, D: DelayUs + DelayMs, { - fn send_command(&mut self, command: Command) -> Result<(), SpiError> { - self.interface.send_command(command) + fn command(&mut self, command: Command) -> Result<(), ERR> { + self.interface.command(command) } - fn send_data(&mut self, val: u8) -> Result<(), SpiError> { - self.interface.send_data(val) + fn send_data(&mut self, val: u8) -> Result<(), ERR> { + self.interface.data(val) } - fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), SpiError> { - self.interface.send_multiple_data(data) + fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), ERR> { + self.interface.multiple_data(data) } fn wait_until_idle(&mut self) { self.interface.wait_until_idle(true) } - fn send_resolution(&mut self) -> Result<(), SpiError> { + fn send_resolution(&mut self) -> Result<(), ERR> { let w = self.get_width(); let h = self.get_height(); - self.send_command(Command::RESOLUTION_SETTING)?; + self.command(Command::RESOLUTION_SETTING)?; self.send_data((w >> 8) as u8)?; self.send_data(w as u8)?; self.send_data((h >> 8) as u8)?; @@ -361,16 +343,16 @@ where /// Fill the look-up table for the EPD //TODO: make public? - fn set_lut(&mut self) -> Result<(), SpiError> { + fn set_lut(&mut self) -> Result<(), ERR> { self.set_lut_helper(&LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB) } /// Fill the look-up table for a quick display (partial refresh) - /// - /// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick()) + /// + /// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick()) /// //TODO: make public? #[cfg(feature = "epd4in2_fast_update")] - fn set_lut_quick(&mut self) -> Result<(), SpiError> { + fn set_lut_quick(&mut self) -> Result<(), ERR> { self.set_lut_helper( &LUT_VCOM0_QUICK, &LUT_WW_QUICK, @@ -387,25 +369,25 @@ where lut_bw: &[u8], lut_wb: &[u8], lut_bb: &[u8], - ) -> Result<(), SpiError> { + ) -> Result<(), ERR> { // LUT VCOM - self.send_command(Command::LUT_FOR_VCOM)?; + self.command(Command::LUT_FOR_VCOM)?; self.send_multiple_data(lut_vcom)?; // LUT WHITE to WHITE - self.send_command(Command::LUT_WHITE_TO_WHITE)?; + self.command(Command::LUT_WHITE_TO_WHITE)?; self.send_multiple_data(lut_ww)?; // LUT BLACK to WHITE - self.send_command(Command::LUT_BLACK_TO_WHITE)?; + self.command(Command::LUT_BLACK_TO_WHITE)?; self.send_multiple_data(lut_bw)?; // LUT WHITE to BLACK - self.send_command(Command::LUT_WHITE_TO_BLACK)?; + self.command(Command::LUT_WHITE_TO_BLACK)?; self.send_multiple_data(lut_wb)?; // LUT BLACK to BLACK - self.send_command(Command::LUT_BLACK_TO_BLACK)?; + self.command(Command::LUT_BLACK_TO_BLACK)?; self.send_multiple_data(lut_bb)?; Ok(()) diff --git a/src/interface/connection_interface.rs b/src/interface/connection_interface.rs index 361937a..f825807 100644 --- a/src/interface/connection_interface.rs +++ b/src/interface/connection_interface.rs @@ -1,16 +1,13 @@ use hal::{ - blocking::{ - spi::Write, - delay::* - }, - digital::* + blocking::{delay::*, spi::Write}, + digital::*, }; use interface::Command; /// The Connection Interface of all (?) Waveshare EPD-Devices /// -pub struct ConnectionInterface { +pub(crate) struct ConnectionInterface { /// SPI spi: SPI, /// CS for SPI @@ -25,58 +22,71 @@ pub struct ConnectionInterface { delay: D, } - -impl ConnectionInterface -where - SPI: Write, +impl + ConnectionInterface +where + SPI: Write, CS: OutputPin, BUSY: InputPin, - DataCommand: OutputPin, + DC: OutputPin, RST: OutputPin, Delay: DelayUs + DelayMs, { - pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DataCommand, rst: RST, delay: Delay) -> Self { - ConnectionInterface {spi, cs, busy, dc, rst, delay } + pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: Delay) -> Self { + ConnectionInterface { + spi, + cs, + busy, + dc, + rst, + delay, + } } - - /// Basic function for sending [Commands](Command). + + /// Basic function for sending [Commands](Command). + /// + /// Enables direct interaction with the device with the help of [data()](ConnectionInterface::data()) /// - /// Enables direct interaction with the device with the help of [send_data()](ConnectionInterface::send_data()) - /// Should rarely be needed! - /// //TODO: make public? - pub(crate) fn send_command(&mut self, command: T) -> Result<(), ErrorSpeziale> { + /// //TODO: make public? + pub(crate) fn command(&mut self, command: T) -> Result<(), ERR> { // low for commands - self.dc.set_low(); + self.dc.set_low(); // Transfer the command over spi - self.with_cs(|epd| { - epd.spi.write(&[command.address()]) - }) + self.with_cs(|epd| epd.spi.write(&[command.address()])) } /// Basic function for sending a single u8 of data over spi + /// + /// Enables direct interaction with the device with the help of [Ecommand()](ConnectionInterface::command()) /// - /// Enables direct interaction with the device with the help of [Esend_command()](ConnectionInterface::send_command()) - /// - /// Should rarely be needed! - /// //TODO: make public? - pub(crate) fn send_data(&mut self, val: u8) -> Result<(), ErrorSpeziale> { + /// //TODO: make public? + pub(crate) fn data(&mut self, val: u8) -> Result<(), ERR> { // high for data self.dc.set_high(); // Transfer data (u8) over spi - self.with_cs(|epd| { - epd.spi.write(&[val]) - }) + self.with_cs(|epd| epd.spi.write(&[val])) } - /// Basic function for sending a single u8 of data over spi + /// Basic function for sending [Commands](Command) and the data belonging to it. /// - /// Enables direct interaction with the device with the help of [Esend_command()](ConnectionInterface::send_command()) + /// //TODO: make public? + pub(crate) fn command_with_data(&mut self, command: T, data: &[u8]) -> Result<(), ERR> { + self.command(command)?; + self.multiple_data(data) + } + + /// Basic function for sending the same byte of data (one u8) multiple times over spi + /// + /// Enables direct interaction with the device with the help of [command()](ConnectionInterface::command()) /// - /// Should rarely be needed! - /// //TODO: make public? - pub(crate) fn send_data_x_times(&mut self, val: u8, repetitions: u16) -> Result<(), ErrorSpeziale> { + /// //TODO: make public? + pub(crate) fn data_x_times( + &mut self, + val: u8, + repetitions: u16, + ) -> Result<(), ERR> { // high for data self.dc.set_high(); @@ -85,30 +95,27 @@ where for _ in 0..repetitions { epd.spi.write(&[val])?; } - Ok(()) + Ok(()) }) } /// Basic function for sending an array of u8-values of data over spi + /// + /// Enables direct interaction with the device with the help of [command()](EPD4in2::command()) /// - /// Enables direct interaction with the device with the help of [send_command()](EPD4in2::send_command()) - /// - /// Should rarely be needed! - /// //TODO: make public? - pub(crate) fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), ErrorSpeziale> { + /// //TODO: make public? + pub(crate) fn multiple_data(&mut self, data: &[u8]) -> Result<(), ERR> { // high for data self.dc.set_high(); // Transfer data (u8-array) over spi - self.with_cs(|epd| { - epd.spi.write(data) - }) + self.with_cs(|epd| epd.spi.write(data)) } // spi write helper/abstraction function - pub(crate) fn with_cs(&mut self, f: F) -> Result<(), ErrorSpeziale> - where - F: FnOnce(&mut Self) -> Result<(), ErrorSpeziale>, + fn with_cs(&mut self, f: F) -> Result<(), ERR> + where + F: FnOnce(&mut Self) -> Result<(), ERR>, { // activate spi with cs low self.cs.set_low(); @@ -120,19 +127,19 @@ where result } - /// Waits until device isn't busy anymore (busy == HIGH) - /// + /// /// This is normally handled by the more complicated commands themselves, /// but in the case you send data and commands directly you might need to check /// if the device is still busy - /// + /// /// is_busy_low - /// + /// /// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5 /// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?) - /// + /// /// Most likely there was a mistake with the 2in9 busy connection + /// //TODO: use the #cfg feature to make this compile the right way for the certain types pub(crate) fn wait_until_idle(&mut self, is_busy_low: bool) { self.delay_ms(1); //low: busy, high: idle @@ -142,23 +149,22 @@ where } } - /// Abstraction of setting the delay for simpler calls - /// + /// /// maximum delay ~65 seconds (u16:max in ms) pub(crate) fn delay_ms(&mut self, delay: u16) { self.delay.delay_ms(delay); } /// Resets the device. - /// + /// /// Often used to awake the module from deep sleep. See [EPD4in2::sleep()](EPD4in2::sleep()) - /// + /// /// TODO: Takes at least 400ms of delay alone, can it be shortened? pub(crate) fn reset(&mut self) { self.rst.set_low(); - //TODO: why 200ms? (besides being in the waveshare code) + //TODO: why 200ms? (besides being in the arduino version) self.delay_ms(200); self.rst.set_high(); @@ -166,5 +172,4 @@ where //TODO: same as 3 lines above self.delay_ms(200); } - -} \ No newline at end of file +} diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 31f9ff7..a3158a5 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -1,18 +1,13 @@ +use core::marker::Sized; use hal::{ - blocking::{ - spi::Write, - delay::* - }, - digital::* + blocking::{delay::*, spi::Write}, + digital::*, }; -use core::marker::Sized; use drawing::color::Color; /// Interface for the physical connection between display and the controlling device -pub mod connection_interface; -use self::connection_interface::ConnectionInterface; - +pub(crate) mod connection_interface; /// All commands need to have this trait which gives the address of the command /// which needs to be send via SPI with activated CommandsPin (Data/Command Pin in CommandMode) @@ -20,96 +15,128 @@ pub(crate) trait Command { fn address(self) -> u8; } - - - //TODO: add LUT trait with set_fast_lut and set_manual_lut and set_normal_lut or sth like that? // for partial updates -trait LUTSupport { - fn set_lut(&mut self) -> Result<(), Error>; - fn set_lut_quick(&mut self) -> Result<(), Error>; - fn set_lut_manual(&mut self, data: &[u8]) -> Result<(), Error>; +trait LUTSupport { + fn set_lut(&mut self) -> Result<(), ERR>; + fn set_lut_quick(&mut self) -> Result<(), ERR>; + fn set_lut_manual(&mut self, data: &[u8]) -> Result<(), ERR>; } - -pub trait WaveshareInterface - where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DataCommand: OutputPin, - RST: OutputPin, - Delay: DelayUs + DelayMs, +pub(crate) trait InternalWiAdditions +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, { - /// Get the width of the display - fn get_width(&self) -> u16; - - /// Get the height of the display - fn get_height(&self) -> u16; - - /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC - /// - /// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards - fn new( - interface: ConnectionInterface - ) -> Result - where Self: Sized; - /// This initialises the EPD and powers it up + /// + /// This function is already called from + /// - [new()](WaveshareInterface::new()) + /// - [`wake_up`] /// - /// This function is already called from [new()](WaveshareInterface::new()) - /// + /// /// This function calls [reset()](WaveshareInterface::reset()), /// so you don't need to call reset your self when trying to wake your device up /// after setting it to sleep. - fn init(&mut self) -> Result<(), Error>; + fn init(&mut self) -> Result<(), ERR>; +} - // void DisplayFrame(const unsigned char* frame_buffer); - /// Transmit a full frame to the SRAM of the DPD - /// - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), Error>; - - //TODO: is dtm always used? - /// Transmit partial data to the SRAM of the EPD, - /// the final parameter dtm chooses between the 2 - /// internal buffers - /// - /// Normally it should be dtm2, so use false - /// - /// BUFFER needs to be of size: w / 8 * l ! - fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), Error>; +pub trait WaveshareInterface +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + - /// Displays the frame data from SRAM - fn display_frame(&mut self) -> Result<(), Error>; + /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC + /// + /// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards + fn new( + spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: Delay, + ) -> Result + where + Self: Sized; // TODO: add this abstraction function - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), Error>; + /// Loads a full image on the EPD and displays it + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), ERR> { + self.update_frame(buffer)?; + self.display_frame() + } + + /// Loads a partial image on the EPD and displays it + fn update_and_display_partial_frame( + &mut self, + buffer: &[u8], + x: u16, + y: u16, + width: u16, + height: u16, + ) -> Result<(), ERR> { + self.update_partial_frame(buffer, x, y, width, height)?; + self.display_frame() + } + + /// Let the device enter deep-sleep mode to save power. + /// + /// The deep sleep mode returns to standby with a hardware reset. + /// But you can also use [reset()](WaveshareInterface::reset()) to awaken. + /// But as you need to power it up once more anyway you can also just directly use [init()](WaveshareInterface::init()) for resetting + /// and initialising which already contains the reset + fn sleep(&mut self) -> Result<(), ERR>; - /// Clears the frame from the buffer - /// - /// Uses the chosen background color - fn clear_frame(&mut self) -> Result<(), Error>; + fn wake_up(&mut self) -> Result<(), ERR>; + /// Sets the backgroundcolor for various commands like [clear_frame()](WaveshareInterface::clear_frame()) fn set_background_color(&mut self, color: Color); + /// Get current background color + fn background_color(&self) -> &Color; - /// Let the device enter deep-sleep mode to save power. - /// - /// The deep sleep mode returns to standby with a hardware reset. - /// But you can also use [reset()](WaveshareInterface::reset()) to awaken. - /// But as you need to power it up once more anyway you can also just directly use [init()](WaveshareInterface::init()) for resetting - /// and initialising which already contains the reset - fn sleep(&mut self) -> Result<(), Error>; + /// Get the width of the display + fn get_width(&self) -> u16; - /// Resets the device. - /// - /// Often used to awake the module from deep sleep. See [sleep()](WaveshareInterface::sleep()) - fn reset(&mut self); + /// Get the height of the display + fn get_height(&self) -> u16; /// Abstraction of setting the delay for simpler calls - /// + /// /// maximum delay ~65 seconds (u16:max in ms) fn delay_ms(&mut self, delay: u16); -} \ No newline at end of file + + // void DisplayFrame(const unsigned char* frame_buffer); + /// Transmit a full frame to the SRAM of the DPD + /// + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), ERR>; + + /// Transmits partial data to the SRAM of the EPD + /// + /// BUFFER needs to be of size: w / 8 * h ! + fn update_partial_frame( + &mut self, + buffer: &[u8], + x: u16, + y: u16, + width: u16, + height: u16, + ) -> Result<(), ERR>; + + /// Displays the frame data from SRAM + fn display_frame(&mut self) -> Result<(), ERR>; + + /// Clears the frame from the buffer with the declared background color + /// The background color can be changed with [`set_background_color`] + /// + /// Uses the chosen background color + fn clear_frame(&mut self) -> Result<(), ERR>; +} diff --git a/src/lib.rs b/src/lib.rs index 814f104..56c9472 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,86 +3,79 @@ //! This driver was built using [`embedded-hal`] traits. //! //! [`embedded-hal`]: https://docs.rs/embedded-hal/~0.1 -//! +//! //! # Requirements -//! +//! //! ### SPI -//! +//! //! - MISO is not connected/available //! - SPI_MODE_0 is used (CPHL = 0, CPOL = 0) //! - 8 bits per word, MSB first //! - Max. Speed tested was 8Mhz but more should be possible -//! +//! //! ### Other.... -//! -//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, +//! +//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, //! where width and length being either the full e-ink size or the partial update window size //! //! # Examples //! //! ```ignore //! use eink-waveshare-rs::epd4in2::EPD4in2; -//! +//! //! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap(); //! //! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; -//! +//! //! // draw something into the buffer -//! +//! //! epd4in2.display_and_transfer_buffer(buffer, None); -//! +//! //! // wait and look at the image -//! +//! //! epd4in2.clear_frame(None); -//! +//! //! epd4in2.sleep(); //! ``` -//! +//! //! #![no_std] //TODO: Make more assertions about buffersizes? - extern crate embedded_hal as hal; -use hal::{ - spi::{Mode, Phase, Polarity}, -}; +use hal::spi::{Mode, Phase, Polarity}; pub mod drawing; mod interface; -pub use interface::{ - WaveshareInterface, - connection_interface::ConnectionInterface}; +pub use interface::{WaveshareInterface}; -#[cfg(feature="epd4in2")] +#[cfg(feature = "epd4in2")] mod epd4in2; -#[cfg(feature="epd4in2")] +#[cfg(feature = "epd4in2")] pub use epd4in2::EPD4in2; -#[cfg(feature="epd1in54")] +#[cfg(feature = "epd1in54")] mod epd1in54; -#[cfg(feature="epd1in54")] +#[cfg(feature = "epd1in54")] pub use epd1in54::EPD1in54; - -#[cfg(feature="epd2in9")] +#[cfg(feature = "epd2in9")] mod epd2in9; ///2in9 eink -#[cfg(feature="epd2in9")] +#[cfg(feature = "epd2in9")] ///2in9 eink pub use epd2in9::EPD2in9; -#[cfg(any(feature="epd1in54", feature="epd2in9"))] +#[cfg(any(feature = "epd1in54", feature = "epd2in9"))] pub mod type_a; - //TODO: test spi mode -/// SPI mode - +/// SPI mode - /// For more infos see [Requirements: SPI](index.html#spi) pub const SPI_MODE: Mode = Mode { phase: Phase::CaptureOnFirstTransition, polarity: Polarity::IdleLow, -}; \ No newline at end of file +}; diff --git a/src/type_a/command.rs b/src/type_a/command.rs index 4adf5a7..5272c07 100644 --- a/src/type_a/command.rs +++ b/src/type_a/command.rs @@ -2,97 +2,93 @@ use interface; - /// EPD1in54 and EPD2IN9 commands -/// +/// /// Should rarely (never?) be needed directly. -/// -/// For more infos about the addresses and what they are doing look into the pdfs +/// +/// For more infos about the addresses and what they are doing look into the pdfs #[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] pub(crate) enum Command { - /// Driver Output control - /// 3 Databytes: - /// A[7:0] - /// 0.. A[8] - /// 0.. B[2:0] - /// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0 - DRIVER_OUTPUT_CONTROL = 0x01, - /// Booster Soft start control - /// 3 Databytes: - /// 1.. A[6:0] - /// 1.. B[6:0] - /// 1.. C[6:0] - /// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D - BOOSTER_SOFT_START_CONTROL = 0x0C, + /// Driver Output control + /// 3 Databytes: + /// A[7:0] + /// 0.. A[8] + /// 0.. B[2:0] + /// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0 + DRIVER_OUTPUT_CONTROL = 0x01, + /// Booster Soft start control + /// 3 Databytes: + /// 1.. A[6:0] + /// 1.. B[6:0] + /// 1.. C[6:0] + /// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D + BOOSTER_SOFT_START_CONTROL = 0x0C, GATE_SCAN_START_POSITION = 0x0F, - //TODO: useful? - // GATE_SCAN_START_POSITION = 0x0F, - /// Deep Sleep Mode Control - /// 1 Databyte: - /// 0.. A[0] - /// Values: - /// A[0] = 0: Normal Mode (POR) - /// A[0] = 1: Enter Deep Sleep Mode - DEEP_SLEEP_MODE = 0x10, - // /// Data Entry mode setting - DATA_ENTRY_MODE_SETTING = 0x11, + //TODO: useful? + // GATE_SCAN_START_POSITION = 0x0F, + /// Deep Sleep Mode Control + /// 1 Databyte: + /// 0.. A[0] + /// Values: + /// A[0] = 0: Normal Mode (POR) + /// A[0] = 1: Enter Deep Sleep Mode + DEEP_SLEEP_MODE = 0x10, + // /// Data Entry mode setting + DATA_ENTRY_MODE_SETTING = 0x11, - SW_RESET = 0x12, + SW_RESET = 0x12, - TEMPERATURE_SENSOR_CONTROL = 0x1A, + TEMPERATURE_SENSOR_CONTROL = 0x1A, - MASTER_ACTIVATION = 0x20, + MASTER_ACTIVATION = 0x20, - DISPLAY_UPDATE_CONTROL_1 = 0x21, + DISPLAY_UPDATE_CONTROL_1 = 0x21, - DISPLAY_UPDATE_CONTROL_2 = 0x22, + DISPLAY_UPDATE_CONTROL_2 = 0x22, - WRITE_RAM = 0x24, + WRITE_RAM = 0x24, - WRITE_VCOM_REGISTER = 0x2C, + WRITE_VCOM_REGISTER = 0x2C, - WRITE_LUT_REGISTER = 0x32, + WRITE_LUT_REGISTER = 0x32, - SET_DUMMY_LINE_PERIOD = 0x3A, + SET_DUMMY_LINE_PERIOD = 0x3A, - SET_GATE_LINE_WIDTH = 0x3B, + SET_GATE_LINE_WIDTH = 0x3B, - BORDER_WAVEFORM_CONTROL = 0x3C, + BORDER_WAVEFORM_CONTROL = 0x3C, - SET_RAM_X_ADDRESS_START_END_POSITION = 0x44, + SET_RAM_X_ADDRESS_START_END_POSITION = 0x44, - SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45, + SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45, - SET_RAM_X_ADDRESS_COUNTER = 0x4E, + SET_RAM_X_ADDRESS_COUNTER = 0x4E, - SET_RAM_Y_ADDRESS_COUNTER = 0x4F, + SET_RAM_Y_ADDRESS_COUNTER = 0x4F, - TERMINATE_COMMANDS_AND_FRAME_WRITE = 0xFF + NOP = 0xFF, } - - impl interface::Command for Command { - /// Returns the address of the command - fn address(self) -> u8 { - self as u8 - } + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } } - #[cfg(test)] mod tests { use super::Command; - use interface::Command as CommandTrait; + use interface::Command as CommandTrait; #[test] fn command_addr() { - assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01); + assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01); - assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.address(), 0x4E); + assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.address(), 0x4E); - assert_eq!(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE.address(), 0xFF); + assert_eq!(Command::NOP.address(), 0xFF); } -} \ No newline at end of file +}