597 lines
17 KiB
Rust
597 lines
17 KiB
Rust
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<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)
|
|
//.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<spi::SPI, SPIAddr>, PeripheralRef<gpio::GPIO, GPIOAddr>) {
|
|
self.set_cs();
|
|
//self.spi.set_enabled(false);
|
|
(self.st7735, self.spi, self.gpio)
|
|
}
|
|
}
|
|
|
|
|
|
impl<SPIAddr, GPIOAddr> gfx::PrimitveGFX for St7735IO<SPIAddr, GPIOAddr>
|
|
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<SPIAddr, GPIOAddr> gfx::GFX for St7735IO<SPIAddr, GPIOAddr>
|
|
where SPIAddr: Location,
|
|
GPIOAddr: Location {}
|
|
|
|
|
|
impl<SPIAddr, GPIOAddr> bitmaps::BitmapGFX for St7735IO<SPIAddr, GPIOAddr>
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
}
|