2024-12-15 20:49:22 +00:00
|
|
|
#![no_std]
|
|
|
|
|
#![no_main]
|
|
|
|
|
|
2024-12-20 16:47:22 +00:00
|
|
|
use core::cell::RefCell;
|
2024-12-28 20:14:14 +00:00
|
|
|
use defmt::{error, info};
|
2024-12-15 21:40:00 +00:00
|
|
|
use display_interface_spi::SPIInterface;
|
2024-12-20 16:47:22 +00:00
|
|
|
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice;
|
2024-12-15 20:49:22 +00:00
|
|
|
use embassy_executor::Spawner;
|
2024-12-20 16:47:22 +00:00
|
|
|
use embassy_nrf::{
|
|
|
|
|
bind_interrupts,
|
2024-12-28 20:14:14 +00:00
|
|
|
gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin},
|
2024-12-21 10:10:20 +00:00
|
|
|
peripherals, spim,
|
2024-12-20 16:47:22 +00:00
|
|
|
};
|
2024-12-28 20:14:14 +00:00
|
|
|
use embassy_sync::blocking_mutex::{raw::ThreadModeRawMutex, Mutex};
|
|
|
|
|
use embassy_sync::channel::{Channel, Sender};
|
2024-12-21 10:10:20 +00:00
|
|
|
use embassy_time::{Duration, Timer};
|
2024-12-15 21:40:00 +00:00
|
|
|
use ili9341::Ili9341;
|
2024-12-15 20:49:22 +00:00
|
|
|
use {defmt_rtt as _, panic_probe as _};
|
|
|
|
|
|
2024-12-15 21:40:00 +00:00
|
|
|
use embedded_graphics::{
|
2024-12-21 09:40:44 +00:00
|
|
|
mono_font::{ascii::FONT_10X20, MonoTextStyle},
|
2024-12-21 10:10:20 +00:00
|
|
|
pixelcolor::Rgb565,
|
2024-12-20 16:47:22 +00:00
|
|
|
prelude::*,
|
|
|
|
|
primitives::{
|
2024-12-15 21:40:00 +00:00
|
|
|
Circle, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment, Triangle,
|
2024-12-20 16:47:22 +00:00
|
|
|
},
|
|
|
|
|
text::{Alignment, Text},
|
2024-12-15 21:40:00 +00:00
|
|
|
};
|
2024-12-28 18:05:58 +00:00
|
|
|
use gui::*;
|
2024-12-15 21:40:00 +00:00
|
|
|
|
|
|
|
|
// Setup interrupts
|
|
|
|
|
bind_interrupts!(struct Irqs {
|
|
|
|
|
// Setup interrupts for SPI (SERIAL3)
|
|
|
|
|
UARTE3_SPIM3_SPIS3_TWIM3_TWIS3 => embassy_nrf::spim::InterruptHandler<peripherals::SERIAL3>;
|
|
|
|
|
});
|
|
|
|
|
|
2024-12-21 10:16:31 +00:00
|
|
|
/// Embassy task to blink a LED
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// pin - Any valid pin is accepted
|
|
|
|
|
/// blink_delay - a `Duration` with the amount of time
|
|
|
|
|
/// to wait between LED toggles.
|
2024-12-21 09:30:14 +00:00
|
|
|
#[embassy_executor::task]
|
|
|
|
|
async fn blink(pin: AnyPin, blink_delay: Duration) -> ! {
|
|
|
|
|
let mut led = Output::new(pin, Level::Low, OutputDrive::Standard);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
led.set_high();
|
|
|
|
|
Timer::after(blink_delay).await;
|
|
|
|
|
led.set_low();
|
|
|
|
|
Timer::after(blink_delay).await;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-28 20:14:14 +00:00
|
|
|
static GUI_CHANNEL: Channel<ThreadModeRawMutex, gui::GuiAction, 8> = Channel::new();
|
|
|
|
|
|
2024-12-28 18:36:20 +00:00
|
|
|
#[embassy_executor::task]
|
|
|
|
|
async fn gui_task(
|
|
|
|
|
peripheral: embassy_nrf::peripherals::SERIAL3,
|
|
|
|
|
sck: AnyPin,
|
|
|
|
|
miso: AnyPin,
|
|
|
|
|
mosi: AnyPin,
|
|
|
|
|
display_reset_pin: AnyPin,
|
|
|
|
|
cs_pin: AnyPin,
|
|
|
|
|
dc_pin: AnyPin,
|
|
|
|
|
) -> ! {
|
2024-12-21 10:09:21 +00:00
|
|
|
///////////////
|
|
|
|
|
// Setup SPI //
|
|
|
|
|
///////////////
|
|
|
|
|
|
|
|
|
|
// Create an SPI config with the SPI frequency at 8MHz
|
|
|
|
|
let mut spim_config = spim::Config::default();
|
|
|
|
|
spim_config.frequency = spim::Frequency::M8;
|
|
|
|
|
|
|
|
|
|
// Grab the SPI Master peripheral form the nRF60
|
2024-12-28 18:36:20 +00:00
|
|
|
let spim = embassy_nrf::spim::Spim::new(peripheral, Irqs, sck, miso, mosi, spim_config);
|
2024-12-21 10:09:21 +00:00
|
|
|
// Use the SPI peripheral to create a shared SPI bus
|
|
|
|
|
let spi_bus: Mutex<embassy_sync::blocking_mutex::raw::NoopRawMutex, _> =
|
|
|
|
|
Mutex::new(RefCell::new(spim));
|
|
|
|
|
|
2024-12-28 18:36:20 +00:00
|
|
|
////////////////
|
|
|
|
|
// Setup pins //
|
|
|
|
|
////////////////
|
|
|
|
|
|
|
|
|
|
// Initialize the CS, DC, and display reset pins as outputs
|
|
|
|
|
let chip_select = Output::new(cs_pin, Level::High, OutputDrive::Standard);
|
|
|
|
|
let direction_control = Output::new(dc_pin, Level::High, OutputDrive::Standard);
|
|
|
|
|
let display_reset = Output::new(display_reset_pin, Level::High, OutputDrive::Standard);
|
2024-12-21 10:09:21 +00:00
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
// Create the display //
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
2024-12-28 18:36:20 +00:00
|
|
|
// Use the shared bus to create a SPI Device to use for the display
|
|
|
|
|
let spi_display = SpiDevice::new(&spi_bus, chip_select);
|
|
|
|
|
// Create a new SPI display interface
|
|
|
|
|
let iface = SPIInterface::new(spi_display, direction_control);
|
|
|
|
|
|
2024-12-21 10:09:21 +00:00
|
|
|
// Some options for the display
|
2024-12-28 18:05:58 +00:00
|
|
|
let display_orientation = ili9341::Orientation::PortraitFlipped;
|
2024-12-21 10:09:21 +00:00
|
|
|
let display_size = ili9341::DisplaySize240x320;
|
2024-12-15 20:49:22 +00:00
|
|
|
|
2024-12-21 10:09:21 +00:00
|
|
|
// Create the display
|
2024-12-28 18:36:20 +00:00
|
|
|
let display = Ili9341::new(
|
2024-12-21 10:09:21 +00:00
|
|
|
iface,
|
|
|
|
|
display_reset,
|
|
|
|
|
&mut embassy_time::Delay,
|
|
|
|
|
display_orientation,
|
|
|
|
|
display_size,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2024-12-28 18:05:58 +00:00
|
|
|
let mut gui = Gui::new(display);
|
2024-12-28 20:14:14 +00:00
|
|
|
gui.draw().expect("Failed to draw GUI");
|
2024-12-28 18:36:20 +00:00
|
|
|
|
|
|
|
|
loop {
|
2024-12-28 20:14:14 +00:00
|
|
|
info!("Waiting for GUI event");
|
|
|
|
|
// Wait to receive a GUI event
|
|
|
|
|
let gui_action = GUI_CHANNEL.receive().await;
|
|
|
|
|
// Re-render the GUI
|
|
|
|
|
info!("Received event, calculating new screen");
|
|
|
|
|
gui = gui.action(gui_action);
|
|
|
|
|
|
|
|
|
|
// Clear the screen
|
|
|
|
|
info!("Clearing screen");
|
|
|
|
|
gui.fill_black().expect("Failed to clear screen");
|
|
|
|
|
// Draw the GUI
|
|
|
|
|
info!("Drawing screen");
|
2024-12-28 18:36:20 +00:00
|
|
|
gui.draw().expect("Failed to draw GUI");
|
2024-12-28 20:14:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[embassy_executor::task(pool_size = 2)]
|
|
|
|
|
async fn input_controller(
|
|
|
|
|
control: Sender<'static, ThreadModeRawMutex, gui::GuiAction, 8>,
|
|
|
|
|
pin: AnyPin,
|
|
|
|
|
action: gui::GuiAction,
|
|
|
|
|
) -> ! {
|
|
|
|
|
let mut button = Input::new(pin, embassy_nrf::gpio::Pull::Up);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
// Wait for the button to be pressed
|
|
|
|
|
button.wait_for_falling_edge().await;
|
|
|
|
|
// Send the event
|
|
|
|
|
if let Err(_e) = control.try_send(action) {
|
|
|
|
|
error!("Failed to send input action with error");
|
|
|
|
|
}
|
2024-12-28 18:36:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[embassy_executor::main]
|
|
|
|
|
async fn main(spawner: Spawner) {
|
|
|
|
|
// Initialize the nRF
|
|
|
|
|
let p = embassy_nrf::init(Default::default());
|
|
|
|
|
|
|
|
|
|
// Spawn the blink task
|
|
|
|
|
spawner
|
|
|
|
|
.spawn(
|
|
|
|
|
// Here we give the task to spawn, `.degrade()` turns the specific
|
|
|
|
|
// pin on the nRF into the `anyPin` type used above.
|
|
|
|
|
// It is basically a conversion between hardware pin and generic pin.
|
|
|
|
|
blink(p.P0_02.degrade(), Duration::from_millis(250)),
|
|
|
|
|
)
|
|
|
|
|
.expect("Failed to start blink task");
|
|
|
|
|
|
|
|
|
|
let mosi = p.P0_13;
|
|
|
|
|
let miso = p.P0_12;
|
|
|
|
|
let sck = p.P0_30;
|
|
|
|
|
let cs_pin = p.P0_31;
|
|
|
|
|
let dc_pin = p.P0_20; // Data / Command pin for the display
|
|
|
|
|
let disp_rst_pin = p.P0_11;
|
|
|
|
|
|
|
|
|
|
// Create the GUI
|
|
|
|
|
spawner
|
|
|
|
|
.spawn(gui_task(
|
|
|
|
|
p.SERIAL3,
|
|
|
|
|
sck.degrade(),
|
|
|
|
|
miso.degrade(),
|
|
|
|
|
mosi.degrade(),
|
|
|
|
|
disp_rst_pin.degrade(),
|
|
|
|
|
cs_pin.degrade(),
|
|
|
|
|
dc_pin.degrade(),
|
|
|
|
|
))
|
|
|
|
|
.expect("Failed to spawn GUI task");
|
2024-12-28 18:05:58 +00:00
|
|
|
|
2024-12-28 20:14:14 +00:00
|
|
|
// Create the GUI controllers
|
|
|
|
|
spawner
|
|
|
|
|
.spawn(input_controller(
|
|
|
|
|
GUI_CHANNEL.sender(),
|
|
|
|
|
p.P0_06.degrade(),
|
|
|
|
|
gui::GuiAction::Down,
|
|
|
|
|
))
|
|
|
|
|
.expect("Failed tp spawn DOWN input controller");
|
|
|
|
|
spawner
|
|
|
|
|
.spawn(input_controller(
|
|
|
|
|
GUI_CHANNEL.sender(),
|
|
|
|
|
p.P0_07.degrade(),
|
|
|
|
|
gui::GuiAction::Select,
|
|
|
|
|
))
|
|
|
|
|
.expect("Failed tp spawn Select input controller");
|
|
|
|
|
|
2024-12-21 10:09:21 +00:00
|
|
|
// Now loop, re-running the demo every second
|
2024-12-15 20:49:22 +00:00
|
|
|
loop {
|
2024-12-28 18:36:20 +00:00
|
|
|
info!("Hello, world!");
|
2024-12-21 10:09:21 +00:00
|
|
|
Timer::after_secs(1).await;
|
2024-12-15 20:49:22 +00:00
|
|
|
}
|
2024-12-20 16:47:22 +00:00
|
|
|
}
|