use crate::hal::adc::{Channel, OneShot};
use crate::{pac::ADC, HALExt};
use core::{convert::Infallible, marker::PhantomData};
use embedded_time::rate::*;
#[derive(Debug)]
pub enum Error {
Moved,
}
pub struct Analog<Pin> {
pin: Pin,
}
pub struct Adc<State> {
peripheral: ADC,
_state: PhantomData<State>,
pub onchip_channels: OnChipChannels,
}
impl HALExt for ADC {
type T = Adc<Disabled>;
fn split(self) -> Adc<Disabled> {
Adc {
peripheral: self,
_state: PhantomData,
onchip_channels: OnChipChannels {
vss: Some(Analog {
pin: Vss::<Input> { _mode: PhantomData },
}),
temp_sense: Some(Analog {
pin: TempSense::<Input> { _mode: PhantomData },
}),
bandgap: Some(Analog {
pin: Bandgap::<Input> { _mode: PhantomData },
}),
vref_h: Some(Analog {
pin: VrefH::<Input> { _mode: PhantomData },
}),
vref_l: Some(Analog {
pin: VrefL::<Input> { _mode: PhantomData },
}),
},
}
}
}
pub struct AdcConfig {
pub clock_source: AdcClocks,
pub clock_divisor: ClockDivisor,
pub resolution: AdcResolution,
pub sample_time: AdcSampleTime,
pub low_power: bool,
}
impl AdcConfig {
pub fn calculate_divisor(&mut self, source_freq: Hertz, req_adc_freq: Hertz) {
let denom: u8 = (source_freq.integer() / req_adc_freq.integer()) as u8;
let mut output: u8 = 1;
let mut err: i8 = (denom - output) as i8;
let mut err_old: i8 = err;
let max_divisor = match self.clock_source {
AdcClocks::Bus => 16,
_ => 8,
};
while output < max_divisor {
err = (denom - (output << 1)) as i8;
if err.is_negative() {
err = err.abs();
}
if err <= err_old {
output <<= 1;
err_old = err;
} else {
break;
}
}
let ad_clock = source_freq.integer() / output as u32;
assert!(400_000 <= ad_clock);
assert!(
ad_clock
<= match self.low_power {
false => 8_000_000,
true => 4_000_000,
}
);
self.clock_divisor = match output {
1 => ClockDivisor::_1,
2 => ClockDivisor::_2,
4 => ClockDivisor::_4,
8 => ClockDivisor::_8,
_ => ClockDivisor::_16,
}
}
pub fn set_divisor(&mut self, divisor: ClockDivisor) {
assert!(
!(!matches!(self.clock_source, AdcClocks::Bus) && matches!(divisor, ClockDivisor::_16))
);
self.clock_divisor = divisor;
}
pub fn set_clock_source(&mut self, clock: AdcClocks) {
assert!(
!matches!(clock, AdcClocks::Bus) && matches!(self.clock_divisor, ClockDivisor::_16)
);
self.clock_source = clock;
}
}
impl Default for AdcConfig {
fn default() -> AdcConfig {
AdcConfig {
clock_source: AdcClocks::Bus,
clock_divisor: ClockDivisor::_1,
resolution: AdcResolution::_12bit,
sample_time: AdcSampleTime::Short,
low_power: false,
}
}
}
pub enum AdcClocks {
Bus,
External,
Async,
}
#[repr(u8)]
pub enum AdcResolution {
_8bit = 0,
_10bit = 1,
_12bit = 2,
}
pub enum AdcSampleTime {
Short = 0,
Long = 1,
}
pub enum ClockDivisor {
_1 = 0,
_2 = 1,
_4 = 2,
_8 = 3,
_16 = 4,
}
pub struct Enabled;
pub struct Disabled;
impl Adc<Enabled> {
pub fn is_done(&self) -> bool {
self.peripheral.sc1.read().coco().bit()
}
pub fn is_converting(&self) -> bool {
self.peripheral.sc2.read().adact().bit()
}
pub fn result(&self) -> u16 {
self.peripheral.r.read().adr().bits()
}
pub fn try_result(&self) -> Option<u16> {
if self.is_done() {
Some(self.result())
} else {
None
}
}
pub fn set_channel<T: Channel<Adc<Enabled>, ID = u8>>(&self, _pin: &T) {
self.peripheral
.sc1
.modify(|_, w| unsafe { w.adch().bits(T::channel()) });
}
pub fn configure(self, config: AdcConfig) -> Adc<Enabled> {
self.peripheral.sc3.modify(|_, w| {
use pac::adc::sc3::{ADICLK_A, ADIV_A, ADLSMP_A, MODE_A};
w.adiclk()
.variant(match config.clock_source {
AdcClocks::Bus =>
{
match config.clock_divisor {
ClockDivisor::_16 => ADICLK_A::_01,
_ => ADICLK_A::_00,
}
}
AdcClocks::External => ADICLK_A::_10,
AdcClocks::Async => ADICLK_A::_11,
})
.mode()
.variant(match config.resolution {
AdcResolution::_8bit => MODE_A::_00,
AdcResolution::_10bit => MODE_A::_01,
AdcResolution::_12bit => MODE_A::_10,
})
.adlsmp()
.variant(match config.sample_time {
AdcSampleTime::Short => ADLSMP_A::_0,
AdcSampleTime::Long => ADLSMP_A::_1,
})
.adiv()
.variant(match config.clock_divisor {
ClockDivisor::_1 => ADIV_A::_00,
ClockDivisor::_2 => ADIV_A::_01,
ClockDivisor::_4 => ADIV_A::_10,
_ => ADIV_A::_11,
})
.adlpc()
.bit(config.low_power)
});
Adc {
peripheral: self.peripheral,
_state: PhantomData,
onchip_channels: self.onchip_channels,
}
}
}
impl Adc<Disabled> {
pub fn enable(self) -> Adc<Enabled> {
cortex_m::interrupt::free(|_| {
unsafe { &(*pac::SIM::ptr()) }.scgc.modify(|_, w| {
use pac::sim::scgc::ADC_A;
w.adc().variant(ADC_A::_1)
});
self.peripheral.sc1.modify(|_, w| w.adch()._11111());
unsafe { &(*pac::PMC::ptr()) }
.spmsc1
.modify(|_, w| w.bgbe()._1());
});
Adc {
peripheral: self.peripheral,
_state: PhantomData,
onchip_channels: self.onchip_channels,
}
}
pub fn configure(self, config: AdcConfig) -> Adc<Enabled> {
self.enable().configure(config)
}
}
impl<Mode> Adc<Mode> {
pub fn into_interrupt(self) -> Adc<Mode> {
unimplemented!("Interrupt is not yet implemented");
}
pub fn into_fifo(self, _depth: u8) -> Adc<Mode> {
unimplemented!("FIFO is not yet implemented");
}
pub fn into_continuous(self) -> Adc<Mode> {
unimplemented!("Continuous Conversion mode not yet implemented");
}
}
impl OnChipChannels {
pub fn vss(&mut self) -> Result<Analog<Vss<Input>>, Error> {
self.vss.take().ok_or(Error::Moved)
}
pub fn return_vss(&mut self, inst: Analog<Vss<Input>>) {
self.vss.replace(inst);
}
pub fn tempsense(&mut self) -> Result<Analog<TempSense<Input>>, Error> {
self.temp_sense.take().ok_or(Error::Moved)
}
pub fn return_tempsense(&mut self, inst: Analog<TempSense<Input>>) {
self.temp_sense.replace(inst);
}
pub fn bandgap(&mut self) -> Result<Analog<Bandgap<Input>>, Error> {
self.bandgap.take().ok_or(Error::Moved)
}
pub fn return_bandgap(&mut self, inst: Analog<Bandgap<Input>>) {
self.bandgap.replace(inst);
}
pub fn vref_h(&mut self) -> Result<Analog<VrefH<Input>>, Error> {
self.vref_h.take().ok_or(Error::Moved)
}
pub fn return_vref_h(&mut self, inst: Analog<VrefH<Input>>) {
self.vref_h.replace(inst);
}
pub fn vref_l(&mut self) -> Result<Analog<VrefL<Input>>, Error> {
self.vref_l.take().ok_or(Error::Moved)
}
pub fn return_vref_l(&mut self, inst: Analog<VrefL<Input>>) {
self.vref_l.replace(inst);
}
pub fn dummy_disable(&self) -> Analog<DummyDisable<Input>> {
Analog {
pin: DummyDisable::<Input> { _mode: PhantomData },
}
}
}
pub struct OnChipChannels {
vss: Option<Analog<Vss<Input>>>,
temp_sense: Option<Analog<TempSense<Input>>>,
bandgap: Option<Analog<Bandgap<Input>>>,
vref_h: Option<Analog<VrefH<Input>>>,
vref_l: Option<Analog<VrefL<Input>>>,
}
pub struct Input;
pub struct Vss<Input> {
_mode: PhantomData<Input>,
}
pub struct TempSense<Input> {
_mode: PhantomData<Input>,
}
pub struct Bandgap<Input> {
_mode: PhantomData<Input>,
}
pub struct VrefH<Input> {
_mode: PhantomData<Input>,
}
pub struct VrefL<Input> {
_mode: PhantomData<Input>,
}
pub struct DummyDisable<Input> {
_mode: PhantomData<Input>,
}
macro_rules! adc_input_channels {
( $($Chan:expr => $Pin:ident),+ $(,)*) => {
$(
impl<OldMode> Channel<Adc<Enabled>> for Analog<$Pin<OldMode>> {
type ID = u8;
fn channel() -> u8 { $Chan }
}
)+
};
}
use crate::gpio::{gpioa::*, gpiob::*};
adc_input_channels! (
0_u8 => PTA0,
1_u8 => PTA1,
2_u8 => PTA6,
3_u8 => PTA7,
4_u8 => PTB0,
5_u8 => PTB1,
6_u8 => PTB2,
7_u8 => PTB3,
8_u8 => PTC0,
9_u8 => PTC1,
10_u8 => PTC2,
11_u8 => PTC3,
12_u8 => PTF4,
13_u8 => PTF5,
14_u8 => PTF6,
15_u8 => PTF7,
16_u8 => Vss,
22_u8 => TempSense,
23_u8 => Bandgap,
24_u8 => VrefH,
25_u8 => VrefL,
0x1F_u8 => DummyDisable,
);
macro_rules! impl_analog_pin {
( $($Chan:expr => $Pin:ident),+ $(,)*) => {
$(
impl<OldMode> $Pin<OldMode> {
pub fn into_analog(self) -> Analog<$Pin<OldMode>> {
unsafe {
(*ADC::ptr())
.apctl1
.modify(|r, w| w.adpc().bits(r.adpc().bits() | (1 << $Chan)));
}
Analog { pin: self }
}
}
impl<OldMode> Analog<$Pin<OldMode>> {
pub fn outof_analog(self) -> $Pin<OldMode> {
let adc = unsafe { &(*ADC::ptr()) };
adc.apctl1
.modify(|r, w| unsafe { w.adpc().bits(r.adpc().bits() & !(1 << $Chan)) });
self.pin
}
}
)+
};
}
impl_analog_pin!(
0_u8 => PTA0,
1_u8 => PTA1,
2_u8 => PTA6,
3_u8 => PTA7,
4_u8 => PTB0,
5_u8 => PTB1,
6_u8 => PTB2,
7_u8 => PTB3,
8_u8 => PTC0,
9_u8 => PTC1,
10_u8 => PTC2,
11_u8 => PTC3,
12_u8 => PTF4,
13_u8 => PTF5,
14_u8 => PTF6,
15_u8 => PTF7,
);
impl<Pin> OneShot<Adc<Enabled>, u16, Pin> for Adc<Enabled>
where
Pin: Channel<Adc<Enabled>, ID = u8>,
{
type Error = Infallible;
fn read(&mut self, pin: &mut Pin) -> nb::Result<u16, Self::Error> {
self.set_channel(pin);
while !self.is_done() {}
let ret_val = Ok(self.result());
let disable = self.onchip_channels.dummy_disable();
self.set_channel(&disable);
ret_val
}
}