2021-10-10 23:22:01 +00:00
#![ macro_use ]
2021-03-24 17:33:17 +00:00
use core ::marker ::PhantomData ;
use core ::sync ::atomic ::{ compiler_fence , Ordering } ;
use core ::task ::Poll ;
2021-09-01 21:54:26 +00:00
use embassy ::interrupt ::InterruptExt ;
2021-09-10 23:53:53 +00:00
use embassy ::util ::Unborrow ;
use embassy ::waitqueue ::AtomicWaker ;
2021-07-29 11:44:51 +00:00
use embassy_hal_common ::unborrow ;
2021-03-24 17:33:17 +00:00
use futures ::future ::poll_fn ;
2021-04-14 17:59:52 +00:00
use crate ::interrupt ;
use crate ::{ pac , peripherals } ;
2021-03-24 17:33:17 +00:00
use pac ::{ saadc , SAADC } ;
pub use saadc ::{
ch ::{
config ::{ GAIN_A as Gain , REFSEL_A as Reference , RESP_A as Resistor , TACQ_A as Time } ,
2021-10-10 22:38:35 +00:00
pselp ::PSELP_A as InputChannel , // We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same
2021-03-24 17:33:17 +00:00
} ,
oversample ::OVERSAMPLE_A as Oversample ,
resolution ::VAL_A as Resolution ,
} ;
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
#[ cfg_attr(feature = " defmt " , derive(defmt::Format)) ]
#[ non_exhaustive ]
pub enum Error { }
/// One-shot saadc. Continuous sample mode TODO.
2021-10-07 07:00:03 +00:00
pub struct OneShot < ' d , const N : usize > {
2021-05-22 13:42:14 +00:00
phantom : PhantomData < & ' d mut peripherals ::SAADC > ,
2021-03-24 17:33:17 +00:00
}
2021-09-01 21:54:26 +00:00
static WAKER : AtomicWaker = AtomicWaker ::new ( ) ;
2021-03-24 17:33:17 +00:00
/// Used to configure the SAADC peripheral.
///
/// See the `Default` impl for suitable default values.
2021-10-07 07:00:03 +00:00
#[ non_exhaustive ]
2021-03-24 17:33:17 +00:00
pub struct Config {
/// Output resolution in bits.
pub resolution : Resolution ,
/// Average 2^`oversample` input samples before transferring the result into memory.
pub oversample : Oversample ,
2021-10-07 07:00:03 +00:00
}
impl Default for Config {
/// Default configuration for single channel sampling.
fn default ( ) -> Self {
Self {
resolution : Resolution ::_14BIT ,
oversample : Oversample ::BYPASS ,
}
}
}
/// Used to configure an individual SAADC peripheral channel.
///
/// See the `Default` impl for suitable default values.
#[ non_exhaustive ]
2021-10-10 21:52:45 +00:00
pub struct ChannelConfig < ' d > {
2021-03-24 17:33:17 +00:00
/// Reference voltage of the SAADC input.
pub reference : Reference ,
/// Gain used to control the effective input range of the SAADC.
pub gain : Gain ,
/// Positive channel resistor control.
pub resistor : Resistor ,
/// Acquisition time in microseconds.
pub time : Time ,
2021-10-07 07:00:03 +00:00
/// Positive channel to sample
2021-10-10 22:38:35 +00:00
p_channel : InputChannel ,
2021-10-07 07:00:03 +00:00
/// An optional negative channel to sample
2021-10-10 22:38:35 +00:00
n_channel : Option < InputChannel > ,
2021-10-10 21:52:45 +00:00
phantom : PhantomData < & ' d ( ) > ,
2021-03-24 17:33:17 +00:00
}
2021-10-10 21:52:45 +00:00
impl < ' d > ChannelConfig < ' d > {
2021-10-07 07:00:03 +00:00
/// Default configuration for single ended channel sampling.
2021-10-10 22:38:35 +00:00
pub fn single_ended ( input : impl Unborrow < Target = impl Input > + ' d ) -> Self {
unborrow! ( input ) ;
2021-10-07 07:00:03 +00:00
Self {
reference : Reference ::INTERNAL ,
gain : Gain ::GAIN1_6 ,
resistor : Resistor ::BYPASS ,
time : Time ::_10US ,
2021-10-10 22:38:35 +00:00
p_channel : input . channel ( ) ,
2021-10-07 07:00:03 +00:00
n_channel : None ,
2021-10-10 21:52:45 +00:00
phantom : PhantomData ,
2021-10-07 07:00:03 +00:00
}
}
/// Default configuration for differential channel sampling.
pub fn differential (
2021-10-10 22:38:35 +00:00
p_input : impl Unborrow < Target = impl Input > + ' d ,
n_input : impl Unborrow < Target = impl Input > + ' d ,
2021-10-07 07:00:03 +00:00
) -> Self {
2021-10-10 22:38:35 +00:00
unborrow! ( p_input , n_input ) ;
2021-03-24 17:33:17 +00:00
Self {
reference : Reference ::VDD1_4 ,
2021-10-07 07:00:03 +00:00
gain : Gain ::GAIN1_6 ,
2021-03-24 17:33:17 +00:00
resistor : Resistor ::BYPASS ,
2021-10-07 07:00:03 +00:00
time : Time ::_10US ,
2021-10-10 22:38:35 +00:00
p_channel : p_input . channel ( ) ,
n_channel : Some ( n_input . channel ( ) ) ,
2021-10-10 21:52:45 +00:00
phantom : PhantomData ,
2021-03-24 17:33:17 +00:00
}
}
}
2021-10-07 07:00:03 +00:00
impl < ' d , const N : usize > OneShot < ' d , N > {
2021-03-24 17:33:17 +00:00
pub fn new (
2021-05-17 10:23:04 +00:00
_saadc : impl Unborrow < Target = peripherals ::SAADC > + ' d ,
2021-04-14 17:59:52 +00:00
irq : impl Unborrow < Target = interrupt ::SAADC > + ' d ,
2021-03-24 17:33:17 +00:00
config : Config ,
2021-10-07 07:00:03 +00:00
channel_configs : [ ChannelConfig ; N ] ,
2021-03-24 17:33:17 +00:00
) -> Self {
2021-05-21 11:06:28 +00:00
unborrow! ( irq ) ;
2021-03-24 17:33:17 +00:00
let r = unsafe { & * SAADC ::ptr ( ) } ;
let Config {
resolution ,
oversample ,
} = config ;
2021-10-07 07:00:03 +00:00
// Configure channels
2021-03-24 17:33:17 +00:00
r . enable . write ( | w | w . enable ( ) . enabled ( ) ) ;
r . resolution . write ( | w | w . val ( ) . variant ( resolution ) ) ;
r . oversample . write ( | w | w . oversample ( ) . variant ( oversample ) ) ;
2021-10-07 07:00:03 +00:00
for ( i , cc ) in channel_configs . iter ( ) . enumerate ( ) {
r . ch [ i ] . pselp . write ( | w | w . pselp ( ) . variant ( cc . p_channel ) ) ;
2021-10-10 22:38:35 +00:00
if let Some ( n_channel ) = cc . n_channel {
r . ch [ i ]
. pseln
. write ( | w | unsafe { w . pseln ( ) . bits ( n_channel as u8 ) } ) ;
2021-03-24 17:33:17 +00:00
}
2021-10-07 07:00:03 +00:00
r . ch [ i ] . config . write ( | w | {
w . refsel ( ) . variant ( cc . reference ) ;
w . gain ( ) . variant ( cc . gain ) ;
w . tacq ( ) . variant ( cc . time ) ;
if cc . n_channel . is_none ( ) {
w . mode ( ) . se ( ) ;
} else {
w . mode ( ) . diff ( ) ;
}
w . resp ( ) . variant ( cc . resistor ) ;
w . resn ( ) . bypass ( ) ;
if ! matches! ( oversample , Oversample ::BYPASS ) {
w . burst ( ) . enabled ( ) ;
} else {
w . burst ( ) . disabled ( ) ;
}
w
} ) ;
}
2021-03-24 17:33:17 +00:00
// Disable all events interrupts
r . intenclr . write ( | w | unsafe { w . bits ( 0x003F_FFFF ) } ) ;
2021-09-01 21:54:26 +00:00
irq . set_handler ( Self ::on_interrupt ) ;
irq . unpend ( ) ;
irq . enable ( ) ;
2021-03-24 17:33:17 +00:00
Self {
phantom : PhantomData ,
}
}
2021-09-01 21:54:26 +00:00
fn on_interrupt ( _ctx : * mut ( ) ) {
let r = Self ::regs ( ) ;
if r . events_end . read ( ) . bits ( ) ! = 0 {
r . intenclr . write ( | w | w . end ( ) . clear ( ) ) ;
WAKER . wake ( ) ;
}
}
fn regs ( ) -> & 'static saadc ::RegisterBlock {
2021-03-24 17:33:17 +00:00
unsafe { & * SAADC ::ptr ( ) }
}
2021-05-22 13:42:14 +00:00
2021-10-07 07:00:03 +00:00
pub async fn sample ( & mut self , buf : & mut [ i16 ; N ] ) {
2021-09-01 21:54:26 +00:00
let r = Self ::regs ( ) ;
2021-05-22 13:42:14 +00:00
// Set up the DMA
r . result
. ptr
2021-10-07 07:00:03 +00:00
. write ( | w | unsafe { w . ptr ( ) . bits ( buf . as_mut_ptr ( ) as u32 ) } ) ;
r . result
. maxcnt
2021-10-10 21:54:24 +00:00
. write ( | w | unsafe { w . maxcnt ( ) . bits ( N as _ ) } ) ;
2021-05-22 13:42:14 +00:00
// Reset and enable the end event
r . events_end . reset ( ) ;
r . intenset . write ( | w | w . end ( ) . set ( ) ) ;
// Don't reorder the ADC start event before the previous writes. Hopefully self
// wouldn't happen anyway.
compiler_fence ( Ordering ::SeqCst ) ;
r . tasks_start . write ( | w | unsafe { w . bits ( 1 ) } ) ;
r . tasks_sample . write ( | w | unsafe { w . bits ( 1 ) } ) ;
// Wait for 'end' event.
poll_fn ( | cx | {
2021-09-01 21:54:26 +00:00
let r = Self ::regs ( ) ;
WAKER . register ( cx . waker ( ) ) ;
2021-05-22 13:42:14 +00:00
if r . events_end . read ( ) . bits ( ) ! = 0 {
r . events_end . reset ( ) ;
return Poll ::Ready ( ( ) ) ;
}
Poll ::Pending
} )
. await ;
}
2021-03-24 17:33:17 +00:00
}
2021-10-07 07:00:03 +00:00
impl < ' d , const N : usize > Drop for OneShot < ' d , N > {
2021-03-24 17:33:17 +00:00
fn drop ( & mut self ) {
2021-09-01 21:54:26 +00:00
let r = Self ::regs ( ) ;
2021-03-24 17:33:17 +00:00
r . enable . write ( | w | w . enable ( ) . disabled ( ) ) ;
}
}
2021-10-10 22:38:35 +00:00
/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.
2021-10-13 19:58:43 +00:00
pub trait Input : Unborrow < Target = Self > {
2021-10-10 22:38:35 +00:00
fn channel ( & self ) -> InputChannel ;
2021-03-24 17:33:17 +00:00
}
2021-10-10 23:22:01 +00:00
macro_rules ! impl_saadc_input {
( $pin :ident , $ch :ident ) = > {
impl crate ::saadc ::Input for crate ::peripherals ::$pin {
fn channel ( & self ) -> crate ::saadc ::InputChannel {
crate ::saadc ::InputChannel ::$ch
2021-10-07 07:00:03 +00:00
}
2021-10-10 23:22:01 +00:00
}
2021-10-07 07:00:03 +00:00
} ;
}