reflow-firmware2.0/src/st7735/mod.rs

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();
}
}