feat: working basic impl

This commit is contained in:
Yandrik 2024-09-09 16:59:40 +02:00
commit 04e1b1f9ae
9 changed files with 1764 additions and 0 deletions

16
.cargo/config.toml Normal file
View File

@ -0,0 +1,16 @@
[env]
DEFMT_LOG="info"
[build]
target = "xtensa-esp32-none-elf"
rustflags = [
# "-C", "link-arg=-Tdefmt.x",
"-C", "link-arg=-Tlinkall.x",
"-C", "link-arg=-nostartfiles",
]
[target.xtensa-esp32-none-elf]
runner = "espflash flash --monitor"
[unstable]
build-std = ["core"]

17
.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

1428
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

38
Cargo.toml Normal file
View File

@ -0,0 +1,38 @@
[package]
name = "kolibri-cyd-tester-app-embassy"
version = "0.1.0"
edition = "2021"
rust-version = "1.76.0"
[[bin]]
name = "firmware"
path = "src/bin/firmware.rs"
[lib]
[dependencies]
display-interface = "0.5.0"
display-interface-spi = "0.5.0"
embassy-embedded-hal = "0.2.0"
# defmt = "0.3.8"
# defmt-rtt = "0.4.1"
embassy-executor = { version = "0.6.0", features = ["log"] }
embassy-sync = "0.6.0"
embassy-time = { version = "0.3.1", features = ["generic-queue-8"] }
embedded-graphics = "0.8.1"
embedded-hal = "1.0.0"
esp-backtrace = { version = "0.14.1", features = ["esp32", "println", "exception-handler", "panic-handler"] }
esp-hal = { version = "0.20.1", features = ["esp32", "log", "async"] }
esp-hal-embassy = { version = "0.3.0", features = ["esp32", "log"] }
esp-println = { version = "0.11.0", features = ["esp32", "log"] }
heapless = { version = "0.8.0", features = ["serde"] }
kolibri-embedded-gui = { git = "https://github.com/Yandrik/kolibri.git", version = "0.0.0-alpha.1" }
mipidsi = "0.8.0"
static_cell = { version = "2.1.0", features = ["nightly"] }
xpt2046 = { git = "https://github.com/Yandrik/xpt2046.git", version = "0.3.1" }
[profile.release]
debug = true # Debug info is useful, and does not affect the size of the final binary
codegen-units = 1 # LLVM can perform better optimizations using a single thread
lto = "fat" # Attempt to perform optimizations across all crates within the dependency graph
opt-level = "s" # Optimize for binary size, but keep loop vectorization enabled

4
rust-toolchain.toml Normal file
View File

@ -0,0 +1,4 @@
[toolchain]
channel = "esp"
components = ["rust-src"]
targets = ["xtensa-esp32-none-elf"]

16
rustfmt.toml Normal file
View File

@ -0,0 +1,16 @@
# Edition
edition = "2021"
# Comments
format_code_in_doc_comments = true
normalize_comments = true
wrap_comments = true
# Imports
group_imports = "StdExternalCrate"
imports_granularity = "Crate"
imports_layout = "HorizontalVertical"
# Miscellaneous
enum_discrim_align_threshold = 25
hex_literal_case = "Upper"

204
src/bin/firmware.rs Normal file
View File

@ -0,0 +1,204 @@
#![no_std]
#![no_main]
use core::cell::RefCell;
use display_interface_spi::SPIInterface;
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice;
use embassy_executor::Spawner;
use embassy_sync::{
blocking_mutex::{raw::NoopRawMutex, NoopMutex},
signal::Signal,
};
use embassy_time::{Duration, Timer};
use embedded_graphics::{mono_font::ascii, prelude::Point};
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::RgbColor;
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
gpio,
gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN},
peripherals::{Peripherals, SPI2, SPI3},
prelude::*,
rtc_cntl::Rtc,
spi::{master::Spi, FullDuplexMode, SpiMode},
system::SystemControl,
timer::{timg::TimerGroup, OneShotTimer},
};
use esp_println::println;
use kolibri_cyd_tester_app_embassy::Debouncer;
use kolibri_embedded_gui::{button::Button, label::Label, style::medsize_rgb565_style, ui::Ui};
use kolibri_embedded_gui::ui::Interaction;
use mipidsi::{
models::{ILI9486Rgb565, ILI9486Rgb666},
options::{ColorInversion, ColorOrder, Orientation, Rotation},
Builder,
};
use static_cell::{make_static, StaticCell};
use xpt2046::Xpt2046;
#[embassy_executor::task]
async fn touch_task(
touch_irq: GpioPin<36>,
spi: &'static mut NoopMutex<RefCell<Spi<'static, SPI3, FullDuplexMode>>>,
touch_cs: GpioPin<33>,
touch_signal: &'static Signal<NoopRawMutex, Option<Point>>,
) -> ! {
let mut touch_driver = Xpt2046::new(
SpiDevice::new(spi, Output::new(touch_cs, Level::Low)),
Input::new(touch_irq, Pull::Up),
xpt2046::Orientation::LandscapeFlipped,
);
touch_driver.set_num_samples(32);
touch_driver.init(&mut embassy_time::Delay).unwrap();
let mut debounce = Debouncer::new();
println!("touch task");
loop {
touch_driver.run().expect("Running Touch driver failed");
if touch_driver.is_touched() {
let point = touch_driver.get_touch_point();
touch_signal.signal(Some(Point::new(point.x, 240-point.y)));
} else {
touch_signal.signal(None);
}
Timer::after(Duration::from_millis(1)).await; // 100 a second
// Your touch handling logic here
}
}
#[main]
async fn main(spawner: Spawner) {
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze();
// Enable the RWDT watchdog timer:
let mut rtc = Rtc::new(peripherals.LPWR);
rtc.rwdt.set_timeout(2.secs());
rtc.rwdt.enable();
println!("RWDT watchdog enabled!");
// Initialize the SYSTIMER peripheral, and then Embassy:
let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks);
esp_hal_embassy::init(&clocks, timg0.timer0);
println!("Embassy initialized!");
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
// let mut led = Output::new(io.pins.gpio5, Level::High);
// Initialize SPI
let sclk = io.pins.gpio14;
let miso = io.pins.gpio12;
let mosi = io.pins.gpio13;
let cs = io.pins.gpio15;
let dc = io.pins.gpio2;
let mut backlight = Output::new(io.pins.gpio21, Level::Low);
// Note: RST is not initialized as it's set to -1 in the instructions
// up to 80MHz is possible (even tho the display driver isn't supposed to be
// that fast) Dataseheet sais 10MHz, so we're gonna go with that
let mut spi = Spi::new(peripherals.SPI2, 10.MHz(), SpiMode::Mode0, &mut clocks).with_pins(
Some(sclk),
Some(mosi),
Some(miso),
NO_PIN,
);
static DISP_SPI_BUS: StaticCell<NoopMutex<RefCell<Spi<SPI2, FullDuplexMode>>>> =
StaticCell::new();
let spi_bus = NoopMutex::new(RefCell::new(spi));
let spi_bus = DISP_SPI_BUS.init(spi_bus);
let di = SPIInterface::new(
SpiDevice::new(spi_bus, Output::new(cs, Level::Low)),
Output::new(dc, Level::Low),
);
let mut display = Builder::new(ILI9486Rgb565, di)
.orientation(Orientation {
rotation: Rotation::Deg90,
mirrored: true,
})
.color_order(ColorOrder::Bgr)
.invert_colors(ColorInversion::Inverted)
.init(&mut embassy_time::Delay)
.unwrap();
{
let mut ui = Ui::new_fullscreen(&mut display, medsize_rgb565_style());
ui.clear_background().ok();
}
backlight.set_high();
// init touchscreen pins
let touch_irq = io.pins.gpio36;
let touch_mosi = io.pins.gpio32;
let touch_miso = io.pins.gpio39;
let touch_clk = io.pins.gpio25;
let touch_cs = io.pins.gpio33;
let mut touch_spi = Spi::new(peripherals.SPI3, 10.MHz(), SpiMode::Mode0, &mut clocks)
.with_pins(Some(touch_clk), Some(touch_mosi), Some(touch_miso), NO_PIN);
static TOUCH_SPI_BUS: StaticCell<NoopMutex<RefCell<Spi<SPI3, FullDuplexMode>>>> =
StaticCell::new();
let touch_spi_bus = NoopMutex::new(RefCell::new(touch_spi));
let touch_spi_bus = TOUCH_SPI_BUS.init(touch_spi_bus);
let touch_signal = Signal::new();
static TOUCH_SIGNAL: StaticCell<Signal<NoopRawMutex, Option<Point>>> = StaticCell::new();
let touch_signal = &*TOUCH_SIGNAL.init(touch_signal);
spawner
.spawn(touch_task(
touch_irq,
touch_spi_bus,
touch_cs,
touch_signal,
))
.unwrap();
// init RGB LED pins
let mut red_led = Output::new(io.pins.gpio4, Level::High);
let mut green_led = Output::new(io.pins.gpio16, Level::High);
let mut blue_led = Output::new(io.pins.gpio17, Level::High);
// TODO: Spawn some tasks
let _ = spawner;
let mut last_touch = None;
static BUF_CELL: StaticCell<[Rgb565; 100*100]> = StaticCell::new();
let buf = BUF_CELL.init([Rgb565::BLACK; 100 * 100]);
// Periodically feed the RWDT watchdog timer when our tasks are not running:
loop {
let mut ui = Ui::new_fullscreen(&mut display, medsize_rgb565_style());
ui.set_buffer(buf);
if let Some(touch) = touch_signal.try_take() {
let interact = match (touch, last_touch) {
(Some(point), Some(_)) => Interaction::Drag(point),
(Some(point), None) => Interaction::Click(point),
(None, Some(point)) => Interaction::Release(point),
(None, None) => Interaction::None,
};
ui.interact(interact);
println!("{:?}, {:?}, {:?}", last_touch, touch, interact);
last_touch = touch;
}
ui.sub_ui(|ui| {
ui.style_mut().default_font = ascii::FONT_9X18_BOLD;
ui.add(Label::new("Kolibri Tester"));
Ok(())
})
.ok();
ui.add_horizontal(Button::new("Works!"));
ui.add(Button::new("And pretty nicely!"));
rtc.rwdt.feed();
Timer::after(Duration::from_millis(17)).await; // 60 a second
}
}

37
src/debouncer.rs Normal file
View File

@ -0,0 +1,37 @@
/// Basic debouncer that checks 16 samples, and only if all of them are on / off, it will return
/// the value.
#[derive(Debug)]
pub struct Debouncer(u16, bool);
impl Debouncer {
pub fn new() -> Self {
Self(0, false)
}
pub fn read(&self) -> bool {
self.1
}
/// Completely fill the debouncer, settng it "true".
pub fn fill(&mut self) {
self.0 = u16::MAX;
self.1 = true;
}
/// Completely empty the debouncer, settng it "false".
pub fn empty(&mut self) {
self.0 = 0;
self.1 = false;
}
pub fn update(&mut self, val: bool) -> bool {
self.0 = (self.0 << 1) | (val as u16);
if self.0 == u16::MAX {
self.1 = true;
} else if self.0 == 0 {
self.1 = false;
}
self.1
}
}

4
src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
#![no_std]
mod debouncer;
pub use debouncer::Debouncer;