use core::cell::RefCell;
use core::sync::atomic::{Ordering, compiler_fence};
use embassy_nrf::{
Peripherals, bind_interrupts,
gpio::{Input, Level, Output, OutputDrive, Pull},
pac,
pwm::{Prescaler, SimpleConfig, SimplePwm},
saadc::{self, ChannelConfig, Config as SaadcConfig, Saadc},
twim::{self, Config as TwimConfig, Frequency as TwimFrequency, Twim},
};
use embedded_hal::delay::DelayNs;
use embedded_hal::pwm::SetDutyCycle;
use embedded_hal_bus::i2c::RefCellDevice as I2cRefCellDevice;
use static_cell::StaticCell;
#[cfg(feature = "async")]
use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice as I2cAsyncDevice;
#[cfg(feature = "async")]
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex as AsyncMutex};
#[cfg(feature = "power-board")]
use embassy_nrf::spim::{self, Config as SpimConfig, Frequency as SpimFrequency, Spim};
#[cfg(feature = "power-board")]
use embedded_hal_bus::spi::RefCellDevice as SpiRefCellDevice;
#[cfg(feature = "async")]
use crate::Async;
use crate::Uferris;
use crate::boards::nrf_buzzer::{BUZZER_COUNTERTOP, NrfBuzzerChannel};
use crate::components::ldr::OneShot;
#[cfg(feature = "async")]
use crate::components::ldr::OneShotAsync;
bind_interrupts!(
struct Irqs {
SAADC => saadc::InterruptHandler;
SERIAL22 => twim::InterruptHandler<embassy_nrf::peripherals::SERIAL22>;
#[cfg(feature = "power-board")]
SERIAL00 => spim::InterruptHandler<embassy_nrf::peripherals::SERIAL00>;
}
);
const SYS_CLOCK_HZ: u32 = 64_000_000;
const I2C_FREQ: TwimFrequency = TwimFrequency::K100;
#[cfg(feature = "power-board")]
const SPI_FREQ: SpimFrequency = SpimFrequency::M1;
#[cfg(feature = "power-board")]
const SPI_DIVISOR: u8 = 126;
const TWIM_RAM_BUFFER_LEN: usize = 16;
static I2C_BUS: StaticCell<RefCell<Nrf54l15I2c>> = StaticCell::new();
static TWIM_RAM_BUFFER: StaticCell<[u8; TWIM_RAM_BUFFER_LEN]> = StaticCell::new();
#[cfg(feature = "power-board")]
static SPI_BUS: StaticCell<RefCell<Nrf54l15Spi>> = StaticCell::new();
#[cfg(feature = "async")]
static ASYNC_I2C_BUS: StaticCell<AsyncMutex<NoopRawMutex, Nrf54l15I2c>> = StaticCell::new();
type Nrf54l15I2c = Twim<'static>;
type SharedI2c = I2cRefCellDevice<'static, Nrf54l15I2c>;
#[cfg(feature = "async")]
type SharedAsyncI2c = I2cAsyncDevice<'static, NoopRawMutex, Nrf54l15I2c>;
type LedPin = Output<'static>;
type ButtonPin = Input<'static>;
const SAADC_CNT_UNIT: u16 = 2;
pub struct LdrAdc {
_saadc: Saadc<'static, 1>,
}
impl OneShot for LdrAdc {
fn read_raw(&mut self) -> u16 {
let r = pac::SAADC;
let mut result: i16 = 0;
r.result().ptr().write_value(&raw mut result as u32);
r.result().maxcnt().write(|w| w.set_maxcnt(SAADC_CNT_UNIT));
r.events_end().write_value(0);
compiler_fence(Ordering::SeqCst);
r.tasks_start().write_value(1);
r.tasks_sample().write_value(1);
while r.events_end().read() == 0 {}
r.events_end().write_value(0);
compiler_fence(Ordering::SeqCst);
r.events_stopped().write_value(0);
r.tasks_stop().write_value(1);
while r.events_stopped().read() == 0 {}
r.events_stopped().write_value(0);
compiler_fence(Ordering::SeqCst);
result.max(0) as u16
}
}
#[cfg(feature = "async")]
pub struct LdrAdcAsync {
saadc: Saadc<'static, 1>,
}
#[cfg(feature = "async")]
impl OneShotAsync for LdrAdcAsync {
async fn read_raw(&mut self) -> u16 {
let mut result = [0i16; 1];
self.saadc.sample(&mut result).await;
result[0].max(0) as u16
}
}
#[cfg(feature = "power-board")]
type Nrf54l15Spi = Spim<'static>;
#[cfg(feature = "power-board")]
type SdCsPin = Output<'static>;
#[cfg(feature = "power-board")]
type SdBlockDevice =
embedded_sdmmc::SdCard<SpiRefCellDevice<'static, Nrf54l15Spi, SdCsPin, CycleDelay>, CycleDelay>;
#[cfg(not(feature = "power-board"))]
pub type UferrisNrf54l15 = Uferris<
LedPin, ButtonPin, NrfBuzzerChannel, SharedI2c, LdrAdc, (),
>;
#[cfg(feature = "power-board")]
pub type UferrisNrf54l15 = Uferris<
LedPin, ButtonPin, NrfBuzzerChannel, SharedI2c, LdrAdc, SdBlockDevice, >;
#[cfg(all(feature = "async", not(feature = "power-board")))]
pub type UferrisNrf54l15Async = Uferris<
LedPin, ButtonPin, NrfBuzzerChannel, SharedAsyncI2c, LdrAdcAsync, (),
Async,
>;
#[cfg(all(feature = "async", feature = "power-board"))]
pub type UferrisNrf54l15Async = Uferris<
LedPin, ButtonPin, NrfBuzzerChannel, SharedAsyncI2c, LdrAdcAsync, SdBlockDevice, Async,
>;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CycleDelay;
impl DelayNs for CycleDelay {
fn delay_ns(&mut self, ns: u32) {
let cycles = u64::from(ns) * u64::from(SYS_CLOCK_HZ) / 1_000_000_000;
cortex_m::asm::delay(cycles as u32);
}
}
pub const fn delay() -> CycleDelay {
CycleDelay
}
pub fn uferris_init(peripherals: Peripherals) -> UferrisNrf54l15 {
let ldr_channel = ChannelConfig::single_ended(peripherals.P1_04);
let saadc = Saadc::new(
peripherals.SAADC,
Irqs,
SaadcConfig::default(),
[ldr_channel],
);
let ldr_driver = LdrAdc { _saadc: saadc };
let mut i2c_config = TwimConfig::default();
i2c_config.frequency = I2C_FREQ;
let i2c = Twim::new(
peripherals.SERIAL22,
Irqs,
peripherals.P1_10,
peripherals.P1_11,
i2c_config,
TWIM_RAM_BUFFER.init([0u8; TWIM_RAM_BUFFER_LEN]),
);
let i2c_bus_ref = I2C_BUS.init(RefCell::new(i2c));
let expander_i2c = I2cRefCellDevice::new(i2c_bus_ref);
let rtc_i2c = I2cRefCellDevice::new(i2c_bus_ref);
let raw_i2c = I2cRefCellDevice::new(i2c_bus_ref);
#[cfg(feature = "power-board")]
let ina_i2c = I2cRefCellDevice::new(i2c_bus_ref);
let led = Output::new(peripherals.P1_05, Level::Low, OutputDrive::Standard);
let button = Input::new(peripherals.P1_07, Pull::None);
let mut pwm_config = SimpleConfig::default();
pwm_config.prescaler = Prescaler::Div1;
pwm_config.max_duty = BUZZER_COUNTERTOP;
let pwm = SimplePwm::new_1ch(peripherals.PWM20, peripherals.P1_06, &pwm_config);
let mut buzzer_channel = NrfBuzzerChannel::new(pwm);
let Ok(()) = buzzer_channel.set_duty_cycle_fully_off();
#[cfg(feature = "power-board")]
let vol_mgr = {
let mut spi_config = SpimConfig::default();
spi_config.frequency = SPI_FREQ;
let spi = Spim::new(
peripherals.SERIAL00,
Irqs,
peripherals.P2_01,
peripherals.P2_04,
peripherals.P2_02,
spi_config,
);
pac::SPIM00
.prescaler()
.write(|w| w.set_divisor(SPI_DIVISOR));
let spi_bus_ref = SPI_BUS.init(RefCell::new(spi));
let sd_cs = Output::new(peripherals.P2_07, Level::High, OutputDrive::Standard);
let sd_device = SpiRefCellDevice::new(spi_bus_ref, sd_cs, delay()).unwrap();
let sd_card = embedded_sdmmc::SdCard::new(sd_device, delay());
Some(embedded_sdmmc::VolumeManager::new(
sd_card,
crate::DummyTimeSource::default(),
))
};
Uferris::new(
led,
button,
buzzer_channel,
ldr_driver,
expander_i2c,
rtc_i2c,
raw_i2c,
#[cfg(feature = "power-board")]
vol_mgr,
#[cfg(feature = "power-board")]
ina_i2c,
)
.unwrap()
}
#[cfg(feature = "async")]
pub async fn uferris_init_async(peripherals: Peripherals) -> UferrisNrf54l15Async {
let ldr_channel = ChannelConfig::single_ended(peripherals.P1_04);
let saadc = Saadc::new(
peripherals.SAADC,
Irqs,
SaadcConfig::default(),
[ldr_channel],
);
let ldr_driver = LdrAdcAsync { saadc };
let mut i2c_config = TwimConfig::default();
i2c_config.frequency = I2C_FREQ;
let i2c = Twim::new(
peripherals.SERIAL22,
Irqs,
peripherals.P1_10,
peripherals.P1_11,
i2c_config,
TWIM_RAM_BUFFER.init([0u8; TWIM_RAM_BUFFER_LEN]),
);
let i2c_bus_ref = ASYNC_I2C_BUS.init(AsyncMutex::new(i2c));
let expander_i2c = I2cAsyncDevice::new(i2c_bus_ref);
let rtc_i2c = I2cAsyncDevice::new(i2c_bus_ref);
let raw_i2c = I2cAsyncDevice::new(i2c_bus_ref);
let led = Output::new(peripherals.P1_05, Level::Low, OutputDrive::Standard);
let button = Input::new(peripherals.P1_07, Pull::None);
let mut pwm_config = SimpleConfig::default();
pwm_config.prescaler = Prescaler::Div1;
pwm_config.max_duty = BUZZER_COUNTERTOP;
let pwm = SimplePwm::new_1ch(peripherals.PWM20, peripherals.P1_06, &pwm_config);
let mut buzzer_channel = NrfBuzzerChannel::new(pwm);
let Ok(()) = buzzer_channel.set_duty_cycle_fully_off();
Uferris::new_async(
led,
button,
buzzer_channel,
ldr_driver,
expander_i2c,
rtc_i2c,
raw_i2c,
)
.await
.unwrap()
}