use core::ptr::null_mut;
use core::sync::atomic::{AtomicPtr, Ordering};
use embassy_rp::Peri;
use embassy_rp::adc::{self, Adc, AdcPin, Async, Channel};
use embassy_rp::gpio::Pull;
use embassy_rp::peripherals::{ADC, ADC_TEMP_SENSOR};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::Mutex;
use static_cell::StaticCell;
use crate::metadata::AVDD;
embassy_rp::bind_interrupts!(struct AdcIrqs {
ADC_IRQ_FIFO => embassy_rp::adc::InterruptHandler;
});
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub enum AdcError {
NotInitialized,
ConversionFailed,
}
const ADC_LEVELS: f64 = 4096.0;
const T_REF: f64 = 27.0;
const V_REF: f64 = 0.706;
const SLOPE: f64 = 0.001721;
struct AdcService {
adc: Adc<'static, Async>,
temp_channel: Channel<'static>,
}
impl AdcService {
fn new(adc_peri: Peri<'static, ADC>, temp_peri: Peri<'static, ADC_TEMP_SENSOR>) -> Self {
let adc = Adc::new(adc_peri, AdcIrqs, adc::Config::default());
let temp_channel = Channel::new_temp_sensor(temp_peri);
Self { adc, temp_channel }
}
async fn read_temp_raw(&mut self) -> Result<u16, AdcError> {
self.adc
.read(&mut self.temp_channel)
.await
.map_err(|_| AdcError::ConversionFailed)
}
async fn read_temp_celsius(&mut self) -> Result<f64, AdcError> {
let raw = self.read_temp_raw().await?;
let voltage = raw as f64 * AVDD / ADC_LEVELS;
let temp = T_REF - (voltage - V_REF) / SLOPE;
Ok(temp)
}
async fn read_pin_raw(
&mut self,
pin: Peri<'_, impl AdcPin>,
pull: Pull,
) -> Result<u16, AdcError> {
let mut channel = Channel::new_pin(pin, pull);
self.adc
.read(&mut channel)
.await
.map_err(|_| AdcError::ConversionFailed)
}
}
type AdcMutex = Mutex<CriticalSectionRawMutex, AdcService>;
static ADC_CELL: StaticCell<AdcMutex> = StaticCell::new();
static ADC_PTR: AtomicPtr<AdcMutex> = AtomicPtr::new(null_mut());
pub fn init_adc(adc: Peri<'static, ADC>, temp_sensor: Peri<'static, ADC_TEMP_SENSOR>) {
let mutex = ADC_CELL.init(Mutex::new(AdcService::new(adc, temp_sensor)));
ADC_PTR.store(mutex as *mut AdcMutex, Ordering::Release);
}
pub async fn read_temperature() -> Result<f64, AdcError> {
let mutex = adc_mutex().ok_or(AdcError::NotInitialized)?;
let mut guard = mutex.lock().await;
guard.read_temp_celsius().await
}
pub async fn read_temperature_raw() -> Result<u16, AdcError> {
let mutex = adc_mutex().ok_or(AdcError::NotInitialized)?;
let mut guard = mutex.lock().await;
guard.read_temp_raw().await
}
pub async fn read_adc_pin<P: AdcPin>(
pin: &mut Peri<'static, P>,
pull: Pull,
) -> Result<u16, AdcError> {
let mutex = adc_mutex().ok_or(AdcError::NotInitialized)?;
let mut guard = mutex.lock().await;
guard.read_pin_raw(pin.reborrow(), pull).await
}
pub async fn read_adc_voltage<P: AdcPin>(
pin: &mut Peri<'static, P>,
pull: Pull,
) -> Result<f64, AdcError> {
let raw = read_adc_pin(pin, pull).await?;
Ok(raw as f64 * AVDD / ADC_LEVELS)
}
fn adc_mutex() -> Option<&'static AdcMutex> {
let ptr = ADC_PTR.load(Ordering::Acquire);
if ptr.is_null() {
return None;
}
Some(unsafe { &*ptr })
}