use hcl::platform::PeripheralRef; use hcl::platform::Location; use hcl::platform::spi; use hcl::platform::gpio; use systick::delay_ms; 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 } // ST7735 commands enum Command { NOP, SWRESET, RDDID, RDDST, SLPIN, SLPOUT, PTLON, NORON, INVOFF, INVON, DISPOFF, DISPON, CASET([u8; 4]), RASET([u8; 4]), RAMWR, RAMRD, PTLAR, COLMOD(u8), MADCTL(u8), FRMCTR1([u8; 3]), FRMCTR2([u8; 3]), FRMCTR3([u8; 6]), INVCTR(u8), DISSET5([u8; 2]), PWCTR1([u8; 2]), PWCTR2(u8), PWCTR3([u8; 2]), PWCTR4([u8; 2]), PWCTR5([u8; 2]), PWCTR6([u8; 2]), VMCTR1([u8; 2]), RDID1, RDID2, RDID3, RDID4, GMCTRP1([u8; 16]), GMCTRN1([u8; 16]), } fn command_to_byte(cmd : &Command) -> u8 { match cmd { Command::NOP => 0x00, Command::SWRESET => 0x01, Command::RDDID => 0x04, Command::RDDST => 0x09, Command::SLPIN => 0x10, Command::SLPOUT => 0x11, Command::PTLON => 0x12, Command::NORON => 0x13, Command::INVOFF => 0x20, Command::INVON => 0x21, Command::DISPOFF => 0x28, Command::DISPON => 0x29, Command::CASET(_) => 0x2A, Command::RASET(_) => 0x2B, Command::RAMWR => 0x2C, Command::RAMRD => 0x2E, Command::PTLAR => 0x30, Command::COLMOD(_) => 0x3A, Command::MADCTL(_) => 0x36, Command::FRMCTR1(_) => 0xB1, Command::FRMCTR2(_) => 0xB2, Command::FRMCTR3(_) => 0xB3, Command::INVCTR(_) => 0xB4, Command::DISSET5(_) => 0xB6, Command::PWCTR1(_) => 0xC0, Command::PWCTR2(_) => 0xC1, Command::PWCTR3(_) => 0xC2, Command::PWCTR4(_) => 0xC3, Command::PWCTR5(_) => 0xC4, Command::PWCTR6(_) => 0xFC, Command::VMCTR1(_) => 0xC5, Command::RDID1 => 0xDA, Command::RDID2 => 0xDB, Command::RDID3 => 0xDC, Command::RDID4 => 0xDD, Command::GMCTRP1(_) => 0xE0, Command::GMCTRN1(_) => 0xE1 } } // Initialization commands for 7735B screens const ST7735_BLUE_INIT : [(Command, u32); 18] = [ (Command::SWRESET, 50), // 1: Software reset, 50 ms delay (Command::SLPOUT, 500), // 2: Out of sleep mode, 500ms delay (Command::COLMOD(0x05), 10), // 3: Set color mode, 16-bit color, 10ms delay (Command::FRMCTR1([0x00, 0x06, 0x03]), 10), // 4: Frame rate control, // fastest refresh, // 6 lines front porch, // 3 lines back porch // 10ms delay (Command::MADCTL(0x08), 0), // 5: Memory access ctrl (directions) // Row addr/col addr, bottom to top refresh (Command::DISSET5([0x15,0x02]), 0), // 6: Display settings #5, // 1 clk cycle nonoverlap, 2 cycle gate // Fix on VTL (Command::INVCTR(0x00), 0), // 7: Display inversion control, // Line inversion (Command::PWCTR1([0x02, 0x07]), 10), // 8: Power control, // GVDD = 4.7V // 1.0uA // 10 ms delay (Command::PWCTR2(0x05), 0), // 9: Power control, // GH = 14.7V, VGL = -7.35V (Command::PWCTR3([0x01, 0x02]), 0), // 10: Power control, // Opamp current small // Boost frequency (Command::VMCTR1([0x3C, 0x38]), 10), // 11: Power control // VCOMH = 4V // VCOML = -1.1V // 10 ms delay (Command::PWCTR6([0x11, 0x15]), 0), // 12: Power control (Command::GMCTRP1([0x09, 0x16, 0x09, 0x20, // 13: Magical unicorn dust 0x21, 0x1B, 0x13, 0x19, 0x17, 0x15, 0x1E, 0x2B, 0x04, 0x05, 0x02, 0x0E]), 0), (Command::GMCTRN1([0x0B, 0x14, 0x08, 0x1E, // 14: Sparkles and rainbows 0x22, 0x1D, 0x18, 0x1E, 0x1B, 0x1A, 0x24, 0x2B, 0x06, 0x06, 0x02, 0x0F]), 10), // 10ms delay (Command::CASET([0x00, 0x02, 0x00, 0x81]), 0), // 15: Column addr set // XSTART = 2 // XEND = 129 (Command::RASET([0x00, 0x02, 0x00, 0x81]), 0), // 16: Row addr set // XSTART = 1 // XEND = 160 (Command::NORON, 10), // 17: Normal display on, 10ms delay (Command::DISPON, 500) // 18: Main screen turn on, 500ms delay ]; // Init for 7735R, part 1 (red or green tab) const ST7735_RED_INIT1 : [(Command, u32); 15] = [ (Command::SWRESET, 150), // 1: Software reset, 150ms delay (Command::SLPOUT, 500), // 2: Out of sleep mode (Command::FRMCTR1([0x01, 0x2C, 0x2D]), 0), // 3: Frame rate ctrl - normal mode // Rate = fosc/(1x2+40) * (LINE+2C+2D) (Command::FRMCTR2([0x01, 0x2C, 0x2D]), 0), // 4: Frame rate control - idle mode // Rate = fosc/(1x2+40) * (LINE+2C+2D) (Command::FRMCTR3([0x01, 0x2C, 0x2D, // 5: Frame rate ctrl - partial mode 0x01, 0x2C, 0x2D]), 0), // Dot inversion mode // Line inversion mode (Command::INVCTR(0x07), 0), // 6: Display inversion ctrl // No inversion (Command::PWCTR1([0xA2, 0x02]), 0), // 7: Power control, -4.6V AUTO mode (Command::PWCTR2(0xC5), 0), // 8: Power control // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD (Command::PWCTR3([0x0A, 0x00]), 0), // 9: Power control: // Opamp current small // Boost frequency (Command::PWCTR4([0x8A, 0x2A]),0), // 10: Power control, 2 args, no delay: // BCLK/2, Opamp current small & Medium low (Command::PWCTR5([0x8A, 0xEE]), 0), // 11: Power control (Command::VMCTR1([0x0E, 0x4D]), 0), // 12: Power control (Command::INVOFF, 0), // 13: Don't invert display (Command::MADCTL(0xC8), 0), // 14: Memory access control (directions), 1 arg: // row addr/col addr, bottom to top refresh (Command::COLMOD(0x05), 0) // 15: set color mode, 1 arg, no delay: // 16-bit color ]; // Init for 7735R, part 2 (green tab only) const ST7735_RED_INIT_GREEN2 : [(Command, u32); 2] = [ (Command::CASET([0x00, 0x02, 0x00, 0x7F+0x02]), 0), // 1: Column addr set // XSTART = 0 // XEND = 127 (Command::RASET([0x00, 0x01, 0x00, 0x9F+0x01]), 0) // 2: Row addr set // XSTART = 0 // XEND = 159 ]; // Init for 7735R, part 2 (red tab only) const ST7735_RED_INIT_RED2 : [(Command, u32); 2] = [ (Command::CASET([0x00, 0x00, 0x00, 0x7F]), 0), // 1: Column addr set, 4 args, no delay: // XSTART = 0 // XEND = 127 (Command::RASET([0x00, 0x00, 0x00, 0x9F]),0) // 2: Row addr set, 4 args, no delay: // XSTART = 0 // XEND = 159 ]; // Init for 7735R, part 2 (green 1.44 tab) const ST7735_RED_INIT_GREEN1442 : [(Command, u32); 2] = [ (Command::CASET([0x00, 0x00, 0x00, 0x7F]), 0), // 1: Column addr set, 4 args, no delay: // XSTART = 0 // XEND = 127 (Command::RASET([0x00, 0x00, 0x00, 0x7F]), 0) // 2: Row addr set, 4 args, no delay: // XSTART = 0 // XEND = 127 ]; // Init for 7735R, part 3 (red or green tab) const ST7735_RED_INIT3: [(Command, u32); 4] = [ (Command::GMCTRP1([0x02, 0x1c, 0x07, 0x12, // 1: Magical unicorn dust 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10]), 0), (Command::GMCTRN1([0x03, 0x1d, 0x07, 0x06, // 2: Sparkles and rainbows 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10]), 0), (Command::NORON, 10), // 3: Normal display, 10 ms delay (Command::DISPON, 100) // 4: Main screen turn on, 100 ms delay ]; 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) }); 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.rx_buffer_not_empty() {} while self.spi.busy() {} self.spi.set_enabled(false); } pub fn write_data_byte(&mut self, data : u8) { self.set_rs(); self.reset_cs(); self.write_byte(data); self.set_cs(); } pub fn write_color_bytes(&mut self, data0 : u8, data1 : u8) { self.set_rs(); self.reset_cs(); self.write_byte(data0); self.write_byte(data1); self.set_cs(); } 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(&ST7735_BLUE_INIT); }, DisplayType::RED_18_GREENTAB => { self.run_commands(&ST7735_RED_INIT1); self.run_commands(&ST7735_RED_INIT_GREEN2); self.run_commands(&ST7735_RED_INIT3); }, DisplayType::RED_18_REDTAB => { self.run_commands(&ST7735_RED_INIT1); self.run_commands(&ST7735_RED_INIT_RED2); self.run_commands(&ST7735_RED_INIT3); }, DisplayType::RED_18_BLACKTAB => { self.run_commands(&ST7735_RED_INIT1); self.run_commands(&ST7735_RED_INIT_RED2); self.run_commands(&ST7735_RED_INIT3); // Change MADCTL color filter for black self.write_command(&Command::MADCTL(0xC0)); }, DisplayType::RED144_GREENTAB => { self.run_commands(&ST7735_RED_INIT1); self.run_commands(&ST7735_RED_INIT_GREEN1442); self.run_commands(&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 fill_rect(&mut self, x : u8, y : u8, w : u8, h : u8, c1 : u8, c2 : u8) { 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_byte(c1); self.write_byte(c2); } } self.set_cs(); } pub fn done(mut self) -> (St7735, PeripheralRef, PeripheralRef) { self.set_cs(); (self.st7735, self.spi, self.gpio) } }