use core::{marker::PhantomData, task::Poll};
use cortex_m::asm;
use embassy_hal_internal::{Peri, PeripheralType};
use crate::{
adc::channel::AdcChannel,
interrupt::typelevel::{Binding, Interrupt as InterruptType},
module_stop::ModuleStop,
pac,
};
#[cfg(adc12)]
use pac::adc12::{self as adc_pac, Adc12 as AdcPac};
#[cfg(adc14)]
use pac::adc14::{self as adc_pac, Adc14 as AdcPac};
#[cfg(adc16)]
use pac::adc16::{self as adc_pac, Adc16 as AdcPac};
use adc_pac::{
regs::Adans,
vals::{AdcCountSelect, Adcs},
};
#[cfg(adc12)]
use crate::peripherals::ADC12_0;
#[cfg(adc14)]
use crate::{pac::adc14::vals::Adprc, peripherals::ADC14_0};
#[cfg(adc16)]
use crate::peripherals::ADC16_0;
pub use channel::{AdcInputPin, AdcPin, AdcSequence, Temperature, Vref};
#[cfg(dmac)]
use crate::dmac::{Channel as DmacChannel, DmacInterruptHandler};
pub(crate) mod channel;
#[allow(private_bounds)]
pub struct Adc<'d, I: Instance> {
_phantom: PhantomData<&'d I>,
average_mode: AverageMode,
sample_time: u8,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, Default, PartialEq)]
pub enum AverageMode {
#[default]
Off,
Average2,
Average4,
#[cfg(any(adc12, adc14))]
Add2,
#[cfg(any(adc12, adc14))]
Add3,
#[cfg(any(adc12, adc14))]
Add4,
#[cfg(any(adc12, adc14))]
Add16,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, Default)]
pub enum Resolution {
#[cfg(any(adc12, adc14))]
#[default]
_12bit,
#[cfg(adc14)]
_14bit,
#[cfg(adc16)]
#[default]
_16bit,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, Default)]
pub enum Alignment {
FlushLeft,
#[default]
FlushRight,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone)]
pub struct AdcConfig {
#[allow(missing_docs)]
pub resolution: Resolution,
pub clear_on_read: bool,
#[allow(missing_docs)]
#[cfg(not(adc16))]
pub alignment: Alignment,
#[allow(missing_docs)]
pub average_mode: AverageMode,
pub sample_time: u8,
}
#[derive(Copy, Clone)]
struct AdcChannelConfig {
pub average_mode: AverageMode,
pub sample_time: u8,
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + ModuleStop + PeripheralType + 'static + Send {
const RX_EVENT: crate::event_link::InterruptEvent;
}
impl Default for AdcConfig {
fn default() -> Self {
Self {
resolution: Default::default(),
clear_on_read: Default::default(),
#[cfg(not(adc16))]
alignment: Default::default(),
average_mode: Default::default(),
sample_time: 13,
}
}
}
trait SealedInstance: PeripheralType {
fn regs() -> AdcPac;
}
#[cfg(adc12)]
impl SealedInstance for ADC12_0 {
fn regs() -> AdcPac {
pac::ADC12_0
}
}
#[cfg(adc14)]
impl SealedInstance for ADC14_0 {
fn regs() -> AdcPac {
pac::ADC14_0
}
}
#[cfg(adc16)]
impl SealedInstance for ADC16_0 {
fn regs() -> AdcPac {
pac::ADC16_0
}
}
#[cfg(adc12)]
impl Instance for ADC12_0 {
const RX_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::Adc0ScanEnd;
}
#[cfg(adc14)]
impl Instance for ADC14_0 {
const RX_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::Adc0ScanEnd;
}
#[cfg(adc16)]
impl Instance for ADC16_0 {
const RX_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::Adc0ScanEnd;
}
impl<'d, I: Instance> Adc<'d, I> {
pub fn new(_adc: Peri<'d, I>, config: AdcConfig) -> Self {
#[cfg(feature = "strict-assert")]
assert!(config.sample_time >= 5);
I::start_module();
#[cfg(adc14)]
let resolution = match config.resolution {
Resolution::_12bit => Adprc::_12bit,
Resolution::_14bit => Adprc::_14bit,
};
let clear_on_read = config.clear_on_read;
#[cfg(not(adc16))]
let alignment = match config.alignment {
Alignment::FlushLeft => true,
Alignment::FlushRight => false,
};
let adc = I::regs();
adc.adansa(0).write_value(Adans(0));
adc.adansa(1).write_value(Adans(0));
adc.adexicr().modify(|w| {
w.set_ocsa(false);
w.set_tssa(false);
});
adc.adcer().write(|w| {
#[cfg(adc14)]
{
w.set_adprc(resolution);
}
#[cfg(any(adc12, adc14))]
{
w.set_adrfmt(alignment);
}
w.set_ace(clear_on_read);
});
adc.adadc().write(|w| match config.average_mode {
AverageMode::Off => {}
AverageMode::Average2 => {
#[cfg(any(adc12, adc14))]
w.set_avee(true);
w.set_adc(AdcCountSelect::Convert2);
}
AverageMode::Average4 => {
#[cfg(any(adc12, adc14))]
w.set_avee(true);
w.set_adc(AdcCountSelect::Convert4);
}
#[cfg(any(adc12, adc14))]
AverageMode::Add2 => {
w.set_avee(false);
w.set_adc(AdcCountSelect::Convert2);
}
#[cfg(any(adc12, adc14))]
AverageMode::Add3 => {
w.set_avee(false);
w.set_adc(AdcCountSelect::Convert3);
}
#[cfg(any(adc12, adc14))]
AverageMode::Add4 => {
w.set_avee(false);
w.set_adc(AdcCountSelect::Convert4);
}
#[cfg(any(adc12, adc14))]
AverageMode::Add16 => {
w.set_avee(false);
w.set_adc(AdcCountSelect::Convert16);
}
});
Self {
_phantom: PhantomData,
average_mode: config.average_mode,
sample_time: config.sample_time,
}
}
pub fn resolution(&self) -> Resolution {
#[cfg(adc12)]
{
Resolution::_12bit
}
#[cfg(adc14)]
{
let adc = I::regs();
match adc.adcer().read().adprc() {
Adprc::_12bit => Resolution::_12bit,
Adprc::_RESERVED_1 => unimplemented!(),
Adprc::_RESERVED_2 => unimplemented!(),
Adprc::_14bit => Resolution::_14bit,
}
}
#[cfg(adc16)]
{
Resolution::_16bit
}
}
#[cfg(adc14)]
pub fn alignment(&self) -> Alignment {
let adc = I::regs();
match adc.adcer().read().adrfmt() {
true => Alignment::FlushLeft,
false => Alignment::FlushRight,
}
}
pub fn temperature_channel(&self) -> Temperature {
Temperature {}
}
pub fn vref_channel(&self) -> Vref {
Vref {}
}
pub fn blocking_read<R, C: AdcChannel<R>>(&self, channel: &C) -> R {
let adc = I::regs();
channel.enable::<I>(AdcChannelConfig {
average_mode: self.average_mode,
sample_time: self.sample_time,
});
adc.adcsr().modify(|w| w.set_adcs(Adcs::Single));
adc.adcsr().modify(|w| w.set_adst(true));
while adc.adcsr().read().adst() {
asm::nop()
}
channel.disable::<I>();
channel.read_one::<I>()
}
#[cfg(dmac)]
pub async fn read<
'a,
R: crate::dmac::Word,
RxDmaInstance: crate::dmac::Instance,
C: AdcChannel<R>,
DmaInt: InterruptType,
>(
&'a self,
rx_dma: Peri<'a, RxDmaInstance>,
channel: &C,
reading: &mut R,
irqs: impl Binding<DmaInt, DmacInterruptHandler<RxDmaInstance>>,
) {
let adc = I::regs();
let mut rx_dma = DmacChannel::new(rx_dma, irqs);
channel.enable::<I>(AdcChannelConfig {
average_mode: self.average_mode,
sample_time: self.sample_time,
});
adc.adcsr().modify(|w| w.set_adcs(Adcs::Single));
let rx = rx_dma.read::<R>(
channel.address::<I>(),
core::slice::from_mut(reading),
I::RX_EVENT,
);
adc.adcsr().modify(|w| w.set_adst(true));
rx.await;
channel.disable::<I>();
}
#[cfg(dmac)]
pub fn configured_sequence<
'a,
R: crate::dmac::Word,
RxDmaInstance: crate::dmac::Instance,
C: AdcChannel<R>,
DmaInt: InterruptType,
>(
&'a self,
rx_dma: Peri<'a, RxDmaInstance>,
channel: &C,
irqs: impl Binding<DmaInt, DmacInterruptHandler<RxDmaInstance>> + 'a,
) -> ConfiguredSequence<'a, I, R> {
let adc = I::regs();
channel.enable::<I>(AdcChannelConfig {
average_mode: self.average_mode,
sample_time: self.sample_time,
});
adc.adcsr().modify(|w| w.set_adcs(Adcs::Single));
ConfiguredSequence {
dma: DmacChannel::new(rx_dma, irqs),
source: channel.address::<I>(),
_phantom: PhantomData,
}
}
#[cfg(dmac)]
pub fn into_ring_buffered<
'a,
R: crate::dmac::Word,
RxDmaInstance: crate::dmac::Instance,
C: AdcChannel<u16>,
DmaInt: InterruptType,
>(
self,
rx_dma: Peri<'a, RxDmaInstance>,
channel: &C,
dma_buf: &'a mut [R],
irqs: impl Binding<DmaInt, DmacInterruptHandler<RxDmaInstance>> + 'a,
) -> RingBufferedAdc<'a, I, R>
where
'd: 'a,
{
assert!(
dma_buf.len() >= 2 && dma_buf.len() % 2 == 0,
"DMA buffer must have an even length >= 2"
);
let source = channel.address::<I>();
channel.enable::<I>(AdcChannelConfig {
average_mode: self.average_mode,
sample_time: self.sample_time,
});
I::regs().adcsr().modify(|w| w.set_adcs(Adcs::Continuous));
let mut dma = DmacChannel::new(rx_dma, irqs);
dma.configure_peripheral_read::<R>(source as _, I::RX_EVENT);
core::mem::forget(self);
RingBufferedAdc {
dma,
source: source as _,
buf: dma_buf.as_mut_ptr(),
half_len: dma_buf.len() / 2,
fill_half: false,
started: false,
_phantom: PhantomData,
}
}
}
#[cfg(dmac)]
#[allow(private_bounds)]
pub struct RingBufferedAdc<'d, I: Instance, R: crate::dmac::Word> {
dma: DmacChannel<'d>,
#[allow(dead_code)]
source: *const R,
buf: *mut R,
half_len: usize,
fill_half: bool,
started: bool,
_phantom: PhantomData<(&'d I, *mut R)>,
}
#[cfg(dmac)]
impl<'d, I: Instance, R: crate::dmac::Word> RingBufferedAdc<'d, I, R> {
#[inline]
fn arm(&mut self) {
let dest = unsafe {
if self.fill_half {
self.buf.add(self.half_len)
} else {
self.buf
}
};
self.dma.rearm::<R>(dest, self.half_len);
}
pub fn start(&mut self) {
if self.started {
return;
}
self.started = true;
self.fill_half = false;
self.arm();
I::regs().adcsr().modify(|w| w.set_adst(true));
}
pub fn stop(&mut self) {
I::regs().adcsr().modify(|w| w.set_adst(false));
self.dma.disable_dte();
self.started = false;
}
pub async fn read(&mut self, buf: &mut [R]) {
#[cfg(feature = "strict-assert")]
assert_eq!(
buf.len(),
self.half_len,
"Buffer must be half the DMA buffer length"
);
if !self.started {
self.start();
}
let fill_half = self.fill_half;
core::future::poll_fn(|cx| {
self.dma.register_waker(cx.waker());
if self.dma.take_dtif() {
self.fill_half = !fill_half;
self.arm();
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
let src_offset = if fill_half { self.half_len } else { 0 };
let src = unsafe { core::slice::from_raw_parts(self.buf.add(src_offset), self.half_len) };
buf.copy_from_slice(src);
}
}
#[cfg(dmac)]
impl<I: Instance, R: crate::dmac::Word> Drop for RingBufferedAdc<'_, I, R> {
fn drop(&mut self) {
if self.started {
self.stop();
}
let adc = I::regs();
adc.adansa(0).write_value(Adans(0));
adc.adansa(1).write_value(Adans(0));
adc.adexicr().modify(|w| {
w.set_ocsa(false);
w.set_tssa(false);
});
I::stop_module();
}
}
#[cfg(dmac)]
#[allow(private_bounds)]
pub struct ConfiguredSequence<'d, I: Instance, R: crate::dmac::Word> {
dma: DmacChannel<'d>,
source: *const R,
_phantom: PhantomData<(&'d I, R)>,
}
#[cfg(dmac)]
impl<'d, I: Instance, R: crate::dmac::Word> ConfiguredSequence<'d, I, R> {
pub async fn read(&mut self, reading: &mut R) {
let adc = I::regs();
let rx = self
.dma
.read::<R>(self.source, core::slice::from_mut(reading), I::RX_EVENT);
adc.adcsr().modify(|w| w.set_adst(true));
rx.await;
}
}
#[cfg(dmac)]
impl<I: Instance, R: crate::dmac::Word> Drop for ConfiguredSequence<'_, I, R> {
fn drop(&mut self) {
let adc = I::regs();
adc.adansa(0).write_value(Adans(0));
adc.adansa(1).write_value(Adans(0));
adc.adexicr().modify(|w| {
w.set_ocsa(false);
w.set_tssa(false);
});
}
}
impl<'d, I: Instance> Drop for Adc<'d, I> {
fn drop(&mut self) {
I::stop_module();
}
}