reflow-firmware2.0/src/st7735.rs

579 lines
19 KiB
Rust

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<SPIAddr, GPIOAddr>
where SPIAddr: Location,
GPIOAddr: Location {
st7735 : St7735,
spi : PeripheralRef<spi::SPI, SPIAddr>,
gpio : PeripheralRef<gpio::GPIO, GPIOAddr>,
}
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<SPIAddr, GPIOAddr>(self, spi : PeripheralRef<spi::SPI, SPIAddr>, gpio : PeripheralRef<gpio::GPIO, GPIOAddr>)
-> St7735IO<SPIAddr, GPIOAddr>
where SPIAddr: Location,
GPIOAddr: Location {
St7735IO::start(self, spi, gpio)
}
}
impl<SPIAddr, GPIOAddr> St7735IO<SPIAddr, GPIOAddr>
where SPIAddr: Location,
GPIOAddr: Location {
pub fn start(st7735 : St7735, mut spi : PeripheralRef<spi::SPI, SPIAddr>, gpio : PeripheralRef<gpio::GPIO, GPIOAddr>)
-> St7735IO<SPIAddr, GPIOAddr>
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<spi::SPI, SPIAddr>, PeripheralRef<gpio::GPIO, GPIOAddr>) {
self.set_cs();
(self.st7735, self.spi, self.gpio)
}
}