//! This example toggles the GPIO0 and GPIO1 pins, with each controlled from a
//! separate PIO state machine.
//!
//! Despite running in separate state machines, the clocks are synchronized at
//! the rise and fall times will be simultaneous.
#![no_std]
#![no_main]

use hal::gpio::{FunctionPio0, Pin};
use hal::pac;
use hal::pio::PIOExt;
use hal::Sio;
use panic_halt as _;
use rp2040_hal as hal;

/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
/// Note: This boot block is not necessary when using a rp-hal based BSP
/// as the BSPs already perform this step.
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;

/// Entry point to our bare-metal application.
///
/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables and the spinlock are initialised.
#[rp2040_hal::entry]
fn main() -> ! {
    let mut pac = pac::Peripherals::take().unwrap();

    let sio = Sio::new(pac.SIO);
    let pins = hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    // configure pins for Pio0.
    let gp0: Pin<_, FunctionPio0, _> = pins.gpio0.into_function();
    let gp1: Pin<_, FunctionPio0, _> = pins.gpio1.into_function();

    // PIN id for use inside of PIO
    let pin0 = gp0.id().num;
    let pin1 = gp1.id().num;

    // Define some simple PIO program.
    let program = pio_proc::pio_asm!(
        "
.wrap_target
    set pins, 1 [31]
    set pins, 0 [31]
.wrap
        "
    );

    // Initialize and start PIO
    let (mut pio, sm0, sm1, _, _) = pac.PIO0.split(&mut pac.RESETS);
    // I'm "measuring" the phase offset between the two pins by connecting
    // then through a LED. If there is a clock offset, there will be a
    // short time with a voltage between the pins, so the LED will flash up.
    // With a slow clock this is not visible, so use a reasonably fast clock.
    let (int, frac) = (256, 0);

    let installed = pio.install(&program.program).unwrap();
    let (mut sm0, _, _) = rp2040_hal::pio::PIOBuilder::from_installed_program(
        // Safety: We won't uninstall the program, ever
        unsafe { installed.share() },
    )
    .set_pins(pin0, 1)
    .clock_divisor_fixed_point(int, frac)
    .build(sm0);
    // The GPIO pin needs to be configured as an output.
    sm0.set_pindirs([(pin0, hal::pio::PinDir::Output)]);

    let (mut sm1, _, _) = rp2040_hal::pio::PIOBuilder::from_installed_program(installed)
        .set_pins(pin1, 1)
        .clock_divisor_fixed_point(int, frac)
        .build(sm1);
    // The GPIO pin needs to be configured as an output.
    sm1.set_pindirs([(pin1, hal::pio::PinDir::Output)]);

    // Start both SMs at the same time
    let group = sm0.with(sm1).sync().start();
    cortex_m::asm::delay(10_000_000);

    // Stop both SMs at the same time
    let group = group.stop();
    cortex_m::asm::delay(10_000_000);

    // Start them again and extract the individual state machines
    let (sm1, sm2) = group.start().free();
    cortex_m::asm::delay(10_000_000);

    // Stop the two state machines separately
    let _sm1 = sm1.stop();
    cortex_m::asm::delay(10_000_000);
    let _sm2 = sm2.stop();

    loop {
        cortex_m::asm::wfi();
    }
}
