#![no_std] #![no_main] mod keyboard; mod touchpad; use panic_rtt_target as _; #[link_section = ".boot2"] #[used] pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_GD25Q64CS; #[rtic::app( device = rp2040_hal::pac, dispatchers = [TIMER_IRQ_1, TIMER_IRQ_2] )] mod app { use crate::{keyboard::Keyboard, touchpad::Touchpad}; use core::fmt::Write; use cortex_m::delay::Delay; use display_interface_spi::SPIInterface; use embedded_graphics::{ draw_target::DrawTarget, mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, pixelcolor::{Rgb565, RgbColor}, prelude::{Point, Primitive}, primitives::{Circle, PrimitiveStyle, Triangle}, text::{Baseline, Text}, Drawable, }; use embedded_hal::PwmPin; use fugit::RateExtU32; use hal::{ gpio::{ bank0::{ Gpio0, Gpio1, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio18, Gpio2, Gpio23, Gpio24, Gpio3, Gpio4, Gpio6, Gpio7, Gpio8, }, FunctionI2C, FunctionSio, FunctionSpi, Pin, PullDown, PullNone, PullUp, SioInput, SioOutput, }, pac::{I2C1, SPI1}, pwm::{self, Channel, FreeRunning, Pwm4, Pwm5, Slice}, spi, timer::{monotonic::Monotonic, Timer}, Clock, I2C, }; use heapless::String; use mipidsi::{models::ST7789, Builder}; use rp2040_hal::{self as hal, timer::Alarm0}; use rtic_monotonics::rp2040::*; use rtt_target::rprintln; const XTAL_FREQ_HZ: u32 = 12_000_000u32; type KeyboardBl = Channel<Slice<Pwm4, FreeRunning>, pwm::B>; type MyTouchpad = Touchpad< I2C< I2C1, ( Pin<Gpio18, FunctionI2C, PullUp>, Pin<Gpio23, FunctionI2C, PullUp>, ), >, Pin<Gpio3, FunctionSio<SioOutput>, PullDown>, Pin<Gpio24, FunctionSio<SioOutput>, PullDown>, >; type MyKeyboard = Keyboard< Pin<Gpio1, FunctionSio<SioOutput>, PullDown>, Pin<Gpio0, FunctionSio<SioOutput>, PullDown>, Pin<Gpio2, FunctionSio<SioOutput>, PullDown>, Pin<Gpio8, FunctionSio<SioOutput>, PullDown>, Pin<Gpio6, FunctionSio<SioInput>, PullNone>, Pin<Gpio7, FunctionSio<SioOutput>, PullDown>, >; type Lcd = mipidsi::Display< SPIInterface< spi::Spi< spi::Enabled, SPI1, ( Pin<Gpio15, FunctionSpi, PullDown>, Pin<Gpio12, FunctionSpi, PullDown>, Pin<Gpio14, FunctionSpi, PullDown>, ), >, Pin<Gpio16, FunctionSio<SioOutput>, PullDown>, Pin<Gpio13, FunctionSio<SioOutput>, PullDown>, >, ST7789, Pin<Gpio4, FunctionSio<SioOutput>, PullDown>, >; type DisplayBl = Channel<Slice<Pwm5, FreeRunning>, pwm::A>; #[monotonic(binds = TIMER_IRQ_0, default = true)] type MyMono = Monotonic<Alarm0>; #[shared] struct Shared {} #[local] struct Local { touchpad: MyTouchpad, keyboard: MyKeyboard, keyboard_bl: KeyboardBl, display: Lcd, display_bl: DisplayBl, r: u8, g: u8, b: u8, } #[init] fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { rtt_target::rtt_init_print!(); let mut pac = ctx.device; let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); let clocks = hal::clocks::init_clocks_and_plls( XTAL_FREQ_HZ, pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut watchdog, ) .ok() .unwrap(); let sio = hal::Sio::new(pac.SIO); let pins = hal::gpio::Pins::new( pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS, ); // Init PWMs let pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS); let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); // Set up the USB driver /*let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( pac.USBCTRL_REGS, pac.USBCTRL_DPRAM, clocks.usb_clock, true, &mut pac.RESETS, ));*/ // Set up the USB Communications Class Device driver /*let mut serial = SerialPort::new(&usb_bus); // Create a USB device with a fake VID and PID let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) .manufacturer("Fake company") .product("Serial port") .serial_number("TEST") .device_class(2) // from: https://www.usb.org/defined-class-codes .build(); */ // let mut vibrator = pins.gpio9.into_push_pull_output(); // Display backlight let mut pwm = pwm_slices.pwm5; pwm.set_ph_correct(); pwm.enable(); let mut display_bl = pwm.channel_a; display_bl.output_to(pins.gpio10); display_bl.set_inverted(); let tp_reset = pins.gpio3.into_push_pull_output(); let disp_reset = pins.gpio4.into_push_pull_output(); // Touchpad let sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio18.reconfigure(); let scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio23.reconfigure(); let tp_shutdown = pins.gpio24.into_push_pull_output(); let i2c = I2C::i2c1( pac.I2C1, sda_pin, scl_pin, 400.kHz(), &mut pac.RESETS, &clocks.system_clock, ); let touchpad = Touchpad::new(i2c, tp_reset, tp_shutdown, timer); // Keyboard let col_clk = pins.gpio1.into_push_pull_output(); let col_ser = pins.gpio0.into_push_pull_output(); let col_rclk = pins.gpio2.into_push_pull_output(); let row_clk = pins.gpio8.into_push_pull_output(); let row_ser = pins.gpio6.into_floating_input(); let row_ld = pins.gpio7.into_push_pull_output(); let keyboard = Keyboard::new(timer, col_clk, col_ser, col_rclk, row_clk, row_ser, row_ld); let mut pwm = pwm_slices.pwm4; pwm.set_ph_correct(); pwm.enable(); let mut keyboard_bl = pwm.channel_b; keyboard_bl.output_to(pins.gpio25); keyboard_bl.set_duty(0); // SPI Display let display_cs = pins.gpio13.into_push_pull_output(); let display_dc = pins.gpio16.into_push_pull_output(); let spi_mosi = pins.gpio15.into_function::<hal::gpio::FunctionSpi>(); let spi_miso = pins.gpio12.into_function::<hal::gpio::FunctionSpi>(); let spi_sclk = pins.gpio14.into_function::<hal::gpio::FunctionSpi>(); let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI1, (spi_mosi, spi_miso, spi_sclk)); let spi = spi.init( &mut pac.RESETS, clocks.peripheral_clock.freq(), 8.MHz(), embedded_hal::spi::MODE_0, ); let spi_cs = SPIInterface::new(spi, display_dc, display_cs); let mut delay = Delay::new(ctx.core.SYST, clocks.system_clock.freq().to_Hz()); let display = Builder::st7789(spi_cs) .with_display_size(320, 240) .init(&mut delay, Some(disp_reset)) .unwrap(); display_update::spawn().unwrap(); keyboard_update::spawn().unwrap(); let mono = Monotonic::new(timer, timer.alarm_0().unwrap()); ( Shared {}, Local { keyboard_bl, touchpad, keyboard, display, display_bl, r: 0, g: 0, b: 0, }, init::Monotonics(mono), ) } #[task(priority = 2, local = [display_bl, keyboard_bl, touchpad, keyboard])] fn keyboard_update(ctx: keyboard_update::Context) { //while usb_dev.poll(&mut [&mut serial]) {} let keyboard_update::LocalResources { display_bl, keyboard_bl, touchpad, keyboard, } = ctx.local; let max_duty = keyboard_bl.get_max_duty(); let mut cur_duty = display_bl.get_duty(); let mut buf: String<64> = String::new(); let state = keyboard.read_state(); if state > 0 { write!(&mut buf, "KB: {state:x}\r\n").unwrap(); //serial.write(&buf.as_bytes()); keyboard_bl.set_duty(max_duty); } else { keyboard_bl.set_duty(0); } let (x, y) = touchpad.get_delta_motion().unwrap(); match y { x if x > 0 => { cur_duty = cur_duty.saturating_sub(250); } x if x < 0 => { cur_duty = cur_duty.saturating_add(250); } _ => {} } display_bl.set_duty(cur_duty.min(display_bl.get_max_duty())); keyboard_update::spawn_after(5.millis()).unwrap(); } #[task(local = [display, r, g, b])] fn display_update(ctx: display_update::Context) { let display_update::LocalResources { display, r, g, b } = ctx.local; *r += 1; *g += 3; *b += 5; *r %= 255; *g %= 255; *b %= 255; display.clear(Rgb565::new(*r, *g, *b)).unwrap(); // Draw a smiley face with white eyes and a red mouth draw_smiley(display).unwrap(); // text! let text_style = MonoTextStyleBuilder::new() .font(&FONT_10X20) .text_color(Rgb565::YELLOW) .build(); Text::with_baseline( "Hello world!", Point::new(16, 128), text_style, Baseline::Top, ) .draw(display) .unwrap(); display_update::spawn_after(100.millis()).unwrap(); } #[idle] fn idle(_: idle::Context) -> ! { loop { cortex_m::asm::wfi(); } } fn draw_smiley<T: DrawTarget<Color = Rgb565>>(display: &mut T) -> Result<(), T::Error> { // Draw the left eye as a circle located at (50, 100), with a diameter of 40, filled with white Circle::new(Point::new(50, 100), 40) .into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE)) .draw(display)?; // Draw the right eye as a circle located at (50, 200), with a diameter of 40, filled with white Circle::new(Point::new(50, 200), 40) .into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE)) .draw(display)?; // Draw an upside down red triangle to represent a smiling mouth Triangle::new( Point::new(130, 140), Point::new(130, 200), Point::new(160, 170), ) .into_styled(PrimitiveStyle::with_fill(Rgb565::RED)) .draw(display)?; // Cover the top part of the mouth with a black triangle so it looks closed instead of open Triangle::new( Point::new(130, 150), Point::new(130, 190), Point::new(150, 170), ) .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK)) .draw(display)?; Ok(()) } }