#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]

use cortex_m::peripheral::DWT;
use cortex_m_semihosting::hprintln;
use panic_halt as _;
use rtfm::cyccnt::{Instant, U32Ext as _};
use stm32f4xx_hal::stm32;

#[rtfm::app(device = stm32f4xx_hal::stm32, monotonic = rtfm::cyccnt::CYCCNT, peripherals = true)]
const APP: () = {
    #[init(schedule = [toggle])]
    fn init(cx: init::Context) {
        let mut core = cx.core;
        let device = cx.device;

        // Initialize (enable) the monotonic timer (CYCCNT)
        core.DCB.enable_trace();
        // required on Cortex-M7 devices that software lock the DWT (e.g. STM32F7)
        DWT::unlock();
        core.DWT.enable_cycle_counter();

        // semantically, the monotonic timer is frozen at time "zero" during `init`
        // NOTE do *not* call `Instant::now` in this context; it will return a nonsense value
        let now = cx.start; // the start time of the system

        // power on GPIOA, RM0368 6.3.11
        device.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit());
        // configure PA5 as output, RM0368 8.4.1
        device.GPIOA.moder.modify(|_, w| w.moder5().bits(1));

        cx.schedule
            .toggle(now + 8_000_000.cycles(), true, device.GPIOA)
            .ok();
    }

    #[task(schedule= [toggle])]
    fn toggle(cx: toggle::Context, toggle: bool, gpioa: stm32::GPIOA) {
        hprintln!("toggle  @ {:?}", Instant::now()).unwrap();

        if toggle {
            gpioa.bsrr.write(|w| w.bs5().set_bit());
        } else {
            gpioa.bsrr.write(|w| w.br5().set_bit());
        }

        cx.schedule
            .toggle(cx.scheduled + 8_000_000.cycles(), !toggle, gpioa)
            .ok();
    }

    extern "C" {
        fn EXTI0();
    }
};