Skip to content
Snippets Groups Projects
main.rs 11 KiB
Newer Older
#![no_std]
#![no_main]

mod buffered_display;
Stephen D's avatar
Stephen D committed
mod gui;
Stephen D's avatar
Stephen D committed
mod keyboard;
Stephen D's avatar
Stephen D committed
mod touchpad;
Stephen D's avatar
Stephen D committed
mod voltage;
Stephen D's avatar
Stephen D committed

use panic_rtt_target as _;

#[link_section = ".boot2"]
#[used]
pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_GD25Q64CS;
Stephen D's avatar
Stephen D committed

#[rtic::app(
    device = rp2040_hal::pac,
Stephen D's avatar
Stephen D committed
    dispatchers = [TIMER_IRQ_1, TIMER_IRQ_2]
    use crate::{
Stephen D's avatar
Stephen D committed
        buffered_display::BufferedDisplay,
Stephen D's avatar
Stephen D committed
        gui::{Element, Gui},
Stephen D's avatar
Stephen D committed
        keyboard::Keyboard,
        touchpad::Touchpad,
Stephen D's avatar
Stephen D committed
        voltage::VoltMeter,
Stephen D's avatar
Stephen D committed
    use cortex_m::{delay::Delay, singleton};
Stephen D's avatar
Stephen D committed
    use display_interface_spi::SPIInterface;
Stephen D's avatar
Stephen D committed
    use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor};
Stephen D's avatar
Stephen D committed
    use embedded_hal::{digital::v2::OutputPin, PwmPin};
    use fugit::{Duration, RateExtU32};
Stephen D's avatar
Stephen D committed
    use hal::{
        gpio::{
Stephen D's avatar
Stephen D committed
            bank0::{
                Gpio0, Gpio1, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio18, Gpio2, Gpio23,
Stephen D's avatar
Stephen D committed
                Gpio24, Gpio26, Gpio3, Gpio4, Gpio6, Gpio7, Gpio8,
Stephen D's avatar
Stephen D committed
            },
Stephen D's avatar
Stephen D committed
            FunctionI2C, FunctionSio, FunctionSpi, Pin, PinState, PullDown, PullNone, PullUp,
            SioInput, SioOutput,
Stephen D's avatar
Stephen D committed
        },
Stephen D's avatar
Stephen D committed
        pac::{I2C1, SPI1},
        pwm::{self, Channel, FreeRunning, Pwm4, Pwm5, Slice},
        timer::{monotonic::Monotonic, Instant, Timer},
Stephen D's avatar
Stephen D committed
        Adc, Clock, I2C,
Stephen D's avatar
Stephen D committed
    };
    use mipidsi::{models::ST7789, Builder, Orientation};
Stephen D's avatar
Stephen D committed
    use rp2040_hal::{self as hal, gpio::FunctionNull, timer::Alarm0};
    use rtic_monotonics::rp2040::*;

    const XTAL_FREQ_HZ: u32 = 12_000_000u32;
    const WIDTH: usize = 320;
    const HEIGHT: usize = 240;
    const BACKLIGHT_DELAY_TIME_SECS: u64 = 5;
    const BACKLIGHT_FADE_TIME_SECS: u64 = 2;
Stephen D's avatar
Stephen D committed
    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<
Stephen D's avatar
Stephen D committed
        Pin<Gpio1, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio0, FunctionSio<SioOutput>, PullDown>,
Stephen D's avatar
Stephen D committed
        Pin<Gpio2, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio8, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio6, FunctionSio<SioInput>, PullNone>,
        Pin<Gpio7, FunctionSio<SioOutput>, PullDown>,
    >;

Stephen D's avatar
Stephen D committed
    type MyBufferedDisplay = BufferedDisplay<
        { WIDTH * HEIGHT },
        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>,
    >;

Stephen D's avatar
Stephen D committed
    type DisplayBl = Channel<Slice<Pwm5, FreeRunning>, pwm::A>;

    #[monotonic(binds = TIMER_IRQ_0, default = true)]
    type MyMono = Monotonic<Alarm0>;

    struct Shared {
Stephen D's avatar
Stephen D committed
        gui: Gui,
Stephen D's avatar
Stephen D committed
    struct Local {
        touchpad: MyTouchpad,
        keyboard: MyKeyboard,
        keyboard_bl: KeyboardBl,
Stephen D's avatar
Stephen D committed
        display: MyBufferedDisplay,
Stephen D's avatar
Stephen D committed
        display_bl: DisplayBl,
        last_key_press: Instant,
Stephen D's avatar
Stephen D committed
    }

    #[init]
    fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
Stephen D's avatar
Stephen D committed
        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
Stephen D's avatar
Stephen D committed
        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();
Stephen D's avatar
Stephen D committed
        let mut delay = Delay::new(ctx.core.SYST, clocks.system_clock.freq().to_Hz());

        // Display backlight
Stephen D's avatar
Stephen D committed
        let mut pwm = pwm_slices.pwm5;
        pwm.set_ph_correct();
        pwm.enable();
Stephen D's avatar
Stephen D committed
        let mut display_bl = pwm.channel_a;
        display_bl.output_to(pins.gpio10);
        display_bl.set_inverted();
        display_bl.set_duty(display_bl.get_max_duty());
Stephen D's avatar
Stephen D committed

        let tp_reset = pins.gpio3.into_push_pull_output();
Stephen D's avatar
Stephen D committed
        let mut disp_reset = pins.gpio4.into_push_pull_output_in_state(PinState::Low);
        delay.delay_us(10);
        disp_reset.set_high().unwrap();
Stephen D's avatar
Stephen D committed

        // 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);
Stephen D's avatar
Stephen D committed
        // Keyboard
Stephen D's avatar
Stephen D committed
        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);
Stephen D's avatar
Stephen D committed

        // SPI Display
Stephen D's avatar
Stephen D committed
        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(),
Stephen D's avatar
Stephen D committed
            60.MHz(),
            embedded_hal::spi::MODE_0,
        );

Stephen D's avatar
Stephen D committed
        let spi_cs = SPIInterface::new(spi, display_dc, display_cs);
Stephen D's avatar
Stephen D committed
        let display = Builder::st7789(spi_cs)
            .with_display_size(HEIGHT.try_into().unwrap(), WIDTH.try_into().unwrap())
            .with_orientation(Orientation::LandscapeInverted(true))
Stephen D's avatar
Stephen D committed
            .init(&mut delay, None)
            .unwrap();
Stephen D's avatar
Stephen D committed
        let mut buf =
            singleton!(: [Rgb565;WIDTH * HEIGHT] = [Rgb565::BLACK; WIDTH * HEIGHT]).unwrap();
        let display = BufferedDisplay::new(display, buf);
Stephen D's avatar
Stephen D committed
        // Set up voltage monitoring
        let adc = Adc::new(pac.ADC, &mut pac.RESETS);
        let volt_meter = VoltMeter::new(
            adc,
            pins.gpio26.into_floating_disabled(),
            pins.gpio28.into_floating_disabled(),
        );

        // Setup app
        let gui = Gui::new(volt_meter);

Stephen D's avatar
Stephen D committed
        display_update::spawn().unwrap();
        keyboard_update::spawn().unwrap();

        let mut mono = Monotonic::new(timer, timer.alarm_0().unwrap());
Stephen D's avatar
Stephen D committed

        (
Stephen D's avatar
Stephen D committed
            Shared { gui },
Stephen D's avatar
Stephen D committed
            Local {
                keyboard_bl,
                touchpad,
                keyboard,
                display,
                display_bl,
                last_key_press: mono.now(),
Stephen D's avatar
Stephen D committed
            },
            init::Monotonics(mono),
        )
    }

    #[task(priority = 2, local = [display_bl, keyboard_bl, touchpad, keyboard, last_key_press], shared = [gui])]
Stephen D's avatar
Stephen D committed
    fn keyboard_update(mut ctx: keyboard_update::Context) {
Stephen D's avatar
Stephen D committed
        //while usb_dev.poll(&mut [&mut serial]) {}

        let keyboard_update::LocalResources {
            display_bl,
            keyboard_bl,
            touchpad,
            keyboard,
            last_key_press,
Stephen D's avatar
Stephen D committed
        } = ctx.local;

        let max_duty = keyboard_bl.get_max_duty();
        let mut cur_duty = display_bl.get_duty();

        let state = keyboard.read_state();
        if state > 0 {
            *last_key_press = monotonics::now();

            if let Some(k) = keyboard.pushed_key() {
Stephen D's avatar
Stephen D committed
                ctx.shared.gui.lock(|g| g.key_push(k));
Stephen D's avatar
Stephen D committed
        }

        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 backlight logic
        let delta = monotonics::now() - *last_key_press;
        if let Some(fade_delta) = delta.checked_sub(BACKLIGHT_DELAY_TIME_SECS.secs::<1, 1>()) {
            let fade_delta = fade_delta.to_millis();
            let fade_time_ms = BACKLIGHT_FADE_TIME_SECS * 1000;
            let frac = u64::from(keyboard_bl.get_max_duty())
                * (fade_time_ms.saturating_sub(fade_delta))
                / fade_time_ms;
            keyboard_bl.set_duty(frac.try_into().unwrap());
        } else {
            keyboard_bl.set_duty(keyboard_bl.get_max_duty());
        }

        // re-enqueue ourselves
Stephen D's avatar
Stephen D committed
        keyboard_update::spawn_after(5.millis()).unwrap();
    }

Stephen D's avatar
Stephen D committed
    #[task(local = [display], shared = [gui])]
    fn display_update(mut ctx: display_update::Context) {
Stephen D's avatar
Stephen D committed
        let display_update::LocalResources { display } = ctx.local;
Stephen D's avatar
Stephen D committed

        ctx.shared.gui.lock(|g| g.render(display).unwrap());
Stephen D's avatar
Stephen D committed
        display.flush().unwrap();
Stephen D's avatar
Stephen D committed
        display_update::spawn_after(1.millis()).unwrap();
Stephen D's avatar
Stephen D committed
    }

    #[idle]
    fn idle(_: idle::Context) -> ! {
        loop {
            cortex_m::asm::wfi();
        }