#![no_main]
#![no_std]

mod calibration;
mod eeprom;
mod lookup_table;
mod sensor;
mod table_tn114;
mod thermal;

use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::dma;
use embassy_stm32::mode::Async;
use embassy_stm32::peripherals::{GPDMA1_CH0, GPDMA1_CH1, I2C2};
use embassy_stm32::{bind_interrupts, i2c, rcc, time, Config};

use defmt_rtt as _;
use panic_probe as _;

bind_interrupts!(struct Irqs {
    GPDMA1_CHANNEL0 => dma::InterruptHandler<GPDMA1_CH0>;
    GPDMA1_CHANNEL1 => dma::InterruptHandler<GPDMA1_CH1>;
    I2C2_EV => i2c::EventInterruptHandler<I2C2>;
    I2C2_ER => i2c::ErrorInterruptHandler<I2C2>;
});

/// Shared I2C bus type used by every module that talks to the sensor.
pub type Bus = i2c::I2c<'static, Async, i2c::Master>;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let mut config = Config::default();
    configure_clocks(&mut config);
    let p = embassy_stm32::init(config);

    let mut i2c_cfg = i2c::Config::default();
    i2c_cfg.sda_pullup = false;
    i2c_cfg.scl_pullup = false;
    let mut i2c = i2c::I2c::new(p.I2C2, p.PB13, p.PB14, p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, i2c_cfg);

    let cal = calibration::Calibration::read(&mut i2c).await;
    info!("sensor table number (TN): {}", cal.table_number);
    if cal.table_number != table_tn114::TABLE.table_number {
        warn!(
            "sensor's TableNumber ({}) does not match the embedded calibration table ({}); \
             object temperatures will be inaccurate",
            cal.table_number,
            table_tn114::TABLE.table_number
        );
    }

    sensor::wake_up_and_load_trim(&mut i2c, &cal).await;

    let frame = sensor::Frame::capture(&mut i2c).await;
    let heatmap = thermal::process_frame(&cal, &frame, &table_tn114::TABLE);

    info!("ambient temperature: {} C", heatmap.ambient_c);
    info!("heatmap range: {}..{} C", heatmap.min_c, heatmap.max_c);
    for row in 0..calibration::GRID {
        info!("row {}: {}", row, heatmap.temperature_c[row]);
    }
}

/// Configure the system clock to run at its maximum from the board's 25 MHz
/// HSE: PLL1 = (25 MHz / 5) * 64 / 2 = 160 MHz, used as SYSCLK. Also enables
/// the board's 32.768 kHz LSE for the RTC.
fn configure_clocks(config: &mut Config) {
    config.rcc.hse = Some(rcc::Hse {
        freq: time::Hertz(25_000_000),
        mode: rcc::HseMode::Oscillator,
    });

    config.rcc.pll1 = Some(rcc::Pll {
        source: rcc::PllSource::HSE,
        prediv: rcc::PllPreDiv::DIV5,
        mul: rcc::PllMul::MUL64,
        divp: Some(rcc::PllDiv::DIV2),
        divq: Some(rcc::PllDiv::DIV2),
        divr: Some(rcc::PllDiv::DIV2), // 160 MHz
    });
    config.rcc.sys = rcc::Sysclk::PLL1_R;

    config.rcc.ls.lse = Some(rcc::LseConfig {
        frequency: time::Hertz(32_768),
        mode: rcc::LseMode::Oscillator(rcc::LseDrive::MediumHigh),
        peripherals_clocked: false,
    });
    config.rcc.ls.rtc = rcc::RtcClockSource::LSE;
}
