#![no_std] #![no_main] use core::cell::RefCell; use defmt::{error, info}; use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; use embassy_nrf::{ bind_interrupts, gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin}, peripherals, spim, }; use embassy_sync::blocking_mutex::{raw::ThreadModeRawMutex, Mutex}; use embassy_sync::channel::{Channel, Sender}; use embassy_time::{Duration, Timer}; use ili9341::Ili9341; use {defmt_rtt as _, panic_probe as _}; use embedded_graphics::{ mono_font::{ascii::FONT_10X20, MonoTextStyle}, pixelcolor::Rgb565, prelude::*, primitives::{ Circle, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment, Triangle, }, text::{Alignment, Text}, }; use gui::*; // Setup interrupts bind_interrupts!(struct Irqs { // Setup interrupts for SPI (SERIAL3) UARTE3_SPIM3_SPIS3_TWIM3_TWIS3 => embassy_nrf::spim::InterruptHandler; }); /// 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. #[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; } } static GUI_CHANNEL: Channel = Channel::new(); #[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, ) -> ! { /////////////// // 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 let spim = embassy_nrf::spim::Spim::new(peripheral, Irqs, sck, miso, mosi, spim_config); // Use the SPI peripheral to create a shared SPI bus let spi_bus: Mutex = Mutex::new(RefCell::new(spim)); //////////////// // 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); //////////////////////// // Create the display // //////////////////////// // 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); // Some options for the display let display_orientation = ili9341::Orientation::PortraitFlipped; let display_size = ili9341::DisplaySize240x320; // Create the display let display = Ili9341::new( iface, display_reset, &mut embassy_time::Delay, display_orientation, display_size, ) .unwrap(); let mut gui = Gui::new(display); gui.draw().expect("Failed to draw GUI"); loop { 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"); gui.draw().expect("Failed to draw GUI"); } } #[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"); } } } #[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"); // 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"); // Now loop, re-running the demo every second loop { info!("Hello, world!"); Timer::after_secs(1).await; } }