use hcl::platform::PeripheralRef; use hcl::platform::Location; use hcl::platform::spi; use hcl::platform::gpio; use systick::delay_ms; mod commands; mod init; pub mod gfx; pub mod bitmaps; use st7735::commands::*; const DEFAULT_WIDTH : u8 = 128; // for 1.44" display const DEFAULT_HEIGHT_144 : u8 = 128; // for 1.8" display const DEFAULT_HEIGHT_18 : u8 = 160; pub enum DisplayType { BLUE, RED_18_GREENTAB, RED_18_REDTAB, RED_18_BLACKTAB, RED144_GREENTAB } pub enum DisplayOrientation { Portrait, InvertedPortrait, Landscape, InvertedLandscape } const MADCTL_MY : u8 = 0x80; // Mirror Y const MADCTL_MX : u8 = 0x40; // Mirrror x const MADCTL_MV : u8 = 0x20; // Swap XY const MADCTL_ML : u8 = 0x10; // Scan address order const MADCTL_RGB : u8 = 0x00; const MADCTL_BGR : u8 = 0x08; const MADCTL_MH : u8 = 0x04; // Horizontal scan oder pub const COLOR_BLACK : u16 = 0x0000; pub const COLOR_BLUE : u16 = 0x001F; pub const COLOR_RED : u16 = 0xF800; pub const COLOR_GREEN : u16 = 0x07E0; pub const COLOR_CYAN : u16 = 0x07FF; pub const COLOR_MAGENTA : u16 = 0xF81F; pub const COLOR_YELLOW : u16 = 0xFFE0; pub const COLOR_WHITE : u16 = 0xFFFF; pub struct St7735 { display_type : DisplayType, column_start : u8, row_start : u8, width : u8, height : u8, cs_pin : u32, rst_pin : u32, rs_pin : u32, led_pin : u32 } pub struct St7735IO where SPIAddr: Location, GPIOAddr: Location { st7735 : St7735, spi : PeripheralRef, gpio : PeripheralRef, } impl St7735 { pub fn new(display_type : DisplayType, cs_pin : u32, rst_pin : u32, rs_pin : u32, led_pin : u32) -> St7735 { match display_type { DisplayType::BLUE => St7735 { display_type : display_type, cs_pin : cs_pin, rst_pin : rst_pin, rs_pin : rs_pin, led_pin: led_pin, column_start : 0, row_start: 0, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT_18, }, DisplayType::RED_18_GREENTAB => St7735 { display_type : display_type, cs_pin : cs_pin, rst_pin : rst_pin, rs_pin : rs_pin, led_pin: led_pin, column_start : 2, row_start: 1, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT_18, }, DisplayType::RED_18_REDTAB => St7735 { display_type : display_type, cs_pin : cs_pin, rst_pin : rst_pin, rs_pin : rs_pin, led_pin: led_pin, column_start : 0, row_start: 0, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT_18, }, DisplayType::RED_18_BLACKTAB => St7735 { display_type : display_type, cs_pin : cs_pin, rst_pin : rst_pin, rs_pin : rs_pin, led_pin: led_pin, column_start : 0, row_start: 0, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT_18, }, DisplayType::RED144_GREENTAB => St7735 { display_type : display_type, cs_pin : cs_pin, rst_pin : rst_pin, rs_pin : rs_pin, led_pin: led_pin, column_start : 2, row_start: 3, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT_144, }, } } pub fn start(self, spi : PeripheralRef, gpio : PeripheralRef) -> St7735IO where SPIAddr: Location, GPIOAddr: Location { St7735IO::start(self, spi, gpio) } } impl St7735IO where SPIAddr: Location, GPIOAddr: Location { pub fn start(st7735 : St7735, mut spi : PeripheralRef, gpio : PeripheralRef) -> St7735IO where SPIAddr: Location, GPIOAddr: Location { spi.configure(|s| { s.set_master_mode(true) .set_software_slave_select(true) .set_clock_divider(8) // required for master mode, even if ss is done manually .set_slave_select_output_enabled(true) //.set_enabled(true) }); let mut io = St7735IO { st7735: st7735, spi : spi, gpio : gpio }; io.set_cs(); io } fn set_rs(&mut self) { self.gpio.set_bit(self.st7735.rs_pin); } fn reset_rs(&mut self) { self.gpio.reset_bit(self.st7735.rs_pin); } fn set_rst(&mut self) { self.gpio.set_bit(self.st7735.rst_pin); } fn reset_rst(&mut self) { self.gpio.reset_bit(self.st7735.rst_pin); } fn set_cs(&mut self) { self.gpio.set_bit(self.st7735.cs_pin); } fn reset_cs(&mut self) { self.gpio.reset_bit(self.st7735.cs_pin); } fn enable_led(&mut self) { self.gpio.set_bit(self.st7735.led_pin); } fn disable_led(&mut self) { self.gpio.reset_bit(self.st7735.led_pin); } fn reset(&mut self) { self.reset_cs(); self.set_rst(); delay_ms(500); self.reset_rst(); delay_ms(500); self.set_rst(); delay_ms(500); } fn write_byte(&mut self, byte : u8) { self.spi.set_enabled(true); self.spi.set_data(byte as u32); while !self.spi.tx_buffer_empty() || self.spi.busy() { unsafe { asm!("nop" :::: "volatile"); } }; self.spi.set_enabled(false); } fn write_data_byte(&mut self, data : u8) { self.set_rs(); self.reset_cs(); self.write_byte(data); self.set_cs(); } fn write_color_bytes(&mut self, color: u16) { let data0 = (color >> 8) as u8; let data1 = (color & 0xFF) as u8; self.write_byte(data0); self.write_byte(data1); } fn write_command_byte(&mut self, cmd : u8) { self.reset_rs(); self.reset_cs(); self.write_byte(cmd); self.set_cs(); } pub fn write_command(&mut self, cmd : &Command) { let cmd_byte = command_to_byte(cmd); self.write_command_byte(cmd_byte); match cmd { Command::COLMOD(arg) | Command::MADCTL(arg) | Command::INVCTR(arg) | Command::PWCTR2(arg) => self.write_data_byte(*arg), Command::DISSET5(args) | Command::PWCTR3(args) | Command::PWCTR4(args) | Command::PWCTR5(args) | Command::PWCTR6(args) | Command::VMCTR1(args) | Command::PWCTR1(args) => { for &arg in args.iter() { self.write_data_byte(arg); } }, Command::CASET(args) | Command::RASET(args) => { for &arg in args.iter() { self.write_data_byte(arg); } }, Command::FRMCTR1(args) | Command::FRMCTR2(args) => { for &arg in args.iter() { self.write_data_byte(arg); } }, Command::FRMCTR3(args) => { for &arg in args.iter() { self.write_data_byte(arg); } }, Command::GMCTRP1(args) | Command::GMCTRN1(args) => { for &arg in args.iter() { self.write_data_byte(arg); } }, _ => () } } fn run_commands(&mut self, cmds: &[(Command, u32)]) { for &(ref cmd, ref delay) in cmds.iter() { self.write_command(cmd); delay_ms(*delay); } } pub fn init(&mut self) { self.enable_led(); self.reset(); match self.st7735.display_type { DisplayType::BLUE => { self.run_commands(&init::ST7735_BLUE_INIT); }, DisplayType::RED_18_GREENTAB => { self.run_commands(&init::ST7735_RED_INIT1); self.run_commands(&init::ST7735_RED_INIT_GREEN2); self.run_commands(&init::ST7735_RED_INIT3); }, DisplayType::RED_18_REDTAB => { self.run_commands(&init::ST7735_RED_INIT1); self.run_commands(&init::ST7735_RED_INIT_RED2); self.run_commands(&init::ST7735_RED_INIT3); }, DisplayType::RED_18_BLACKTAB => { self.run_commands(&init::ST7735_RED_INIT1); self.run_commands(&init::ST7735_RED_INIT_RED2); self.run_commands(&init::ST7735_RED_INIT3); // Change MADCTL color filter for black self.write_command(&Command::MADCTL(0xC0)); }, DisplayType::RED144_GREENTAB => { self.run_commands(&init::ST7735_RED_INIT1); self.run_commands(&init::ST7735_RED_INIT_GREEN1442); self.run_commands(&init::ST7735_RED_INIT3); } } } pub fn set_addr_win(&mut self, x0 : u8, y0 : u8, x1 : u8, y1 : u8) { let xstart = x0 + self.st7735.column_start; let xend = x1 + self.st7735.column_start; self.write_command(&Command::CASET([0x00, xstart, 0x00, xend])); // Column addr set let ystart = y0 + self.st7735.row_start; let yend = y1 + self.st7735.row_start; self.write_command(&Command::RASET([0x00, ystart, 0x00, yend])); // Column addr set self.write_command(&Command::RAMWR); // write to RAM } pub fn set_orientation(&mut self, orientation : DisplayOrientation) { let param = match orientation { DisplayOrientation::Portrait => { self.st7735.width = DEFAULT_WIDTH; self.st7735.height = match self.st7735.display_type { DisplayType::RED144_GREENTAB => DEFAULT_HEIGHT_144, _ => DEFAULT_HEIGHT_18, }; match self.st7735.display_type { DisplayType::RED_18_BLACKTAB => MADCTL_MX | MADCTL_MY | MADCTL_RGB, _ => MADCTL_MX | MADCTL_MY | MADCTL_BGR } }, DisplayOrientation::Landscape => { self.st7735.height = DEFAULT_WIDTH; self.st7735.width = match self.st7735.display_type { DisplayType::RED144_GREENTAB => DEFAULT_HEIGHT_144, _ => DEFAULT_HEIGHT_18, }; match self.st7735.display_type { DisplayType::RED_18_BLACKTAB => MADCTL_MY | MADCTL_MV | MADCTL_RGB, _ => MADCTL_MY | MADCTL_MV | MADCTL_BGR } }, DisplayOrientation::InvertedPortrait => { self.st7735.width = DEFAULT_WIDTH; self.st7735.height = match self.st7735.display_type { DisplayType::RED144_GREENTAB => DEFAULT_HEIGHT_144, _ => DEFAULT_HEIGHT_18, }; match self.st7735.display_type { DisplayType::RED_18_BLACKTAB => MADCTL_RGB, _ => MADCTL_BGR } }, DisplayOrientation::InvertedLandscape => { self.st7735.height = DEFAULT_WIDTH; self.st7735.width = match self.st7735.display_type { DisplayType::RED144_GREENTAB => DEFAULT_HEIGHT_144, _ => DEFAULT_HEIGHT_18, }; match self.st7735.display_type { DisplayType::RED_18_BLACKTAB => MADCTL_MX | MADCTL_MV | MADCTL_RGB, _ => MADCTL_MX | MADCTL_MV | MADCTL_BGR } }, }; self.write_command(&Command::MADCTL(param)); } pub fn done(mut self) -> (St7735, PeripheralRef, PeripheralRef) { self.set_cs(); //self.spi.set_enabled(false); (self.st7735, self.spi, self.gpio) } } impl gfx::PrimitveGFX for St7735IO where SPIAddr: Location, GPIOAddr: Location { fn draw_pixel(&mut self, x: u8, y: u8, color: u16) { if x >= self.st7735.width || y >= self.st7735.height { return; } self.set_addr_win(x, y, x+1, y+1); self.set_rs(); self.reset_cs(); self.write_color_bytes(color); self.set_cs(); } fn fill_rect(&mut self, x: u8, y: u8, w: u8, h: u8, color: u16) { if x >= self.st7735.width || y >= self.st7735.height { return; } let w = if (x + w - 1) >= self.st7735.width { self.st7735.width - x } else { w }; let h = if (y + h - 1) >= self.st7735.height { self.st7735.height - y } else { h }; self.set_addr_win(x, y, x + w - 1, y + h - 1); self.set_rs(); self.reset_cs(); for i in 0..h { for j in 0..w { self.write_color_bytes(color); } } self.set_cs(); } fn draw_fast_vline(&mut self, x: u8, y: u8, h: u8, color: u16) { self.fill_rect(x, y, 1, h, color); } fn draw_fast_hline(&mut self, x: u8, y: u8, w: u8, color: u16) { self.fill_rect(x, y, w, 1, color); } } impl gfx::GFX for St7735IO where SPIAddr: Location, GPIOAddr: Location {} impl bitmaps::BitmapGFX for St7735IO where SPIAddr: Location, GPIOAddr: Location { fn draw_color_bitmap(&mut self, x : u8, y: u8, bitmap : &bitmaps::ColorBitmap) { if x >= self.st7735.width || y >= self.st7735.height { return; } if((x + bitmap.width - 1) >= self.st7735.width) { return; } if((y + bitmap.height - 1) >= self.st7735.height) { return; } let x_end = x + bitmap.width - 1; let y_end = y + bitmap.height - 1; self.set_addr_win(x, y, x_end, y_end); self.set_rs(); self.reset_cs(); let mut byte_pos : usize = 0; for i in 0..bitmap.height { for j in 0..bitmap.width { let color = bitmap.pixels[byte_pos]; self.write_color_bytes(color); byte_pos += 1; } } self.set_cs(); } fn draw_mono_bitmap(&mut self, x : u8, y: u8, bitmap: &bitmaps::MonoBitmap, color_set : u16, color_unset : u16) { if x >= self.st7735.width || y >= self.st7735.height { return; } if((x + bitmap.width - 1) >= self.st7735.width) { return; } if((y + bitmap.height - 1) >= self.st7735.height) { return; } let x_end = x + bitmap.width - 1; let y_end = y + bitmap.height - 1; self.set_addr_win(x, y, x_end, y_end); self.set_rs(); self.reset_cs(); let mut byte_pos : usize = 0; let mut bit_pos : u8 = 0; let mut byte : u8 = 0; for i in 0..bitmap.width { for j in 0..bitmap.height{ if bit_pos == 0 { byte = bitmap.pixels[byte_pos]; byte_pos += 1; } if (byte & (1 << bit_pos)) != 0{ self.write_color_bytes(color_set); } else { self.write_color_bytes(color_unset); } bit_pos = (bit_pos + 1) % 8; } } self.set_cs(); } }