Compare commits
No commits in common. "master" and "waveshare" have entirely different histories.
73
Cargo.lock
generated
73
Cargo.lock
generated
@ -160,7 +160,7 @@ version = "4.5.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.77",
|
||||||
@ -359,7 +359,7 @@ version = "0.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
|
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.77",
|
"syn 2.0.77",
|
||||||
@ -393,7 +393,6 @@ dependencies = [
|
|||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-graphics-core",
|
"embedded-graphics-core",
|
||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
"log",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -586,6 +585,12 @@ dependencies = [
|
|||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -665,6 +670,19 @@ version = "0.4.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maybe-async-cfg"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1e083394889336bc66a4eaf1011ffbfa74893e910f902a9f271fa624c61e1b2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"pulldown-cmark",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maze"
|
name = "maze"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -833,6 +851,17 @@ dependencies = [
|
|||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"memchr",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
@ -884,6 +913,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"shared",
|
"shared",
|
||||||
"strum",
|
"strum",
|
||||||
|
"weact-studio-epd",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -922,6 +952,18 @@ dependencies = [
|
|||||||
"twox-hash",
|
"twox-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sealed"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.4.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.77",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.210"
|
||||||
@ -992,7 +1034,7 @@ version = "0.26.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
@ -1074,6 +1116,15 @@ dependencies = [
|
|||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.13"
|
version = "1.0.13"
|
||||||
@ -1116,6 +1167,20 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weact-studio-epd"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e531b21e70dfc6294be2429e4f616f634c8ca1a328325dceefc4f92c12d6e9b"
|
||||||
|
dependencies = [
|
||||||
|
"display-interface",
|
||||||
|
"embedded-graphics",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"embedded-hal-async",
|
||||||
|
"maybe-async-cfg",
|
||||||
|
"sealed",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
@ -8,13 +8,14 @@ display-interface-spi = "0.5.0"
|
|||||||
embedded-graphics = "0.8.1"
|
embedded-graphics = "0.8.1"
|
||||||
embedded-hal-bus = "0.2.0"
|
embedded-hal-bus = "0.2.0"
|
||||||
esp-backtrace = { version = "0.13.0", features = ["esp32c6", "exception-handler", "panic-handler", "println"] }
|
esp-backtrace = { version = "0.13.0", features = ["esp32c6", "exception-handler", "panic-handler", "println"] }
|
||||||
esp-hal = { version = "0.20.1", features = ["esp32c6", "embedded-hal"] }
|
esp-hal = { version = "0.20", features = ["esp32c6", "embedded-hal"] }
|
||||||
esp-println = { version = "0.10.0", default-features = false, features = ["esp32c6", "log", "auto"] }
|
esp-println = { version = "0.10.0", default-features = false, features = ["esp32c6", "log", "auto"] }
|
||||||
heapless = "0.8.0"
|
heapless = "0.8.0"
|
||||||
log = { version = "0.4.21" }
|
log = { version = "0.4.21" }
|
||||||
nutype = { version = "0.5.0", default-features = false }
|
nutype = { version = "0.5.0", default-features = false }
|
||||||
profont = "0.7.0"
|
profont = "0.7.0"
|
||||||
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
||||||
|
weact-studio-epd = { version = "0.1.2", features = ["blocking"] }
|
||||||
|
|
||||||
shared = { path = "./shared", features = ['trng'] }
|
shared = { path = "./shared", features = ['trng'] }
|
||||||
maze = { path = "./maze" }
|
maze = { path = "./maze" }
|
||||||
|
@ -19,7 +19,6 @@ edition = "2021"
|
|||||||
embedded-graphics-core = { version = "0.4", optional = true }
|
embedded-graphics-core = { version = "0.4", optional = true }
|
||||||
embedded-hal = "1.0.0"
|
embedded-hal = "1.0.0"
|
||||||
bit_field = "0.10.1"
|
bit_field = "0.10.1"
|
||||||
log = { version = "0.4.21" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
embedded-graphics = "0.8"
|
embedded-graphics = "0.8"
|
||||||
|
@ -289,7 +289,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
||||||
let _ = self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ pub(crate) const LUT_1GRAY_GC: [u8; 105] = [
|
|||||||
|
|
||||||
// This LUT updates only the pixels that have changed.
|
// This LUT updates only the pixels that have changed.
|
||||||
pub(crate) const LUT_1GRAY_DU: [u8; 105] = [
|
pub(crate) const LUT_1GRAY_DU: [u8; 105] = [
|
||||||
/*
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1
|
||||||
0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //2
|
0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //2
|
||||||
0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3
|
0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3
|
||||||
@ -26,18 +25,5 @@ pub(crate) const LUT_1GRAY_DU: [u8; 105] = [
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10
|
||||||
0x22, 0x22, 0x22, 0x22, 0x22,*/
|
|
||||||
//
|
|
||||||
//
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1
|
|
||||||
0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2
|
|
||||||
0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //5
|
|
||||||
0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x03, 0x05, 0x05, 0x00, // 6
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //7
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //10
|
|
||||||
0x22, 0x22, 0x22, 0x22, 0x22,
|
0x22, 0x22, 0x22, 0x22, 0x22,
|
||||||
];
|
];
|
||||||
|
@ -29,7 +29,6 @@ pub const HEIGHT: u32 = 480;
|
|||||||
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
|
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
|
||||||
|
|
||||||
const IS_BUSY_LOW: bool = false;
|
const IS_BUSY_LOW: bool = false;
|
||||||
const IS_BUSY_HIGH: bool = true;
|
|
||||||
|
|
||||||
const SINGLE_BYTE_WRITE: bool = true;
|
const SINGLE_BYTE_WRITE: bool = true;
|
||||||
|
|
||||||
@ -61,51 +60,32 @@ where
|
|||||||
DELAY: DelayNs,
|
DELAY: DelayNs,
|
||||||
{
|
{
|
||||||
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
||||||
log::info!("self.interface.reset(delay, 30, 10)");
|
|
||||||
// reset the device
|
// reset the device
|
||||||
self.interface.reset(delay, 30, 10);
|
self.interface.reset(delay, 30, 10);
|
||||||
|
|
||||||
log::info!("self.interface.cmd(spi, Command::SwReset)?");
|
|
||||||
self.interface.cmd(spi, Command::SwReset)?;
|
self.interface.cmd(spi, Command::SwReset)?;
|
||||||
log::info!("delay.delay_us(300000u32)");
|
|
||||||
delay.delay_us(300000u32);
|
delay.delay_us(300000u32);
|
||||||
|
|
||||||
log::info!(".cmd_with_data(spi, Command::AutoWriteRedRamRegularPattern, &[0xF7])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::AutoWriteRedRamRegularPattern, &[0xF7])?;
|
.cmd_with_data(spi, Command::AutoWriteRedRamRegularPattern, &[0xF7])?;
|
||||||
log::info!("1 .wait_until_idle(delay, IS_BUSY_LOW)");
|
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
||||||
if let Err(e) = self.interface.wait_until_idle(delay, IS_BUSY_LOW) {
|
|
||||||
log::error!("{e}");
|
|
||||||
panic!("{e}");
|
|
||||||
};
|
|
||||||
log::info!(".cmd_with_data(spi, Command::AutoWriteBwRamRegularPattern, &[0xF7])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::AutoWriteBwRamRegularPattern, &[0xF7])?;
|
.cmd_with_data(spi, Command::AutoWriteBwRamRegularPattern, &[0xF7])?;
|
||||||
log::info!("2 .wait_until_idle(delay, IS_BUSY_LOW)");
|
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
||||||
if let Err(e) = self.interface.wait_until_idle(delay, IS_BUSY_LOW) {
|
|
||||||
log::error!("{e}");
|
|
||||||
panic!("{e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!(".cmd_with_data(spi, Command::GateSetting, &[0xDF, 0x01, 0x00])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::GateSetting, &[0xDF, 0x01, 0x00])?;
|
.cmd_with_data(spi, Command::GateSetting, &[0xDF, 0x01, 0x00])?;
|
||||||
log::info!(".cmd_with_data(spi, Command::GateVoltage, &[0x00])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::GateVoltage, &[0x00])?;
|
.cmd_with_data(spi, Command::GateVoltage, &[0x00])?;
|
||||||
log::info!(".cmd_with_data(spi, Command::GateVoltageSource, &[0x41, 0xA8, 0x32])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::GateVoltageSource, &[0x41, 0xA8, 0x32])?;
|
.cmd_with_data(spi, Command::GateVoltageSource, &[0x41, 0xA8, 0x32])?;
|
||||||
|
|
||||||
log::info!(".cmd_with_data(spi, Command::DataEntrySequence, &[0x03])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::DataEntrySequence, &[0x03])?;
|
.cmd_with_data(spi, Command::DataEntrySequence, &[0x03])?;
|
||||||
|
|
||||||
log::info!(".cmd_with_data(spi, Command::BorderWaveformControl, &[0x03])?");
|
|
||||||
self.interface
|
self.interface
|
||||||
.cmd_with_data(spi, Command::BorderWaveformControl, &[0x03])?;
|
.cmd_with_data(spi, Command::BorderWaveformControl, &[0x03])?;
|
||||||
|
|
||||||
log::info!(".cmd_with_data( spi, Command::BoosterSoftStartControl, &[0xAE, 0xC7, 0xC3, 0xC0, 0xC0],)?");
|
|
||||||
self.interface.cmd_with_data(
|
self.interface.cmd_with_data(
|
||||||
spi,
|
spi,
|
||||||
Command::BoosterSoftStartControl,
|
Command::BoosterSoftStartControl,
|
||||||
@ -143,72 +123,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl<SPI, BUSY, DC, RST, DELAY> EPD3in7<SPI, BUSY, DC, RST, DELAY>
|
|
||||||
where
|
|
||||||
SPI: SpiDevice,
|
|
||||||
BUSY: InputPin,
|
|
||||||
DC: OutputPin,
|
|
||||||
RST: OutputPin,
|
|
||||||
DELAY: DelayNs,
|
|
||||||
{
|
|
||||||
pub(crate) fn set_ram_area(
|
|
||||||
&mut self,
|
|
||||||
spi: &mut SPI,
|
|
||||||
delay: &mut DELAY,
|
|
||||||
start_x: u32,
|
|
||||||
start_y: u32,
|
|
||||||
end_x: u32,
|
|
||||||
end_y: u32,
|
|
||||||
) -> Result<(), SPI::Error> {
|
|
||||||
self.wait_until_idle(spi, delay)?;
|
|
||||||
assert!(start_x < end_x);
|
|
||||||
assert!(start_y < end_y);
|
|
||||||
|
|
||||||
// x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
|
|
||||||
// aren't relevant
|
|
||||||
self.interface.cmd_with_data(
|
|
||||||
spi,
|
|
||||||
Command::SetRamXAddressStartEndPosition,
|
|
||||||
&[(start_x >> 3) as u8, (end_x >> 3) as u8],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// 2 Databytes: A[7:0] & 0..A[8] for each - start and end
|
|
||||||
self.interface.cmd_with_data(
|
|
||||||
spi,
|
|
||||||
Command::SetRamYAddressStartEndPosition,
|
|
||||||
&[
|
|
||||||
start_y as u8,
|
|
||||||
(start_y >> 8) as u8,
|
|
||||||
end_y as u8,
|
|
||||||
(end_y >> 8) as u8,
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_ram_counter(
|
|
||||||
&mut self,
|
|
||||||
spi: &mut SPI,
|
|
||||||
delay: &mut DELAY,
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
) -> Result<(), SPI::Error> {
|
|
||||||
self.wait_until_idle(spi, delay)?;
|
|
||||||
// x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
|
|
||||||
// aren't relevant
|
|
||||||
self.interface
|
|
||||||
.cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])?;
|
|
||||||
|
|
||||||
// 2 Databytes: A[7:0] & 0..A[8]
|
|
||||||
self.interface.cmd_with_data(
|
|
||||||
spi,
|
|
||||||
Command::SetRamYAddressCounter,
|
|
||||||
&[y as u8, (y >> 8) as u8],
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
|
impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
|
||||||
for EPD3in7<SPI, BUSY, DC, RST, DELAY>
|
for EPD3in7<SPI, BUSY, DC, RST, DELAY>
|
||||||
where
|
where
|
||||||
@ -228,13 +142,11 @@ where
|
|||||||
delay: &mut DELAY,
|
delay: &mut DELAY,
|
||||||
delay_us: Option<u32>,
|
delay_us: Option<u32>,
|
||||||
) -> Result<Self, SPI::Error> {
|
) -> Result<Self, SPI::Error> {
|
||||||
log::info!("DisplayInterface::new(busy, dc, rst, delay_us)");
|
|
||||||
let mut epd = EPD3in7 {
|
let mut epd = EPD3in7 {
|
||||||
interface: DisplayInterface::new(busy, dc, rst, delay_us),
|
interface: DisplayInterface::new(busy, dc, rst, delay_us),
|
||||||
background_color: DEFAULT_BACKGROUND_COLOR,
|
background_color: DEFAULT_BACKGROUND_COLOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
log::info!("init");
|
|
||||||
epd.init(spi, delay)?;
|
epd.init(spi, delay)?;
|
||||||
Ok(epd)
|
Ok(epd)
|
||||||
}
|
}
|
||||||
@ -285,56 +197,25 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
void EPD_3IN7_1Gray_Display_Part(const UBYTE *Image, UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend)
|
|
||||||
{
|
|
||||||
UWORD i, Width;
|
|
||||||
Width = (Xend-Xstart)%8 == 0 ? (Xend-Xstart)/8 : (Xend-Xstart)/8+1;
|
|
||||||
UWORD IMAGE_COUNTER = Width * (Yend-Ystart);
|
|
||||||
|
|
||||||
EPD_3IN7_SendCommand(0x44);
|
|
||||||
EPD_3IN7_SendData(Xstart & 0xff);
|
|
||||||
EPD_3IN7_SendData((Xstart>>8) & 0x03);
|
|
||||||
EPD_3IN7_SendData(Xend & 0xff);
|
|
||||||
EPD_3IN7_SendData((Xend>>8) & 0x03);
|
|
||||||
EPD_3IN7_SendCommand(0x45);
|
|
||||||
EPD_3IN7_SendData(Ystart & 0xff);
|
|
||||||
EPD_3IN7_SendData((Ystart>>8) & 0x03);
|
|
||||||
EPD_3IN7_SendData(Yend & 0xff);
|
|
||||||
EPD_3IN7_SendData((Yend>>8) & 0x03);
|
|
||||||
|
|
||||||
EPD_3IN7_SendCommand(0x24);
|
|
||||||
for (i = 0; i < IMAGE_COUNTER; i++)
|
|
||||||
{
|
|
||||||
EPD_3IN7_SendData(Image[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
EPD_3IN7_Load_LUT(2);
|
|
||||||
EPD_3IN7_SendCommand(0x20);
|
|
||||||
EPD_3IN7_ReadBusy_HIGH();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn update_partial_frame(
|
fn update_partial_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
spi: &mut SPI,
|
spi: &mut SPI,
|
||||||
delay: &mut DELAY,
|
delay: &mut DELAY,
|
||||||
buffer: &[u8],
|
buffer: &[u8],
|
||||||
start_x: u32,
|
x: u32,
|
||||||
start_y: u32,
|
y: u32,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> Result<(), SPI::Error> {
|
) -> Result<(), SPI::Error> {
|
||||||
// broken beyond repart
|
todo!()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
||||||
//self.interface
|
//self.interface
|
||||||
// .cmd_with_data(spi, Command::WRITE_LUT_REGISTER, &LUT_1GRAY_GC)?;
|
// .cmd_with_data(spi, Command::WRITE_LUT_REGISTER, &LUT_1GRAY_GC)?;
|
||||||
self.interface.cmd(spi, Command::DisplayUpdateSequence)?;
|
self.interface.cmd(spi, Command::DisplayUpdateSequence)?;
|
||||||
let _ = self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +260,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
|
||||||
self.interface.wait_until_idle(delay, IS_BUSY_LOW).unwrap();
|
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,7 @@ where
|
|||||||
/// If no delay is given, a default delay of 10ms is used.
|
/// If no delay is given, a default delay of 10ms is used.
|
||||||
pub fn new(busy: BUSY, dc: DC, rst: RST, delay_us: Option<u32>) -> Self {
|
pub fn new(busy: BUSY, dc: DC, rst: RST, delay_us: Option<u32>) -> Self {
|
||||||
// default delay of 10ms
|
// default delay of 10ms
|
||||||
log::info!("delay_us.unwrap_or(10_000)");
|
|
||||||
let delay_us = delay_us.unwrap_or(10_000);
|
let delay_us = delay_us.unwrap_or(10_000);
|
||||||
log::info!("delay_us.unwrap_or(10_000) done");
|
|
||||||
DisplayInterface {
|
DisplayInterface {
|
||||||
_spi: PhantomData,
|
_spi: PhantomData,
|
||||||
_delay: PhantomData,
|
_delay: PhantomData,
|
||||||
@ -136,20 +134,8 @@ where
|
|||||||
/// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?)
|
/// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?)
|
||||||
///
|
///
|
||||||
/// Most likely there was a mistake with the 2in9 busy connection
|
/// Most likely there was a mistake with the 2in9 busy connection
|
||||||
pub(crate) fn wait_until_idle(
|
pub(crate) fn wait_until_idle(&mut self, delay: &mut DELAY, is_busy_low: bool) {
|
||||||
&mut self,
|
|
||||||
delay: &mut DELAY,
|
|
||||||
is_busy_low: bool,
|
|
||||||
) -> Result<(), &'static str> {
|
|
||||||
let mut times = 0;
|
|
||||||
while self.is_busy(is_busy_low) {
|
while self.is_busy(is_busy_low) {
|
||||||
times += 1;
|
|
||||||
if times % 10 == 0 {
|
|
||||||
log::warn!("wait_until_idle {times}");
|
|
||||||
}
|
|
||||||
if times == 1_000 {
|
|
||||||
return Err("Failed to wait 1 000 times despite idle");
|
|
||||||
}
|
|
||||||
// This has been removed and added many time :
|
// This has been removed and added many time :
|
||||||
// - it is faster to not have it
|
// - it is faster to not have it
|
||||||
// - it is complicated to pass the delay everywhere all the time
|
// - it is complicated to pass the delay everywhere all the time
|
||||||
@ -160,7 +146,6 @@ where
|
|||||||
delay.delay_us(self.delay_us);
|
delay.delay_us(self.delay_us);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `wait_until_idle` for device needing a command to probe Busy pin
|
/// Same as `wait_until_idle` for device needing a command to probe Busy pin
|
||||||
|
121
maze/src/lib.rs
121
maze/src/lib.rs
@ -409,7 +409,7 @@ impl<const X: usize, const Y: usize, const SIZE: usize> AsciiBroad<X, Y, SIZE> {
|
|||||||
// const LINE_SIZE: usize = X * Self::CELL_SIZE + 1;
|
// const LINE_SIZE: usize = X * Self::CELL_SIZE + 1;
|
||||||
// const BUFFER_SIZE: usize = Y + Self::CELL_SIZE * Self::LINE_SIZE;
|
// const BUFFER_SIZE: usize = Y + Self::CELL_SIZE * Self::LINE_SIZE;
|
||||||
|
|
||||||
pub fn format(&self, grid: &Grid<X, Y, SIZE>, output: &mut impl Write) {
|
fn format(&self, grid: &Grid<X, Y, SIZE>, output: &mut impl Write) {
|
||||||
write!(output, "#").unwrap();
|
write!(output, "#").unwrap();
|
||||||
(0..X).into_iter().for_each(|_| {
|
(0..X).into_iter().for_each(|_| {
|
||||||
write!(output, "##").unwrap();
|
write!(output, "##").unwrap();
|
||||||
@ -426,16 +426,16 @@ impl<const X: usize, const Y: usize, const SIZE: usize> AsciiBroad<X, Y, SIZE> {
|
|||||||
write!(bottom_line, "#").unwrap();
|
write!(bottom_line, "#").unwrap();
|
||||||
|
|
||||||
for x in 0..X {
|
for x in 0..X {
|
||||||
write!(top_line, ".").unwrap();
|
write!(top_line, " ").unwrap();
|
||||||
let east_boundary = if grid.is_carved((x, y), Cell::EAST) {
|
let east_boundary = if grid.is_carved((x, y), Cell::EAST) {
|
||||||
"."
|
" "
|
||||||
} else {
|
} else {
|
||||||
"#"
|
"#"
|
||||||
};
|
};
|
||||||
write!(top_line, "{east_boundary}").unwrap();
|
write!(top_line, "{east_boundary}").unwrap();
|
||||||
|
|
||||||
let south_boundary = if grid.is_carved((x, y), Cell::SOUTH) {
|
let south_boundary = if grid.is_carved((x, y), Cell::SOUTH) {
|
||||||
"."
|
" "
|
||||||
} else {
|
} else {
|
||||||
"#"
|
"#"
|
||||||
};
|
};
|
||||||
@ -456,12 +456,10 @@ impl<'s> BufWriter<'s> {
|
|||||||
pub fn new(b: &'s mut [MazePart]) -> Self {
|
pub fn new(b: &'s mut [MazePart]) -> Self {
|
||||||
Self(b, 0)
|
Self(b, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, part: MazePart) {
|
pub fn push(&mut self, part: MazePart) {
|
||||||
self.0[self.1] = part;
|
self.0[self.1] = part;
|
||||||
self.1 += 1;
|
self.1 += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy(&mut self, src: &[MazePart]) {
|
pub fn copy(&mut self, src: &[MazePart]) {
|
||||||
src.iter()
|
src.iter()
|
||||||
.take_while(|p| **p != MazePart::Noop)
|
.take_while(|p| **p != MazePart::Noop)
|
||||||
@ -577,8 +575,10 @@ impl<const X: usize, const Y: usize, const SIZE: usize> BinaryMapVisitor<X, Y, S
|
|||||||
let mut bw = BufWriter::new(buffer);
|
let mut bw = BufWriter::new(buffer);
|
||||||
|
|
||||||
bw.push(MazePart::Wall);
|
bw.push(MazePart::Wall);
|
||||||
bw.copy(&[MazePart::Wall; X]);
|
(0..X).into_iter().for_each(|_| {
|
||||||
bw.copy(&[MazePart::Wall; X]);
|
bw.push(MazePart::Wall);
|
||||||
|
bw.push(MazePart::Wall);
|
||||||
|
});
|
||||||
|
|
||||||
let mut top_line = [MazePart::Noop; SIZE];
|
let mut top_line = [MazePart::Noop; SIZE];
|
||||||
let mut bottom_line = [MazePart::Noop; SIZE];
|
let mut bottom_line = [MazePart::Noop; SIZE];
|
||||||
@ -605,33 +605,12 @@ impl<const X: usize, const Y: usize, const SIZE: usize> BinaryMapVisitor<X, Y, S
|
|||||||
} else {
|
} else {
|
||||||
MazePart::Wall
|
MazePart::Wall
|
||||||
});
|
});
|
||||||
bl.push(MazePart::Wall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bw.copy(&top_line);
|
bw.copy(&top_line);
|
||||||
bw.copy(&bottom_line);
|
bw.copy(&bottom_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn binary_to_ascii_broad<const X: usize, const Y: usize>(b: &[MazePart], f: &mut impl Write) {
|
|
||||||
use MazePart::Passage as P;
|
|
||||||
use MazePart::Wall as W;
|
|
||||||
use MazePart::*;
|
|
||||||
|
|
||||||
write!(f, "\n").unwrap();
|
|
||||||
for y in 0..Y {
|
|
||||||
for x in 0..X {
|
|
||||||
let c = match b[y * X + x] {
|
|
||||||
P => '.',
|
|
||||||
W => '#',
|
|
||||||
Noop => '?',
|
|
||||||
Player => 'O',
|
|
||||||
};
|
|
||||||
write!(f, "{c}").unwrap();
|
|
||||||
}
|
|
||||||
write!(f, "\n").unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum MazePart {
|
pub enum MazePart {
|
||||||
@ -699,90 +678,6 @@ mod print_tests {
|
|||||||
let mut map = BinaryMap::<122, 122, 14884>::new();
|
let mut map = BinaryMap::<122, 122, 14884>::new();
|
||||||
BinaryMapVisitor.format(&mut grid, &mut map.0);
|
BinaryMapVisitor.format(&mut grid, &mut map.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn binary_map() {
|
|
||||||
let mut g = Grid::<5, 5, 25>::new();
|
|
||||||
g.cells[0] = Cell::EAST | Cell::SOUTH;
|
|
||||||
g.cells[1] = Cell::EAST | Cell::SOUTH;
|
|
||||||
g.cells[2] = Cell::WEST | Cell::SOUTH;
|
|
||||||
g.cells[3] = Cell::EAST | Cell::SOUTH;
|
|
||||||
g.cells[4] = Cell::WEST | Cell::SOUTH;
|
|
||||||
|
|
||||||
g.cells[5] = Cell::WEST | Cell::SOUTH;
|
|
||||||
g.cells[6] = Cell::WEST | Cell::SOUTH;
|
|
||||||
g.cells[7] = Cell::WEST | Cell::NORTH;
|
|
||||||
g.cells[8] = Cell::WEST | Cell::SOUTH;
|
|
||||||
g.cells[9] = Cell::WEST | Cell::SOUTH;
|
|
||||||
|
|
||||||
g.cells[10] = Cell::WEST | Cell::SOUTH;
|
|
||||||
g.cells[11] = Cell::EAST | Cell::NORTH;
|
|
||||||
g.cells[12] = Cell::EAST | Cell::SOUTH;
|
|
||||||
g.cells[13] = Cell::WEST | Cell::NORTH;
|
|
||||||
g.cells[14] = Cell::WEST | Cell::SOUTH;
|
|
||||||
|
|
||||||
g.cells[15] = Cell::EAST | Cell::SOUTH;
|
|
||||||
g.cells[16] = Cell::WEST | Cell::NORTH;
|
|
||||||
g.cells[17] = Cell::EAST | Cell::NORTH;
|
|
||||||
g.cells[18] = Cell::WEST | Cell::SOUTH;
|
|
||||||
g.cells[19] = Cell::WEST | Cell::SOUTH;
|
|
||||||
|
|
||||||
g.cells[20] = Cell::EAST | Cell::NORTH;
|
|
||||||
g.cells[21] = Cell::EAST | Cell::NORTH;
|
|
||||||
g.cells[22] = Cell::EAST | Cell::NORTH;
|
|
||||||
g.cells[23] = Cell::WEST | Cell::NORTH;
|
|
||||||
g.cells[24] = Cell::WEST | Cell::NORTH;
|
|
||||||
|
|
||||||
let mut map = BinaryMap::<11, 11, 121>::new();
|
|
||||||
BinaryMapVisitor.format(&g, &mut map.0);
|
|
||||||
|
|
||||||
let mut s = heapless::String::<144>::new();
|
|
||||||
AsciiBroad.format(&g, &mut s);
|
|
||||||
println!("{s}");
|
|
||||||
|
|
||||||
use MazePart::Passage as P;
|
|
||||||
use MazePart::Wall as W;
|
|
||||||
use MazePart::*;
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
struct MB([MazePart; 121]);
|
|
||||||
|
|
||||||
impl std::fmt::Debug for MB {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "\n");
|
|
||||||
for y in 0..11 {
|
|
||||||
for x in 0..11 {
|
|
||||||
let c = match self.0[y * 11 + x] {
|
|
||||||
P => '.',
|
|
||||||
W => '#',
|
|
||||||
Noop => '?',
|
|
||||||
Player => 'O',
|
|
||||||
};
|
|
||||||
write!(f, "{c}");
|
|
||||||
}
|
|
||||||
write!(f, "\n");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
MB(map.0),
|
|
||||||
MB([
|
|
||||||
W, W, W, W, W, W, W, W, W, W, W, //
|
|
||||||
W, P, P, P, P, P, W, P, P, P, W, //
|
|
||||||
W, P, W, P, W, P, W, P, W, P, W, //
|
|
||||||
W, P, W, P, W, P, W, P, W, P, W, //
|
|
||||||
W, P, W, P, W, W, W, P, W, P, W, //
|
|
||||||
W, P, W, P, P, P, P, P, W, P, W, //
|
|
||||||
W, P, W, W, W, P, W, W, W, P, W, //
|
|
||||||
W, P, P, P, W, P, P, P, W, P, W, //
|
|
||||||
W, P, W, W, W, W, W, P, W, P, W, //
|
|
||||||
W, P, P, P, P, P, P, P, W, P, W, //
|
|
||||||
W, W, W, W, W, W, W, W, W, W, W,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,179 +1,91 @@
|
|||||||
use epd_waveshare::color::Color;
|
use maze::{BinaryMapVisitor, Direction};
|
||||||
use maze::{binary_to_ascii_broad, BinaryMapVisitor, Direction, MazePart};
|
|
||||||
|
|
||||||
use crate::Button;
|
use crate::Button;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub const MAZE_WIDTH: usize = 25;
|
|
||||||
pub const MAZE_HEIGHT: usize = 25;
|
|
||||||
|
|
||||||
pub struct MazeGame {
|
pub struct MazeGame {
|
||||||
map: maze::BinaryMap<MAZE_WIDTH, MAZE_HEIGHT, 625>,
|
map: maze::BinaryMap<122, 122, 14884>,
|
||||||
player: Point,
|
player: (u16, u16),
|
||||||
old_player: Option<Point>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MazeGame {
|
impl MazeGame {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
map: maze::BinaryMap::new(),
|
map: maze::BinaryMap::new(),
|
||||||
player: Point { x: 0, y: 1 },
|
player: (0, 1),
|
||||||
old_player: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MazeGame {
|
|
||||||
const X_OFFSET: i32 = 4;
|
|
||||||
const Y_OFFSET: i32 = 4;
|
|
||||||
const CELL_SIZE: i32 = 10;
|
|
||||||
|
|
||||||
fn player_pos(&self) -> (u16, u16) {
|
|
||||||
(self.player.x as u16, self.player.y as u16)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_walls(&self, ctx: &mut Context) {
|
|
||||||
let wall_style = PrimitiveStyleBuilder::new()
|
|
||||||
.fill_color(Color::Black)
|
|
||||||
.build();
|
|
||||||
let passage_style = PrimitiveStyleBuilder::new()
|
|
||||||
.fill_color(Color::White)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
for y in 0..MAZE_HEIGHT {
|
|
||||||
for x in 0..MAZE_WIDTH {
|
|
||||||
let style = match self.map.at(x as u16, y as u16) {
|
|
||||||
maze::MazePart::Wall => &wall_style,
|
|
||||||
_ => &passage_style,
|
|
||||||
};
|
|
||||||
Rectangle::new(
|
|
||||||
Point::new(
|
|
||||||
(x as i32 * Self::CELL_SIZE) + Self::X_OFFSET,
|
|
||||||
(y as i32 * Self::CELL_SIZE) + Self::Y_OFFSET,
|
|
||||||
),
|
|
||||||
Size::new(Self::CELL_SIZE as u32, Self::CELL_SIZE as u32),
|
|
||||||
)
|
|
||||||
.draw_styled(style, &mut ctx.epaper.display)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_player(&self, ctx: &mut Context) {
|
|
||||||
let player_style = PrimitiveStyleBuilder::new()
|
|
||||||
.stroke_color(Color::Black)
|
|
||||||
.stroke_width(1)
|
|
||||||
.fill_color(Color::Black)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Rectangle::new(
|
|
||||||
Point::new(
|
|
||||||
(self.player.x * Self::CELL_SIZE) + Self::X_OFFSET + 1,
|
|
||||||
(self.player.y * Self::CELL_SIZE) + Self::Y_OFFSET + 1,
|
|
||||||
),
|
|
||||||
Size::new((Self::CELL_SIZE - 2) as u32, (Self::CELL_SIZE - 2) as u32),
|
|
||||||
)
|
|
||||||
.draw_styled(&player_style, &mut ctx.epaper.display)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_map(&mut self, trng: &mut Trng) {
|
|
||||||
let mut grid = maze::Grid::<12, 12, 144>::new();
|
|
||||||
maze::RecursiveDivision.generate(&mut grid, trng);
|
|
||||||
BinaryMapVisitor.format(&mut grid, &mut self.map.0);
|
|
||||||
for x in 0..MAZE_WIDTH {
|
|
||||||
let part = &mut self.map.0[(MAZE_WIDTH + x) as usize];
|
|
||||||
if *part != MazePart::Wall {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*part = MazePart::Passage;
|
|
||||||
}
|
|
||||||
for x in (0..MAZE_WIDTH).into_iter().rev() {
|
|
||||||
let part = &mut self.map.0[((MAZE_WIDTH * (MAZE_HEIGHT - 2)) + x) as usize];
|
|
||||||
if *part != MazePart::Wall {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*part = MazePart::Passage;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result = heapless::String::<4_000>::new();
|
|
||||||
// AsciiBroad.format(&grid, &mut result);
|
|
||||||
binary_to_ascii_broad::<MAZE_WIDTH, MAZE_HEIGHT>(&self.map.0, &mut result);
|
|
||||||
println!("{result}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App for MazeGame {
|
impl App for MazeGame {
|
||||||
fn start(&mut self, trng: &mut Trng) {
|
fn start(&mut self, trng: &mut Trng) {
|
||||||
self.generate_map(trng);
|
let mut grid = maze::Grid::<60, 60, 3600>::new();
|
||||||
|
maze::RecursiveDivision.generate(&mut grid, trng);
|
||||||
|
BinaryMapVisitor.format(&mut grid, &mut self.map.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, ctx: &mut Context) {
|
fn draw(&self, ctx: &mut Context) {
|
||||||
/*
|
/*
|
||||||
self.draw_walls(ctx);
|
let wall_style = PrimitiveStyleBuilder::new()
|
||||||
self.draw_player(ctx);*/
|
.stroke_color(TriColor::Black)
|
||||||
// FIXME: Partial update
|
.stroke_width(3)
|
||||||
let Some(_old) = &self.old_player else {
|
.fill_color(TriColor::Black)
|
||||||
self.draw_walls(ctx);
|
.build();
|
||||||
self.draw_player(ctx);
|
let player_style = PrimitiveStyleBuilder::new()
|
||||||
return;
|
.stroke_color(TriColor::Red)
|
||||||
};
|
.stroke_width(3)
|
||||||
|
.fill_color(TriColor::Red)
|
||||||
|
.build();
|
||||||
|
|
||||||
ctx.epaper.fast_update();
|
const X_OFFSET: i32 = 2;
|
||||||
ctx.epaper.clear_frame();
|
const Y_OFFSET: i32 = 2;
|
||||||
self.draw_walls(ctx);
|
|
||||||
self.draw_player(ctx);
|
for x in 0..122 {
|
||||||
ctx.epaper.full_update();
|
for y in 0..122 {
|
||||||
// ctx.epaper.partial_update(
|
match self.map.at(x, y) {
|
||||||
// *old,
|
maze::MazePart::Wall => {
|
||||||
// Size {
|
let p = Rectangle::new(
|
||||||
// width: 1,
|
Point::new(x as i32 + X_OFFSET, y as i32 + Y_OFFSET),
|
||||||
// height: 1,
|
Size::new(1, 1),
|
||||||
// },
|
);
|
||||||
// &[Color::White as u8],
|
p.draw_styled(&wall_style, display).unwrap();
|
||||||
// );
|
}
|
||||||
// ctx.epaper.partial_update(
|
_ => {}
|
||||||
// self.player,
|
}
|
||||||
// Size {
|
}
|
||||||
// width: 1,
|
let p = Rectangle::new(
|
||||||
// height: 1,
|
Point::new(
|
||||||
// },
|
self.player.0 as i32 + X_OFFSET,
|
||||||
// &[Color::Black as u8],
|
self.player.1 as i32 + Y_OFFSET,
|
||||||
// );
|
),
|
||||||
|
Size::new(1, 1),
|
||||||
|
);
|
||||||
|
p.draw_styled(&player_style, ctx.display).unwrap();
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &mut Context) -> Option<Action> {
|
fn update(&mut self, ctx: &mut Context) -> Option<Action> {
|
||||||
let Some(button) = ctx.button_pressed else {
|
let Some(button) = ctx.button_pressed else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let player_old = self.player;
|
Some(match button {
|
||||||
|
Button::Up if self.map.can_move(self.player, Direction::North) => {
|
||||||
match button {
|
self.player.1 -= 1;
|
||||||
Button::Up if self.map.can_move(self.player_pos(), Direction::North) => {
|
|
||||||
self.player.y -= 1;
|
|
||||||
self.old_player = Some(player_old);
|
|
||||||
Some(Action::PartialRender)
|
|
||||||
}
|
}
|
||||||
Button::Down if self.map.can_move(self.player_pos(), Direction::South) => {
|
Button::Down if self.map.can_move(self.player, Direction::South) => {
|
||||||
self.player.y += 1;
|
self.player.1 += 1;
|
||||||
self.old_player = Some(player_old);
|
|
||||||
Some(Action::PartialRender)
|
|
||||||
}
|
}
|
||||||
Button::Left if self.map.can_move(self.player_pos(), Direction::West) => {
|
Button::Left if self.map.can_move(self.player, Direction::West) => {
|
||||||
self.player.x -= 1;
|
self.player.0 -= 1;
|
||||||
self.old_player = Some(player_old);
|
|
||||||
Some(Action::PartialRender)
|
|
||||||
}
|
}
|
||||||
Button::Right if self.map.can_move(self.player_pos(), Direction::East) => {
|
Button::Right if self.map.can_move(self.player, Direction::East) => {
|
||||||
self.player.x += 1;
|
self.player.0 += 1;
|
||||||
self.old_player = Some(player_old);
|
|
||||||
Some(Action::PartialRender)
|
|
||||||
}
|
|
||||||
Button::Back => Some(Action::GoToMenu),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
|
Button::Back => {}
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::Button;
|
use crate::Button;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use epd_waveshare::color::Color;
|
|
||||||
use profont::PROFONT_18_POINT;
|
use profont::PROFONT_18_POINT;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
@ -49,30 +48,31 @@ impl App for Menu {
|
|||||||
};
|
};
|
||||||
match button {
|
match button {
|
||||||
Button::Up => match self.selected {
|
Button::Up => match self.selected {
|
||||||
MenuEntry::Labirynth => None,
|
MenuEntry::Labirynth => return None,
|
||||||
MenuEntry::Pairs => {
|
MenuEntry::Pairs => {
|
||||||
self.selected = MenuEntry::Labirynth;
|
self.selected = MenuEntry::Labirynth;
|
||||||
Some(Action::Render)
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Button::Down => match self.selected {
|
Button::Down => match self.selected {
|
||||||
MenuEntry::Labirynth => {
|
MenuEntry::Labirynth => {
|
||||||
self.selected = MenuEntry::Pairs;
|
self.selected = MenuEntry::Pairs;
|
||||||
Some(Action::Render)
|
None
|
||||||
}
|
}
|
||||||
MenuEntry::Pairs => None,
|
MenuEntry::Pairs => None,
|
||||||
},
|
},
|
||||||
Button::Circle => match self.selected {
|
Button::Circle => match self.selected {
|
||||||
MenuEntry::Labirynth => Some(Action::StartMaze),
|
MenuEntry::Labirynth => return Some(Action::StartMaze),
|
||||||
MenuEntry::Pairs => Some(Action::StartPairs),
|
MenuEntry::Pairs => return Some(Action::StartPairs),
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, ctx: &mut Context) {
|
fn draw(&self, ctx: &mut Context) {
|
||||||
let style = MonoTextStyle::new(&PROFONT_18_POINT, Color::Black);
|
/*
|
||||||
let selected_style = MonoTextStyle::new(&PROFONT_18_POINT, Color::Black);
|
let style = MonoTextStyle::new(&PROFONT_18_POINT, TriColor::Black);
|
||||||
|
let selected_style = MonoTextStyle::new(&PROFONT_18_POINT, TriColor::Red);
|
||||||
|
|
||||||
MenuEntry::iter().for_each(|entry| {
|
MenuEntry::iter().for_each(|entry| {
|
||||||
if entry == self.selected {
|
if entry == self.selected {
|
||||||
@ -82,7 +82,7 @@ impl App for Menu {
|
|||||||
selected_style,
|
selected_style,
|
||||||
TextStyle::default(),
|
TextStyle::default(),
|
||||||
)
|
)
|
||||||
.draw(&mut ctx.epaper.display)
|
.draw(display)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,8 +96,9 @@ impl App for Menu {
|
|||||||
},
|
},
|
||||||
TextStyle::default(),
|
TextStyle::default(),
|
||||||
)
|
)
|
||||||
.draw(&mut ctx.epaper.display)
|
.draw(display)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ use embedded_graphics::{
|
|||||||
Drawable,
|
Drawable,
|
||||||
};
|
};
|
||||||
use esp_hal::rng::Trng;
|
use esp_hal::rng::Trng;
|
||||||
use esp_println::println;
|
|
||||||
use maze_game::MazeGame;
|
use maze_game::MazeGame;
|
||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
|
use weact_studio_epd::{graphics::Display290TriColor, TriColor};
|
||||||
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
@ -16,13 +16,10 @@ pub mod guess_game;
|
|||||||
pub mod maze_game;
|
pub mod maze_game;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
GoToMenu,
|
GoToMenu,
|
||||||
StartMaze,
|
StartMaze,
|
||||||
StartPairs,
|
StartPairs,
|
||||||
Render,
|
|
||||||
PartialRender,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Application {
|
pub enum Application {
|
||||||
@ -30,72 +27,47 @@ pub enum Application {
|
|||||||
Maze(maze_game::MazeGame),
|
Maze(maze_game::MazeGame),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Application {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Menu(Menu::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
pub fn draw(&self, ctx: &mut Context, full: bool) {
|
pub fn update(&mut self, ctx: &mut Context, trng: &mut Trng) {
|
||||||
if full {
|
|
||||||
ctx.epaper.slow_update();
|
|
||||||
ctx.epaper.full_erase();
|
|
||||||
}
|
|
||||||
match self {
|
|
||||||
Self::Menu(menu) => menu.draw(ctx),
|
|
||||||
Self::Maze(maze) => maze.draw(ctx),
|
|
||||||
};
|
|
||||||
if full {
|
|
||||||
ctx.epaper.full_update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_and_draw(&mut self, ctx: &mut Context, trng: &mut Trng) {
|
|
||||||
let Some(action) = (match self {
|
let Some(action) = (match self {
|
||||||
Self::Menu(menu) => menu.update_and_draw(ctx),
|
Self::Menu(menu) => menu.update(ctx),
|
||||||
Self::Maze(maze) => maze.update_and_draw(ctx),
|
Self::Maze(maze) => maze.update(ctx),
|
||||||
}) else {
|
}) else {
|
||||||
println!("No action");
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
println!("Action: {action:?}");
|
|
||||||
match action {
|
match action {
|
||||||
Action::StartMaze => {
|
Action::StartMaze => {
|
||||||
let mut maze = MazeGame::new();
|
let mut maze = MazeGame::new();
|
||||||
maze.start(trng);
|
maze.start(trng);
|
||||||
*self = Application::Maze(maze);
|
*self = Application::Maze(maze);
|
||||||
self.draw(ctx, true);
|
|
||||||
}
|
}
|
||||||
Action::GoToMenu => {
|
Action::GoToMenu => {
|
||||||
let mut menu = Menu::new();
|
let mut menu = Menu::new();
|
||||||
menu.start(trng);
|
menu.start(trng);
|
||||||
*self = Application::Menu(menu);
|
*self = Application::Menu(menu);
|
||||||
self.draw(ctx, true);
|
|
||||||
}
|
}
|
||||||
Action::StartPairs => {}
|
Action::StartPairs => {}
|
||||||
Action::Render => {
|
|
||||||
self.draw(ctx, true);
|
|
||||||
}
|
}
|
||||||
Action::PartialRender => {
|
|
||||||
self.draw(ctx, false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
pub fn draw(&self, ctx: &mut Context) {
|
||||||
|
match self {
|
||||||
|
Self::Menu(menu) => menu.draw(ctx),
|
||||||
|
Self::Maze(maze) => maze.draw(ctx),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait App {
|
pub trait App {
|
||||||
fn start(&mut self, trng: &mut Trng);
|
fn start(&mut self, trng: &mut Trng);
|
||||||
|
|
||||||
fn draw(&self, ctx: &mut Context);
|
fn draw(&self, display: &mut Context);
|
||||||
|
|
||||||
fn update(&mut self, ctx: &mut Context) -> Option<Action>;
|
fn update(&mut self, ctx: &mut Context) -> Option<Action>;
|
||||||
|
|
||||||
fn update_and_draw(&mut self, ctx: &mut Context) -> Option<Action> {
|
fn draw_and_update(&mut self, ctx: &mut Context) -> Option<Action> {
|
||||||
let action = self.update(ctx);
|
let action = self.update(ctx);
|
||||||
if action.is_none() {
|
|
||||||
self.draw(ctx);
|
self.draw(ctx);
|
||||||
}
|
|
||||||
action
|
action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
208
src/epaper.rs
208
src/epaper.rs
@ -1,208 +0,0 @@
|
|||||||
use embedded_graphics::{
|
|
||||||
geometry::{Point, Size},
|
|
||||||
mono_font::MonoTextStyleBuilder,
|
|
||||||
primitives::{Primitive, PrimitiveStyleBuilder, Rectangle},
|
|
||||||
text::{Baseline, Text, TextStyleBuilder},
|
|
||||||
Drawable,
|
|
||||||
};
|
|
||||||
use embedded_hal::delay::DelayNs;
|
|
||||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
|
||||||
use epd_waveshare::epd3in7::*;
|
|
||||||
use epd_waveshare::prelude::*;
|
|
||||||
use esp_hal::{
|
|
||||||
clock::Clocks,
|
|
||||||
delay::Delay,
|
|
||||||
gpio::{Gpio18, Gpio19, Gpio20, Gpio21, Gpio22, Gpio23, Input, Level, Output, NO_PIN},
|
|
||||||
prelude::*,
|
|
||||||
spi::{master::Spi, FullDuplexMode, SpiMode},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type CS<'b> = Output<'b, Gpio20>;
|
|
||||||
pub type BUS<'b> = Input<'b, Gpio23>;
|
|
||||||
pub type RST<'b> = Output<'b, Gpio22>;
|
|
||||||
pub type DC<'b> = Output<'b, Gpio21>;
|
|
||||||
pub type SpiBus<'b> = Spi<'b, esp_hal::peripherals::SPI2, FullDuplexMode>;
|
|
||||||
pub type SPI<'b> = ExclusiveDevice<SpiBus<'b>, CS<'b>, Delay>;
|
|
||||||
pub type Driver<'b> = EPD3in7<SPI<'b>, BUS<'b>, DC<'b>, RST<'b>, Delay>;
|
|
||||||
|
|
||||||
pub struct Epaper<'d> {
|
|
||||||
pub display: Display3in7,
|
|
||||||
pub driver: Driver<'d>,
|
|
||||||
pub spi: SPI<'d>,
|
|
||||||
pub delay: Delay,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Board mapping:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* _________
|
|
||||||
* __| |__
|
|
||||||
* | |
|
|
||||||
* 3.3v O |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | O 23 - BUSY
|
|
||||||
* | O 22 - RESET (RST)
|
|
||||||
* | O 21 - DC
|
|
||||||
* | O 20 - CS
|
|
||||||
* | O 19 - CLK / SCLK
|
|
||||||
* | O 18 - DIM
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* |__ ___ __|
|
|
||||||
* | | | |
|
|
||||||
* CH343 ESP32C6
|
|
||||||
*
|
|
||||||
* Cannonical wiring
|
|
||||||
* * BUSY - purple - Gpio23
|
|
||||||
* * RST - white - Gpio22
|
|
||||||
* * DC - green - Gpio21
|
|
||||||
* * CS - orange - Gpio20
|
|
||||||
* * CLK - yellow - Gpio19
|
|
||||||
* * DIM - blue - Gpio18
|
|
||||||
* * GND - brown - ANY GND
|
|
||||||
* * VCC - gray - 3.3v
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// let mut epaper = epaper::Epaper::new(mosi, sclk, cs, dc, rst, busy, peripherals.SPI2, &clocks);
|
|
||||||
impl<'d> Epaper<'d> {
|
|
||||||
pub fn new(
|
|
||||||
din: Gpio18,
|
|
||||||
sclk: Gpio19,
|
|
||||||
cs: Gpio20,
|
|
||||||
dc: Gpio21,
|
|
||||||
rst: Gpio22,
|
|
||||||
busy: Gpio23,
|
|
||||||
spi: esp_hal::peripherals::SPI2,
|
|
||||||
clocks: &'d Clocks,
|
|
||||||
) -> Self {
|
|
||||||
let mut delay = Delay::new(clocks);
|
|
||||||
|
|
||||||
let spi_bus: SpiBus = Spi::new(spi, 4_000.kHz(), SpiMode::Mode0, clocks).with_pins(
|
|
||||||
Some(sclk),
|
|
||||||
Some(din),
|
|
||||||
NO_PIN,
|
|
||||||
NO_PIN, // cs is handled by the exclusive device
|
|
||||||
);
|
|
||||||
|
|
||||||
let cs: CS = Output::new(cs, Level::High);
|
|
||||||
let busy: BUS = Input::new(busy, esp_hal::gpio::Pull::Up);
|
|
||||||
let dc: DC = Output::new(dc, Level::High);
|
|
||||||
let rst = Output::new(rst, Level::High);
|
|
||||||
|
|
||||||
log::info!("Intializing SPI Device...");
|
|
||||||
let mut spi: SPI =
|
|
||||||
ExclusiveDevice::new(spi_bus, cs, delay).expect("SPI device initialize error");
|
|
||||||
log::info!("SPI ExclusiveDevice done");
|
|
||||||
let mut driver =
|
|
||||||
EPD3in7::new(&mut spi, busy, dc, rst, &mut delay, None).expect("eink initalize error");
|
|
||||||
log::info!("Driver DONE");
|
|
||||||
|
|
||||||
let mut display = Display3in7::default();
|
|
||||||
log::info!("Display DONE");
|
|
||||||
display.set_rotation(DisplayRotation::Rotate90);
|
|
||||||
log::info!("Display ratation DONE");
|
|
||||||
driver.set_background_color(Color::White);
|
|
||||||
driver.clear_frame(&mut spi, &mut delay).unwrap();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
display,
|
|
||||||
driver,
|
|
||||||
spi,
|
|
||||||
delay,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn full_update(&mut self) {
|
|
||||||
// self.driver
|
|
||||||
// .clear_frame(&mut self.spi, &mut self.delay)
|
|
||||||
// .unwrap();
|
|
||||||
self.driver
|
|
||||||
.update_and_display_frame(&mut self.spi, self.display.buffer(), &mut self.delay)
|
|
||||||
.expect("display frame new graphics");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn full_erase(&mut self) {
|
|
||||||
self.driver
|
|
||||||
.clear_frame(&mut self.spi, &mut self.delay)
|
|
||||||
.unwrap();
|
|
||||||
log::info!("Clearing display");
|
|
||||||
let _ = Rectangle::new(Point::new(0, 0), Size::new(480, 280))
|
|
||||||
.into_styled(
|
|
||||||
PrimitiveStyleBuilder::new()
|
|
||||||
.fill_color(Color::White)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.draw(&mut self.display);
|
|
||||||
log::info!("Clearing display DONE");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fast_update(&mut self) {
|
|
||||||
self.driver
|
|
||||||
.set_lut(&mut self.spi, &mut self.delay, Some(RefreshLut::Quick))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
pub fn slow_update(&mut self) {
|
|
||||||
self.driver
|
|
||||||
.set_lut(&mut self.spi, &mut self.delay, Some(RefreshLut::Full))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_welcome(&mut self) {
|
|
||||||
// draw white on black background
|
|
||||||
let style = MonoTextStyleBuilder::new()
|
|
||||||
.font(&embedded_graphics::mono_font::ascii::FONT_9X15_BOLD)
|
|
||||||
.text_color(Color::Black)
|
|
||||||
.background_color(Color::White)
|
|
||||||
.build();
|
|
||||||
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
|
|
||||||
let _ = Text::with_text_style("Booting...", Point::new(90, 10), style, text_style)
|
|
||||||
.draw(&mut self.display);
|
|
||||||
self.driver
|
|
||||||
.update_and_display_frame(&mut self.spi, self.display.buffer(), &mut self.delay)
|
|
||||||
.expect("display frame new graphics");
|
|
||||||
self.delay.delay_ms(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wake_up(&mut self) {
|
|
||||||
self.driver.wake_up(&mut self.spi, &mut self.delay).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sleep(&mut self) {
|
|
||||||
let _ = self.driver.sleep(&mut self.spi, &mut self.delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_frame(&mut self) {
|
|
||||||
self.driver
|
|
||||||
.clear_frame(&mut self.spi, &mut self.delay)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn partial_update(&mut self, pos: Point, size: Size, buffer: &[u8]) {
|
|
||||||
let _ = self.driver.update_partial_frame(
|
|
||||||
&mut self.spi,
|
|
||||||
&mut self.delay,
|
|
||||||
buffer,
|
|
||||||
pos.x as u32,
|
|
||||||
pos.y as u32,
|
|
||||||
size.width,
|
|
||||||
size.height,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pub fn partial_update_buffer(&mut self, pos: Point, size: Size) {
|
|
||||||
let _ = self.driver.update_partial_frame(
|
|
||||||
&mut self.spi,
|
|
||||||
&mut self.delay,
|
|
||||||
self.display.buffer(),
|
|
||||||
pos.x as u32,
|
|
||||||
pos.y as u32,
|
|
||||||
size.width,
|
|
||||||
size.height,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
351
src/keypad.rs
351
src/keypad.rs
@ -1,351 +0,0 @@
|
|||||||
#[doc(hidden)]
|
|
||||||
pub extern crate core as _core;
|
|
||||||
|
|
||||||
use core::cell::RefCell;
|
|
||||||
|
|
||||||
use esp_hal::gpio::{Input, InputPin, OutputOpenDrain, OutputPin};
|
|
||||||
|
|
||||||
/// A virtual `embedded-hal` input pin representing one key of the keypad.
|
|
||||||
///
|
|
||||||
/// A `KeypadInput` stores references to one row and one column pin. When you
|
|
||||||
/// read from it with `.is_low()` or `.is_high()`, it secretly sets the column
|
|
||||||
/// pin low, reads from the row pin, and then sets the column pin high again.
|
|
||||||
/// The column pin is actually stored inside a `RefCell` in the keypad struct,
|
|
||||||
/// so that multiple `KeypadInput`s can mutate the column pin's state as needed,
|
|
||||||
/// even though they only have a shared/immutable reference to it.
|
|
||||||
///
|
|
||||||
/// This has several implications.
|
|
||||||
///
|
|
||||||
/// 1) Reading from `KeypadInput`s is not reentrant. If we were in the middle
|
|
||||||
/// of reading a `KeypadInput` and entered an interrupt service routine that
|
|
||||||
/// read any `KeypadInput` of the same keypad, we might read an incorrect value
|
|
||||||
/// or cause a `panic`.
|
|
||||||
///
|
|
||||||
/// 2) Reading from a `KeypadInput` is slower than reading from a real input
|
|
||||||
/// pin, because it needs to change the output pin state twice for every read.
|
|
||||||
pub struct KeypadInput<'a> {
|
|
||||||
row: &'a RefCell<Input<'a, InputPin>>,
|
|
||||||
col: &'a RefCell<OutputOpenDrain<'a, dyn InputPin + OutputPin>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, E> KeypadInput<'a, E> {
|
|
||||||
/// Create a new `KeypadInput`. For use in macros.
|
|
||||||
pub fn new(row: &'a dyn InputPin, col: &'a RefCell<dyn OutputPin>) -> Self {
|
|
||||||
Self { row, col }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, E> InputPin for KeypadInput<'a, E> {
|
|
||||||
type Error = E;
|
|
||||||
/// Read the state of the key at this row and column. Not reentrant.
|
|
||||||
fn is_high(&self) -> Result<bool, E> {
|
|
||||||
Ok(!self.is_low()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read the state of the key at this row and column. Not reentrant.
|
|
||||||
fn is_low(&self) -> Result<bool, E> {
|
|
||||||
self.col.borrow_mut().set_low()?;
|
|
||||||
let out = self.row.is_low()?;
|
|
||||||
self.col.borrow_mut().set_high()?;
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define a new struct representing your keypad matrix circuit.
|
|
||||||
///
|
|
||||||
/// Every pin has a unique type, depending on its pin number and its current
|
|
||||||
/// mode. This struct is where you specify which pin types will be used in the
|
|
||||||
/// rows and columns of the keypad matrix. All the row pins must implement the
|
|
||||||
/// `InputPin` trait, and the column pins must implement the `OutputPin` trait.
|
|
||||||
/// The associated `Error` type of the `InputPin` and `OutputPin` traits must be
|
|
||||||
/// the same for every row and column pin, and you must specify it after your
|
|
||||||
/// struct name with `<Error = ...>`
|
|
||||||
///
|
|
||||||
/// You can specify the visibility of the struct (eg. `pub`) as usual, and add
|
|
||||||
/// doc comments using the `#[doc="..."]` attribute.
|
|
||||||
///
|
|
||||||
/// Don't access or modify the struct's fields directly. Instead, use
|
|
||||||
/// the methods implemented by this macro, documented here:
|
|
||||||
/// [`example_generated::ExampleKeypad`](./example_generated/struct.ExampleKeypad.html)
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![cfg_attr(docs_rs_workaround, feature(macro_vis_matcher))]
|
|
||||||
/// #[macro_use]
|
|
||||||
/// extern crate keypad;
|
|
||||||
///
|
|
||||||
/// use keypad::mock_hal::{self, Input, OpenDrain, Output, PullUp};
|
|
||||||
/// use core::convert::Infallible;
|
|
||||||
///
|
|
||||||
/// keypad_struct! {
|
|
||||||
/// #[doc="My super-special keypad."]
|
|
||||||
/// pub struct ExampleKeypad<Error = Infallible> {
|
|
||||||
/// rows: (
|
|
||||||
/// mock_hal::gpioa::PA0<Input<PullUp>>,
|
|
||||||
/// mock_hal::gpioa::PA1<Input<PullUp>>,
|
|
||||||
/// mock_hal::gpioa::PA2<Input<PullUp>>,
|
|
||||||
/// mock_hal::gpioa::PA3<Input<PullUp>>,
|
|
||||||
/// ),
|
|
||||||
/// columns: (
|
|
||||||
/// mock_hal::gpioa::PA4<Output<OpenDrain>>,
|
|
||||||
/// mock_hal::gpioa::PA5<Output<OpenDrain>>,
|
|
||||||
/// mock_hal::gpioa::PA6<Output<OpenDrain>>,
|
|
||||||
/// mock_hal::gpioa::PA7<Output<OpenDrain>>,
|
|
||||||
/// mock_hal::gpioa::PA8<Output<OpenDrain>>,
|
|
||||||
/// ),
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This macro uses `unsafe` to create an array with uninitialized memory, which
|
|
||||||
/// is then immediately initialized in a loop. This is fine as long as there is
|
|
||||||
/// not a bug in how the macro calculates the dimensions of the array.
|
|
||||||
|
|
||||||
// There are two reasons why this big, scary macro is necessary:
|
|
||||||
//
|
|
||||||
// 1) Every single pin has a unique type, and we don't know which pins will be used. We know that
|
|
||||||
// they all implement a certain trait, but that doesn't help much because it doesn't tell us the
|
|
||||||
// size of the type, so we can't directly stick it into the struct. If we could use dynamic
|
|
||||||
// allocation, we could just store pins on the heap as boxed trait objects. But this crate needs
|
|
||||||
// to work on embedded platforms without an allocator! So, we use this macro to generate a
|
|
||||||
// struct containing the exact, concrete pin types the user provides.
|
|
||||||
//
|
|
||||||
// 2) We don't know how many pins there will be, because the keypad could have any number of rows
|
|
||||||
// and columns. That makes it hard to implement `decompose()`, which needs to iterate over the
|
|
||||||
// row and column pins. We can't store pins in arrays because they all have unique types, but we
|
|
||||||
// can store them in tuples instead. The problem is that you can't actually iterate over a tuple,
|
|
||||||
// and even indexing into arbitrary fields of a tuple using a macro is stupidly hard. The best
|
|
||||||
// approach I could come up with was to repeatedly destructure the tuple with different patterns,
|
|
||||||
// like this:
|
|
||||||
//
|
|
||||||
// let tuple = (0, 1, 2);
|
|
||||||
// let array = [
|
|
||||||
// {
|
|
||||||
// let (ref x, ..) = tuple;
|
|
||||||
// x
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// let (_, ref x, ..) = tuple;
|
|
||||||
// x
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// let (_, _, ref x, ..) = tuple;
|
|
||||||
// x
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
// for reference in array.into_iter() {
|
|
||||||
// // ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// So that's how the `keypad_struct!()` macro iterates over tuples of pins. It counts the length of
|
|
||||||
// the tuple (also tricky!), creates patterns with increasing numbers of underscores, and uses them
|
|
||||||
// to build up a temporary array of references that it can iterate over. Luckily this code only
|
|
||||||
// needs to be run once, in `decompose()`, and not every time we read from a pin.
|
|
||||||
//
|
|
||||||
// I can't think of any simpler design that still has a convenient API and allows the keypad struct
|
|
||||||
// to own the row and column pins. If they weren't owned, the crate would be less convenient to use
|
|
||||||
// but could provide a generic Keypad struct like this:
|
|
||||||
//
|
|
||||||
// pub struct Keypad<'a, E, const R: usize, const C: usize> {
|
|
||||||
// rows: [&'a dyn InputPin<Error = E>; R],
|
|
||||||
// columns: [&'a RefCell<dyn OutputPin<Error = E>>; C],
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! keypad_struct {
|
|
||||||
(
|
|
||||||
$(#[$attributes:meta])* $visibility:vis struct $struct_name:ident {
|
|
||||||
rows: ( $($row_type:ty),* $(,)* ),
|
|
||||||
columns: ( $($col_type:ty),* $(,)* ),
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
compile_error!("You must specify the associated `Error` type of the row and column pins'\
|
|
||||||
`InputPin` and `OutputPin` traits.\n\
|
|
||||||
Example: `struct MyStruct <Error = Infallible> { ... }`");
|
|
||||||
};
|
|
||||||
(
|
|
||||||
$(#[$attributes:meta])* $visibility:vis struct $struct_name:ident <Error = $error_type:ty> {
|
|
||||||
rows: ( $($row_type:ty),* $(,)* ),
|
|
||||||
columns: ( $($col_type:ty),* $(,)* ),
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
$(#[$attributes])* $visibility struct $struct_name {
|
|
||||||
/// The input pins used for reading each row.
|
|
||||||
rows: ($($row_type),* ,),
|
|
||||||
/// The output pins used for scanning through each column. They're
|
|
||||||
/// wrapped in RefCells so that we can change their state even if we
|
|
||||||
/// only have shared/immutable reference to them. This lets us
|
|
||||||
/// actively scan the matrix when reading the state of a virtual
|
|
||||||
/// `KeypadInput` pin.
|
|
||||||
columns: ($($crate::_core::cell::RefCell<$col_type>),* ,),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $struct_name {
|
|
||||||
/// Get a 2d array of embedded-hal input pins, each representing one
|
|
||||||
/// key in the keypad matrix.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
$visibility fn decompose<'a>(&'a self) ->
|
|
||||||
keypad_struct!(
|
|
||||||
@array2d_type
|
|
||||||
$crate::KeypadInput<'a, $error_type>,
|
|
||||||
($($row_type),*)
|
|
||||||
($($crate::_core::cell::RefCell<$col_type>),*)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
|
|
||||||
let rows: [
|
|
||||||
&dyn $crate::embedded_hal::digital::v2::InputPin<Error = $error_type>;
|
|
||||||
keypad_struct!(@count $($row_type)*)
|
|
||||||
]
|
|
||||||
= keypad_struct!(@tuple self.rows, ($($row_type),*));
|
|
||||||
|
|
||||||
let columns: [
|
|
||||||
&$crate::_core::cell::RefCell<dyn $crate::embedded_hal::digital::v2::OutputPin<Error = $error_type>>;
|
|
||||||
keypad_struct!(@count $($col_type)*)
|
|
||||||
]
|
|
||||||
= keypad_struct!(@tuple self.columns, ($($col_type),*));
|
|
||||||
|
|
||||||
// Create an uninitialized 2d array of MaybeUninit.
|
|
||||||
let mut out: keypad_struct!(
|
|
||||||
@array2d_type
|
|
||||||
$crate::_core::mem::MaybeUninit<$crate::KeypadInput<'a, $error_type>>,
|
|
||||||
($($row_type),*)
|
|
||||||
($($crate::_core::cell::RefCell<$col_type>),*)
|
|
||||||
) = unsafe {
|
|
||||||
$crate::_core::mem::MaybeUninit::uninit().assume_init()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize each element with a KeypadInput struct
|
|
||||||
for r in 0..rows.len() {
|
|
||||||
for c in 0..columns.len() {
|
|
||||||
out[r][c].write($crate::KeypadInput::new(rows[r], columns[c]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// All elements are initialized. Transmute the array to the initialized type.
|
|
||||||
unsafe { $crate::_core::mem::transmute::<_, _>(out) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Give back ownership of the row and column pins.
|
|
||||||
///
|
|
||||||
/// This consumes the keypad struct. All references to its virtual
|
|
||||||
/// `KeypadInput` pins must have gone out of scope before you try to
|
|
||||||
/// call `.release()`, or it will fail to compile.
|
|
||||||
///
|
|
||||||
/// The column pins will be returned inside of `RefCell`s (because
|
|
||||||
/// macros are hard). You can use `.into_inner()` to extract
|
|
||||||
/// each column pin from its `RefCell`.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
$visibility fn release(self) ->(($($row_type),* ,), ($($crate::_core::cell::RefCell<$col_type>),* ,)) {
|
|
||||||
(self.rows, self.columns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(@array2d_type $element_type:ty, ($($row:ty),*) ($($col:ty),*) ) => {
|
|
||||||
[keypad_struct!(@array1d_type $element_type, ($($col),*)) ; keypad_struct!(@count $($row)*)]
|
|
||||||
};
|
|
||||||
(@array1d_type $element_type:ty, ($($col:ty),*)) => {
|
|
||||||
[$element_type ; keypad_struct!(@count $($col)*)]
|
|
||||||
};
|
|
||||||
(@count $($token_trees:tt)*) => {
|
|
||||||
0usize $(+ keypad_struct!(@replace $token_trees 1usize))*
|
|
||||||
};
|
|
||||||
(@replace $_t:tt $sub:expr) => {
|
|
||||||
$sub
|
|
||||||
};
|
|
||||||
(@underscore $unused:tt) => {
|
|
||||||
_
|
|
||||||
};
|
|
||||||
(@destructure_ref $tuple:expr, ($($repeat_n:ty),*)) => {
|
|
||||||
{
|
|
||||||
let (
|
|
||||||
$(keypad_struct!(@underscore $repeat_n),)*
|
|
||||||
ref nth, ..) = $tuple;
|
|
||||||
nth
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(@tuple_helper $tuple:expr, ($head:ty), ($($result:expr),* $(,)*)) => {
|
|
||||||
[
|
|
||||||
keypad_struct!(@destructure_ref $tuple, ()),
|
|
||||||
$($result),*
|
|
||||||
]
|
|
||||||
};
|
|
||||||
(@tuple_helper $tuple:expr, ($head:ty $(,$repeats:ty)* $(,)*), ($($result:expr),* $(,)*)) => {
|
|
||||||
keypad_struct!(
|
|
||||||
@tuple_helper $tuple, ($($repeats),*),
|
|
||||||
(
|
|
||||||
keypad_struct!(@destructure_ref $tuple, ($($repeats),*)),
|
|
||||||
$($result),*
|
|
||||||
)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(@tuple $tuple:expr, ($($repeats:ty),*)) => {
|
|
||||||
keypad_struct!(@tuple_helper $tuple, ($($repeats),*) , ())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an instance of the struct you defined with the `keypad_struct!()` macro..
|
|
||||||
///
|
|
||||||
/// The pin numbers and modes will need to match the ones you specified with `keypad_struct!()`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![cfg_attr(docs_rs_workaround, feature(macro_vis_matcher))]
|
|
||||||
/// # #[macro_use]
|
|
||||||
/// # extern crate keypad;
|
|
||||||
/// # use core::convert::Infallible;
|
|
||||||
/// # use keypad::mock_hal::{self, Input, OpenDrain, Output, PullUp};
|
|
||||||
/// # use keypad::mock_hal::{GpioExt, GPIOA};
|
|
||||||
/// # keypad_struct!{
|
|
||||||
/// # pub struct ExampleKeypad<Error = Infallible>{
|
|
||||||
/// # rows: (
|
|
||||||
/// # mock_hal::gpioa::PA0<Input<PullUp>>,
|
|
||||||
/// # mock_hal::gpioa::PA1<Input<PullUp>>,
|
|
||||||
/// # mock_hal::gpioa::PA2<Input<PullUp>>,
|
|
||||||
/// # mock_hal::gpioa::PA3<Input<PullUp>>,
|
|
||||||
/// # ),
|
|
||||||
/// # columns: (
|
|
||||||
/// # mock_hal::gpioa::PA4<Output<OpenDrain>>,
|
|
||||||
/// # mock_hal::gpioa::PA5<Output<OpenDrain>>,
|
|
||||||
/// # mock_hal::gpioa::PA6<Output<OpenDrain>>,
|
|
||||||
/// # mock_hal::gpioa::PA7<Output<OpenDrain>>,
|
|
||||||
/// # mock_hal::gpioa::PA8<Output<OpenDrain>>,
|
|
||||||
/// # ),
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// # fn main() {
|
|
||||||
/// let pins = GPIOA::split();
|
|
||||||
///
|
|
||||||
/// let keypad = keypad_new!(ExampleKeypad {
|
|
||||||
/// rows: (
|
|
||||||
/// pins.pa0.into_pull_up_input(),
|
|
||||||
/// pins.pa1.into_pull_up_input(),
|
|
||||||
/// pins.pa2.into_pull_up_input(),
|
|
||||||
/// pins.pa3.into_pull_up_input(),
|
|
||||||
/// ),
|
|
||||||
/// columns: (
|
|
||||||
/// pins.pa4.into_open_drain_output(),
|
|
||||||
/// pins.pa5.into_open_drain_output(),
|
|
||||||
/// pins.pa6.into_open_drain_output(),
|
|
||||||
/// pins.pa7.into_open_drain_output(),
|
|
||||||
/// pins.pa8.into_open_drain_output(),
|
|
||||||
/// ),
|
|
||||||
/// });
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! keypad_new {
|
|
||||||
( $struct_name:ident {
|
|
||||||
rows: ( $($row_val:expr),* $(,)* ),
|
|
||||||
columns: ( $($col_val:expr),* $(,)* ),
|
|
||||||
}) => {
|
|
||||||
$struct_name {
|
|
||||||
rows: ($($row_val),* ,),
|
|
||||||
columns: ($($crate::_core::cell::RefCell::new($col_val)),* ,),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
322
src/main.rs
322
src/main.rs
@ -1,12 +1,16 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
mod epaper;
|
use apps::{menu::Menu, Application};
|
||||||
|
use display_interface_spi::SPIInterface;
|
||||||
use apps::Application;
|
use embedded_graphics::{
|
||||||
use embedded_graphics::Drawable;
|
geometry::Point,
|
||||||
|
mono_font::{MonoTextStyle, MonoTextStyleBuilder},
|
||||||
|
text::{Baseline, Text, TextStyle, TextStyleBuilder},
|
||||||
|
Drawable,
|
||||||
|
};
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||||
use epaper::Epaper;
|
|
||||||
use epd_waveshare::epd3in7::*;
|
use epd_waveshare::epd3in7::*;
|
||||||
use epd_waveshare::prelude::*;
|
use epd_waveshare::prelude::*;
|
||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
@ -14,13 +18,13 @@ use esp_hal::{
|
|||||||
clock::ClockControl,
|
clock::ClockControl,
|
||||||
delay::Delay,
|
delay::Delay,
|
||||||
gpio::{
|
gpio::{
|
||||||
Gpio0, Gpio1, Gpio10, Gpio20, Gpio21, Gpio22, Gpio23, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8,
|
Gpio10, Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8,
|
||||||
Input, Io, Level, Output, OutputOpenDrain, Pull,
|
Input, Io, Level, Output, Pull, NO_PIN,
|
||||||
},
|
},
|
||||||
peripherals::Peripherals,
|
peripherals::Peripherals,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
rng::Trng,
|
rng::Trng,
|
||||||
spi::{master::Spi, FullDuplexMode},
|
spi::{master::Spi, FullDuplexMode, SpiMode},
|
||||||
system::SystemControl,
|
system::SystemControl,
|
||||||
};
|
};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
@ -42,7 +46,7 @@ fn main() -> ! {
|
|||||||
let system = SystemControl::new(peripherals.SYSTEM);
|
let system = SystemControl::new(peripherals.SYSTEM);
|
||||||
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
let clocks = ClockControl::max(system.clock_control).freeze();
|
let clocks = ClockControl::max(system.clock_control).freeze();
|
||||||
let delay = Delay::new(&clocks);
|
let mut delay = Delay::new(&clocks);
|
||||||
|
|
||||||
esp_println::logger::init_logger(LevelFilter::Trace);
|
esp_println::logger::init_logger(LevelFilter::Trace);
|
||||||
// esp_println::logger::init_logger_from_env();
|
// esp_println::logger::init_logger_from_env();
|
||||||
@ -53,7 +57,7 @@ fn main() -> ! {
|
|||||||
// Configure SPI
|
// Configure SPI
|
||||||
// Settings are taken from
|
// Settings are taken from
|
||||||
|
|
||||||
// Pins for e-paper
|
// Pins for WeAct
|
||||||
let mosi /* sda */ = io.pins.gpio18; // D10 / GPIO18
|
let mosi /* sda */ = io.pins.gpio18; // D10 / GPIO18
|
||||||
let sclk = io.pins.gpio19; // D8 / GPIO19
|
let sclk = io.pins.gpio19; // D8 / GPIO19
|
||||||
let cs = io.pins.gpio20; // D9 / GPIO20
|
let cs = io.pins.gpio20; // D9 / GPIO20
|
||||||
@ -61,45 +65,109 @@ fn main() -> ! {
|
|||||||
let rst = io.pins.gpio22; // D4 / GPIO22
|
let rst = io.pins.gpio22; // D4 / GPIO22
|
||||||
let busy = io.pins.gpio23; // D5 / GPIO23
|
let busy = io.pins.gpio23; // D5 / GPIO23
|
||||||
|
|
||||||
let mut epaper = epaper::Epaper::new(mosi, sclk, cs, dc, rst, busy, peripherals.SPI2, &clocks);
|
let spi_bus: SpiBus = Spi::new(peripherals.SPI2, 4_000.kHz(), SpiMode::Mode0, &clocks)
|
||||||
|
.with_pins(
|
||||||
|
Some(sclk),
|
||||||
|
Some(mosi),
|
||||||
|
NO_PIN,
|
||||||
|
NO_PIN, // cs is handled by the exclusive device
|
||||||
|
);
|
||||||
|
|
||||||
epaper.full_erase();
|
// Convert pins into InputPins and OutputPins
|
||||||
epaper.print_welcome();
|
/*
|
||||||
|
CS: OutputPin,
|
||||||
|
BUSY: InputPin,
|
||||||
|
DC: OutputPin,
|
||||||
|
RST: OutputPin,
|
||||||
|
*/
|
||||||
|
|
||||||
let mut kbd = Keypad {
|
let cs: CS = Output::new(cs, Level::High);
|
||||||
i1: Input::new(io.pins.gpio4, Pull::Up),
|
let busy: BUS = Input::new(busy, esp_hal::gpio::Pull::Up);
|
||||||
i2: Input::new(io.pins.gpio5, Pull::Up),
|
let dc: DC = Output::new(dc, Level::Low);
|
||||||
i3: Input::new(io.pins.gpio6, Pull::Up),
|
let rst = Output::new(rst, Level::High);
|
||||||
i4: Input::new(io.pins.gpio7, Pull::Up),
|
|
||||||
//---------------------------
|
log::info!("Intializing SPI Device...");
|
||||||
o1: OutputOpenDrain::new(io.pins.gpio0, Level::Low, Pull::Up),
|
let mut spi: SPI =
|
||||||
o2: OutputOpenDrain::new(io.pins.gpio1, Level::Low, Pull::Up),
|
ExclusiveDevice::new(spi_bus, cs, delay).expect("SPI device initialize error");
|
||||||
o3: OutputOpenDrain::new(io.pins.gpio8, Level::Low, Pull::Up),
|
// let spi_interface = SPIInterface::new(spi_device, dc);
|
||||||
o4: OutputOpenDrain::new(io.pins.gpio10, Level::Low, Pull::Up),
|
let mut driver =
|
||||||
|
EPD3in7::new(&mut spi, busy, dc, rst, &mut delay, None).expect("eink initalize error");
|
||||||
|
|
||||||
|
let mut display = Display3in7::default();
|
||||||
|
display.set_rotation(DisplayRotation::Rotate90);
|
||||||
|
|
||||||
|
// draw white on black background
|
||||||
|
let style = MonoTextStyleBuilder::new()
|
||||||
|
.font(&embedded_graphics::mono_font::ascii::FONT_6X10)
|
||||||
|
.text_color(Color::White)
|
||||||
|
.background_color(Color::Black)
|
||||||
|
.build();
|
||||||
|
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
|
||||||
|
let _ =
|
||||||
|
Text::with_text_style("Witamy!", Point::new(90, 10), style, text_style).draw(&mut display);
|
||||||
|
driver
|
||||||
|
.update_and_display_frame(&mut spi, display.buffer(), &mut delay)
|
||||||
|
.expect("display frame new graphics");
|
||||||
|
delay.delay_ms(500);
|
||||||
|
|
||||||
|
// epd2in13
|
||||||
|
// .set_refresh(&mut spi, &mut delay, RefreshLut::Quick)
|
||||||
|
// .unwrap();
|
||||||
|
driver.clear_frame(&mut spi, &mut delay).unwrap();
|
||||||
|
|
||||||
|
let mut kbd = Keyboard {
|
||||||
|
rows: Rows {
|
||||||
|
gpio2: Input::new(io.pins.gpio2, Pull::Up),
|
||||||
|
gpio3: Input::new(io.pins.gpio3, Pull::Up),
|
||||||
|
gpio4: Input::new(io.pins.gpio4, Pull::Up),
|
||||||
|
gpio5: Input::new(io.pins.gpio5, Pull::Up),
|
||||||
|
},
|
||||||
|
cols: Cols {
|
||||||
|
gpio6: Output::new(io.pins.gpio6, Level::Low),
|
||||||
|
gpio7: Output::new(io.pins.gpio7, Level::Low),
|
||||||
|
gpio8: Output::new(io.pins.gpio8, Level::Low),
|
||||||
|
gpio10: Output::new(io.pins.gpio10, Level::Low),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
button_pressed: None,
|
button_pressed: None,
|
||||||
epaper,
|
driver,
|
||||||
|
display,
|
||||||
|
spi,
|
||||||
delay,
|
delay,
|
||||||
};
|
};
|
||||||
let mut app = Application::default();
|
let mut app = Application::Menu(Menu::new());
|
||||||
let mut trng = Trng::new(peripherals.RNG, peripherals.ADC1);
|
let mut trng = Trng::new(peripherals.RNG, peripherals.ADC1);
|
||||||
|
|
||||||
app.draw(&mut ctx, true);
|
//app.draw(&mut display);
|
||||||
app.update_and_draw(&mut ctx, &mut trng);
|
//driver.full_update(&display).unwrap();
|
||||||
|
//driver.sleep().unwrap();
|
||||||
// ctx.epaper.sleep();
|
ctx.driver.clear_frame(&mut ctx.spi, &mut delay).unwrap();
|
||||||
|
ctx.driver
|
||||||
|
.update_and_display_frame(&mut ctx.spi, ctx.display.buffer(), &mut delay)
|
||||||
|
.expect("display frame new graphics");
|
||||||
|
let _ = ctx.driver.sleep(&mut ctx.spi, &mut delay);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
ctx.button_pressed = kbd.pressed();
|
ctx.button_pressed = kbd.pressed();
|
||||||
if ctx.button_pressed.is_some() {
|
if !ctx.button_pressed.is_none() {
|
||||||
// ctx.epaper.clear_frame();
|
|
||||||
log::info!("Wake up!");
|
log::info!("Wake up!");
|
||||||
app.update_and_draw(&mut ctx, &mut trng);
|
ctx.driver.wake_up(&mut ctx.spi, &mut delay).unwrap();
|
||||||
|
//display.clear(TriColor::White);
|
||||||
|
//app.update(&ctx, &mut trng);
|
||||||
|
// app.draw(&mut display);
|
||||||
|
//driver.full_update(&display).unwrap();
|
||||||
|
ctx.driver.clear_frame(&mut ctx.spi, &mut delay).unwrap();
|
||||||
|
ctx.driver
|
||||||
|
.update_and_display_frame(&mut ctx.spi, ctx.display.buffer(), &mut delay)
|
||||||
|
.expect("display frame new graphics");
|
||||||
|
let _ = ctx.driver.sleep(&mut ctx.spi, &mut delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
delay.delay(100.millis());
|
log::info!("Sleeping for 100ms...");
|
||||||
|
//driver.sleep().unwrap();
|
||||||
|
delay.delay(300.millis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,107 +203,107 @@ pub enum Button {
|
|||||||
Back,
|
Back,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Button {
|
|
||||||
pub fn from_idx(idx: usize) -> Option<Button> {
|
|
||||||
Some(match idx {
|
|
||||||
0 => Button::A,
|
|
||||||
1 => Button::Up,
|
|
||||||
2 => Button::B,
|
|
||||||
3 => Button::X,
|
|
||||||
//
|
|
||||||
4 => Button::Left,
|
|
||||||
5 => Button::Circle,
|
|
||||||
6 => Button::Right,
|
|
||||||
7 => Button::Y,
|
|
||||||
//
|
|
||||||
8 => Button::C,
|
|
||||||
9 => Button::Down,
|
|
||||||
10 => Button::D,
|
|
||||||
11 => Button::Z,
|
|
||||||
//
|
|
||||||
12 => Button::M1,
|
|
||||||
13 => Button::M2,
|
|
||||||
14 => Button::M3,
|
|
||||||
15 => Button::Back,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Context<'d> {
|
pub struct Context<'d> {
|
||||||
pub button_pressed: Option<Button>,
|
pub button_pressed: Option<Button>,
|
||||||
pub epaper: Epaper<'d>,
|
pub display: Display3in7,
|
||||||
|
pub driver: Driver<'d>,
|
||||||
|
pub spi: SPI<'d>,
|
||||||
pub delay: Delay,
|
pub delay: Delay,
|
||||||
}
|
}
|
||||||
struct Keypad<'d> {
|
|
||||||
// COLS
|
struct Rows<'d> {
|
||||||
o1: OutputOpenDrain<'d, Gpio0>, //
|
gpio2: Input<'d, Gpio2>, //
|
||||||
o2: OutputOpenDrain<'d, Gpio1>, //
|
gpio3: Input<'d, Gpio3>, //
|
||||||
o3: OutputOpenDrain<'d, Gpio8>, //
|
gpio4: Input<'d, Gpio4>, //
|
||||||
o4: OutputOpenDrain<'d, Gpio10>, //
|
gpio5: Input<'d, Gpio5>, //
|
||||||
// --------------------------------------
|
|
||||||
// ROWS
|
|
||||||
i1: Input<'d, Gpio4>, //
|
|
||||||
i2: Input<'d, Gpio5>, //
|
|
||||||
i3: Input<'d, Gpio6>, //
|
|
||||||
i4: Input<'d, Gpio7>, //
|
|
||||||
}
|
}
|
||||||
impl<'d> Keypad<'d> {
|
struct Cols<'d> {
|
||||||
|
gpio6: Output<'d, Gpio6>, //
|
||||||
|
gpio7: Output<'d, Gpio7>, //
|
||||||
|
gpio8: Output<'d, Gpio8>, //
|
||||||
|
gpio10: Output<'d, Gpio10>, //
|
||||||
|
}
|
||||||
|
struct Keyboard<'d> {
|
||||||
|
rows: Rows<'d>,
|
||||||
|
cols: Cols<'d>,
|
||||||
|
}
|
||||||
|
impl<'d> Keyboard<'d> {
|
||||||
pub fn pressed(&mut self) -> Option<Button> {
|
pub fn pressed(&mut self) -> Option<Button> {
|
||||||
let res: [[bool; 4]; 4] = core::array::from_fn(|x| {
|
self.cols.gpio6.set_high();
|
||||||
core::array::from_fn(|y| {
|
let col1 = [
|
||||||
self.set_low(x as u8);
|
self.rows.gpio2.is_high(),
|
||||||
let v = self.is_low(y as u8);
|
self.rows.gpio3.is_high(),
|
||||||
self.set_high(x as u8);
|
self.rows.gpio4.is_high(),
|
||||||
v
|
self.rows.gpio5.is_high(),
|
||||||
})
|
];
|
||||||
});
|
self.cols.gpio6.set_high();
|
||||||
|
|
||||||
if res.iter().any(|a| a.iter().any(|b| *b)) {
|
self.cols.gpio7.set_high();
|
||||||
|
let col2 = [
|
||||||
|
self.rows.gpio2.is_high(),
|
||||||
|
self.rows.gpio3.is_high(),
|
||||||
|
self.rows.gpio4.is_high(),
|
||||||
|
self.rows.gpio5.is_high(),
|
||||||
|
];
|
||||||
|
self.cols.gpio7.set_low();
|
||||||
|
|
||||||
|
self.cols.gpio8.set_high();
|
||||||
|
let col3 = [
|
||||||
|
self.rows.gpio2.is_high(),
|
||||||
|
self.rows.gpio3.is_high(),
|
||||||
|
self.rows.gpio4.is_high(),
|
||||||
|
self.rows.gpio5.is_high(),
|
||||||
|
];
|
||||||
|
self.cols.gpio8.set_low();
|
||||||
|
|
||||||
|
self.cols.gpio10.set_high();
|
||||||
|
let col4 = [
|
||||||
|
self.rows.gpio2.is_high(),
|
||||||
|
self.rows.gpio3.is_high(),
|
||||||
|
self.rows.gpio4.is_high(),
|
||||||
|
self.rows.gpio5.is_high(),
|
||||||
|
];
|
||||||
|
self.cols.gpio10.set_low();
|
||||||
println!("***************************************");
|
println!("***************************************");
|
||||||
for a in res {
|
println!("col1 {col1:?}");
|
||||||
println!("col {a:?}");
|
println!("col2 {col2:?}");
|
||||||
}
|
println!("col3 {col3:?}");
|
||||||
}
|
println!("col4 {col4:?}");
|
||||||
let c = res
|
None
|
||||||
.into_iter()
|
|
||||||
.flat_map(|v| v.into_iter())
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_idx, b)| *b)
|
|
||||||
.and_then(|(idx, _)| Button::from_idx(idx));
|
|
||||||
|
|
||||||
if c.is_some() {
|
|
||||||
println!("Button: {c:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
c
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_high(&mut self, o: u8) {
|
|
||||||
match o {
|
|
||||||
0 => self.o1.set_high(),
|
|
||||||
1 => self.o2.set_high(),
|
|
||||||
2 => self.o3.set_high(),
|
|
||||||
3 => self.o4.set_high(),
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fn set_low(&mut self, o: u8) {
|
|
||||||
match o {
|
|
||||||
0 => self.o1.set_low(),
|
|
||||||
1 => self.o2.set_low(),
|
|
||||||
2 => self.o3.set_low(),
|
|
||||||
3 => self.o4.set_low(),
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fn is_low(&mut self, o: u8) -> bool {
|
|
||||||
match o {
|
|
||||||
0 => self.i1.is_low(),
|
|
||||||
1 => self.i2.is_low(),
|
|
||||||
2 => self.i3.is_low(),
|
|
||||||
3 => self.i4.is_low(),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
struct Keyboard<'d>(
|
||||||
|
[(
|
||||||
|
core::cell::RefCell<dyn InputPin + 'd>,
|
||||||
|
core::cell::RefCell<dyn OutputPin + 'd>,
|
||||||
|
); 16],
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'d> Keyboard<'d> {
|
||||||
|
pub fn new(cols: [impl InputPin; 4], rows: [impl OutputPin; 4]) {
|
||||||
|
core::array::from_fn(|x| )
|
||||||
|
for (x, c) in cols.into_iter().enumerate() {
|
||||||
|
for (y, r) in rows.into_iter().enumerate() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trait KeyboardInput<'d> {
|
||||||
|
fn is_low(&mut self) -> bool;
|
||||||
|
fn is_high(&mut self) -> bool {
|
||||||
|
!self.is_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, C: InputPin, R: OutputPin> KeyboardInput<'d> for (Input<'d, C>, Output<'d, R>) {
|
||||||
|
fn is_low(&mut self) -> bool {
|
||||||
|
self.1.set_low();
|
||||||
|
let s = self.0.is_low();
|
||||||
|
self.1.set_high();
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
Loading…
Reference in New Issue
Block a user