//! examples/rtt-pwm-dma.rs
//! cargo run --examples rtt-pwm-dma

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

use cortex_m::{asm, peripheral::DWT};
use panic_halt as _;
use rtt_target::{rprint, rprintln, rtt_init_print};
use stm32f4xx_hal::{bb, dma, gpio::Speed, prelude::*, pwm, stm32};

#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true)]
const APP: () = {
    #[init]
    fn init(mut cx: init::Context) {
        rtt_init_print!();
        rprintln!("init");
        let dp = cx.device;

        // Initialize (enable) the monotonic timer (CYCCNT)
        cx.core.DCB.enable_trace();
        cx.core.DWT.enable_cycle_counter();

        let rcc = dp.RCC.constrain();
        // Set up the system clock. 48 MHz?
        let clocks = rcc
            .cfgr
            // .use_hse(8.mhz())
            .sysclk(48.mhz())
            .pclk1(24.mhz())
            .freeze();

        let gpioa = dp.GPIOA.split();
        // we set the pins to VeryHigh to get the sharpest waveform possible
        // (rise and fall times should have similar characteristics)
        let channels = (
            gpioa.pa8.into_alternate_af1().set_speed(Speed::VeryHigh),
            gpioa.pa9.into_alternate_af1().set_speed(Speed::VeryHigh),
        );

        // Setup PWM RAW
        let tim1 = dp.TIM1;
        // Here we need unsafe as we are "stealing" the RCC peripheral
        // At this point it has been contrained into SysConf and used to set clocks
        let rcc = unsafe { &(*stm32::RCC::ptr()) };

        rcc.apb2enr.modify(|_, w| w.tim1en().set_bit());
        rcc.apb2rstr.modify(|_, w| w.tim1rst().set_bit());
        rcc.apb2rstr.modify(|_, w| w.tim1rst().clear_bit());

        // Setup chanel 1 and 2 as pwm_mode1
        tim1.ccmr1_output()
            .modify(|_, w| w.oc1pe().set_bit().oc1m().pwm_mode1());

        tim1.ccmr1_output()
            .modify(|_, w| w.oc2pe().set_bit().oc2m().pwm_mode1());

        // The reference manual is a bit ambiguous about when enabling this bit is really
        // necessary, but since we MUST enable the preload for the output channels then we
        // might as well enable for the auto-reload too
        tim1.cr1.modify(|_, w| w.arpe().set_bit());

        let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
        // check that its actually 48_000_000
        rprintln!("clk {}", clk);

        // we want maximum performance, thus we set the prescaler to 0
        let pre = 0;
        rprintln!("pre {}", pre);
        tim1.psc.write(|w| w.psc().bits(pre));

        // we want 8 bits of resolution
        // so our ARR = 2^8 - 1 = 256 - 1 = 255
        let arr = 255;
        rprintln!("arr {}", arr);
        tim1.arr.write(|w| unsafe { w.bits(arr) });

        //  Trigger update event to load the registers
        tim1.cr1.modify(|_, w| w.urs().set_bit());
        tim1.egr.write(|w| w.ug().set_bit());
        tim1.cr1.modify(|_, w| w.urs().clear_bit());

        // Set main output enable of all Output Compare (OC) registers
        tim1.bdtr.modify(|_, w| w.moe().set_bit());

        // Set output enable for channels 1 and 2
        // Channel 1 (bit  0)
        unsafe { bb::set(&tim1.ccer, 0) }
        // Channel 4 (bit  0)
        unsafe { bb::set(&tim1.ccer, 4) }

        // Setup the timer
        tim1.cr1.write(|w| {
            w.cms()
                .bits(0b00) // edge aligned mode
                .dir() // counter used as upcounter
                .clear_bit()
                .opm() // one pulse mode
                .clear_bit()
                .cen() // enable counter
                .set_bit()
        });

        // Setup TIMER2
        let tim2 = dp.TIM2;
        // Here we need unsafe as we are "stealing" the RCC peripheral
        // At this point it has been contrained into SysConf and used to set clocks
        let rcc = unsafe { &(*stm32::RCC::ptr()) };

        rcc.apb1enr.modify(|_, w| w.tim2en().set_bit());
        rcc.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
        rcc.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());

        // auto-reload, preload
        tim1.cr1.modify(|_, w| w.arpe().set_bit());

        let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
        // check that its actually 48_000_000
        rprintln!("clk {}", clk);

        // assume we have a sine at
        let pre = 0;
        rprintln!("pre {}", pre);
        tim2.psc.write(|w| w.psc().bits(pre));

        // we want 8 bits of resolution
        // so our ARR = 2^8 - 1 = 256 - 1 = 255
        let arr = 255;
        rprintln!("arr {}", arr);
        tim1.arr.write(|w| unsafe { w.bits(arr) });

        //  Trigger update event to load the registers
        tim1.cr1.modify(|_, w| w.urs().set_bit());
        tim1.egr.write(|w| w.ug().set_bit());
        tim1.cr1.modify(|_, w| w.urs().clear_bit());

        // Set main output enable of all Output Compare (OC) registers
        tim1.bdtr.modify(|_, w| w.moe().set_bit());

        // Set output enable for channels 1 and 2
        // Channel 1 (bit  0)
        unsafe { bb::set(&tim1.ccer, 0) }
        // Channel 4 (bit  0)
        unsafe { bb::set(&tim1.ccer, 4) }

        // Setup the timer
        tim1.cr1.write(|w| {
            w.cms()
                .bits(0b00) // edge aligned mode
                .dir() // counter used as upcounter
                .clear_bit()
                .opm() // one pulse mode
                .clear_bit()
                .cen() // enable counter
                .set_bit()
        });

        // Set duty cycle of Channels
        tim1.ccr1.write(|w| unsafe { w.ccr().bits(128) });
        tim1.ccr2.write(|w| unsafe { w.ccr().bits(128) });

        // Set preload for the CCx
        tim1.cr2.write(|w| w.ccpc().set_bit());

        tim1.dier.write(|w| w.uie().enabled());
        tim1.sr.modify(|_, w| w.uif().clear());

        while tim1.sr.read().uif().is_clear() {
            rprint!("-");
        }
        rprintln!("here");
        tim1.sr.modify(|_, w| w.uif().clear());

        let mut dma_buff = [0u8; 256];
        for i in 0..256 {
            dma_buff[i] = i as u8;
        }

        loop {
            for i in 0..256 {
                // wait until next update event
                while tim1.sr.read().uif().is_clear() {}
                tim1.sr.modify(|_, w| w.uif().clear());

                tim1.ccr1
                    .write(|w| unsafe { w.ccr().bits(dma_buff[i] as u16) });
                tim1.ccr2
                    .write(|w| unsafe { w.ccr().bits(dma_buff[i] as u16) });
            }
        }
    }

    #[idle]
    fn idle(_cx: idle::Context) -> ! {
        rprintln!("idle");
        loop {
            continue;
        }
    }
};