use core::{
ops::DerefMut,
pin::Pin,
sync::atomic::{compiler_fence, Ordering},
};
use as_slice::AsMutSlice;
use crate::{
gpio::*,
hal::adc::{Channel, OneShot},
pac::ADC,
rcc::{Enable, Rcc},
};
use crate::dma::{self, Buffer as _};
pub trait AdcExt {
fn constrain(self, rcc: &mut Rcc) -> Adc<Ready>;
}
impl AdcExt for ADC {
fn constrain(self, rcc: &mut Rcc) -> Adc<Ready> {
Adc::new(self, rcc)
}
}
#[derive(Eq, PartialEq)]
pub enum Align {
Right,
Left,
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Precision {
B_12 = 0b00,
B_10 = 0b01,
B_8 = 0b10,
B_6 = 0b11,
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum SampleTime {
T_1_5 = 0b000,
T_3_5 = 0b001,
T_7_5 = 0b010,
T_12_5 = 0b011,
T_19_5 = 0b100,
T_39_5 = 0b101,
T_79_5 = 0b110,
T_160_5 = 0b111,
}
pub struct Adc<State> {
rb: ADC,
sample_time: SampleTime,
align: Align,
precision: Precision,
_state: State,
}
impl Adc<Ready> {
pub fn new(adc: ADC, rcc: &mut Rcc) -> Self {
ADC::enable(rcc);
adc.cr.modify(|_, w| w.advregen().set_bit());
Self {
rb: adc,
sample_time: SampleTime::T_1_5,
align: Align::Right,
precision: Precision::B_12,
_state: Ready,
}
}
pub fn set_sample_time(&mut self, t_samp: SampleTime) {
self.sample_time = t_samp;
}
pub fn set_align(&mut self, align: Align) {
self.align = align;
}
pub fn set_precision(&mut self, precision: Precision) {
self.precision = precision;
}
pub fn start<DmaChan, Buf>(
mut self,
channels: impl Into<Channels>,
trigger: Option<Trigger>,
dma: &mut dma::Handle,
dma_chan: DmaChan,
buffer: Pin<Buf>,
) -> Adc<Active<DmaChan, Buf>>
where
DmaToken: dma::Target<DmaChan>,
Buf: DerefMut + 'static,
Buf::Target: AsMutSlice<Element = u16>,
DmaChan: dma::Channel,
{
let dma_token = DmaToken(());
let num_words = (*buffer).len();
let address = &self.rb.dr as *const _ as u32;
let buffer_unsafe = Buffer {
ptr: buffer.as_ptr(),
len: buffer.len() as u16,
pos: 0,
dma_pos: 0,
r_gt_w: false,
};
let transfer = unsafe {
dma::Transfer::new(
dma,
dma_token,
dma_chan,
buffer,
num_words,
address,
dma::Priority::high(),
dma::Direction::peripheral_to_memory(),
true,
)
}
.start();
let continous = trigger.is_none();
self.power_up();
self.configure(channels, continous, trigger);
Adc {
rb: self.rb,
sample_time: self.sample_time,
align: self.align,
precision: self.precision,
_state: Active {
buffer: buffer_unsafe,
transfer,
},
}
}
}
impl<DmaChan, Buffer> Adc<Active<DmaChan, Buffer>>
where
DmaChan: dma::Channel,
{
pub fn read_available(
&mut self,
) -> Result<impl Iterator<Item = Result<u16, Error>> + '_, Error> {
if self.rb.isr.read().ovr().is_overrun() {
self.rb.isr.write(|w| w.ovr().clear());
return Err(Error::AdcOverrun);
}
Ok(ReadAvailable {
buffer: &mut self._state.buffer,
transfer: &mut self._state.transfer,
})
}
}
impl<State> Adc<State> {
pub fn release(self) -> ADC {
self.rb
}
fn power_up(&mut self) {
self.rb.isr.modify(|_, w| w.adrdy().set_bit());
self.rb.cr.modify(|_, w| w.aden().set_bit());
while self.rb.isr.read().adrdy().bit_is_clear() {}
}
fn power_down(&mut self) {
self.rb.cr.modify(|_, w| w.addis().set_bit());
self.rb.isr.modify(|_, w| w.adrdy().set_bit());
while self.rb.cr.read().aden().bit_is_set() {}
}
fn configure(&mut self, channels: impl Into<Channels>, cont: bool, trigger: Option<Trigger>) {
self.rb.cfgr1.write(|w| {
w.res().bits(self.precision as u8);
w.cont().bit(cont);
w.align().bit(self.align == Align::Left);
w.dmacfg().set_bit();
w.dmaen().set_bit();
if let Some(trigger) = trigger {
w.extsel().bits(trigger as u8);
w.exten().rising_edge();
}
w
});
self.rb
.smpr
.modify(|_, w| w.smp().bits(self.sample_time as u8));
self.rb.chselr.write(|w|
unsafe { w.bits(channels.into().flags) });
self.rb.isr.modify(|_, w| w.eos().set_bit());
self.rb.cr.modify(|_, w| w.adstart().set_bit());
}
}
impl<WORD, PIN> OneShot<Adc<Ready>, WORD, PIN> for Adc<Ready>
where
WORD: From<u16>,
PIN: Channel<Adc<Ready>, ID = u8>,
{
type Error = ();
fn read(&mut self, _: &mut PIN) -> nb::Result<WORD, Self::Error> {
self.power_up();
self.configure(
Channels {
flags: 0x1 << PIN::channel(),
},
false,
None,
);
while self.rb.isr.read().eos().bit_is_clear() {}
let res = self.rb.dr.read().bits() as u16;
let val = if self.align == Align::Left && self.precision == Precision::B_6 {
res << 8
} else {
res
};
self.power_down();
Ok(val.into())
}
}
pub struct Ready;
pub struct Active<DmaChan, Buf> {
transfer: dma::Transfer<DmaToken, DmaChan, Buf, dma::Started>,
buffer: Buffer,
}
#[derive(Default)]
pub struct Channels {
flags: u32,
}
impl Channels {
pub fn new() -> Channels {
Default::default()
}
pub fn add<C>(&mut self, _: C)
where
C: Channel<Adc<Ready>, ID = u8>,
{
self.flags |= 0x1 << C::channel()
}
}
impl<C> From<C> for Channels
where
C: Channel<Adc<Ready>, ID = u8>,
{
fn from(channel: C) -> Self {
let mut c = Channels { flags: 0 };
c.add(channel);
c
}
}
#[repr(u8)]
pub enum Trigger {
TIM6_TRGO = 0b000,
TIM21_CH2 = 0b001,
TIM2_TRGO = 0b010,
TIM2_CH4 = 0b011,
TIM22_TRGO = 0b100,
#[cfg(any(feature = "stm32l072", feature = "stm32l082"))]
TIM2_CH3 = 0b101,
TIM3_TRGO = 0b110,
EXTI11 = 0b111,
}
struct Buffer {
ptr: *const u16,
len: u16,
pos: u16,
dma_pos: u16,
r_gt_w: bool,
}
impl Buffer {
fn read<T, C, B>(
&mut self,
transfer: &dma::Transfer<T, C, B, dma::Started>,
) -> Option<Result<u16, Error>>
where
C: dma::Channel,
{
let transfer_state = self.transfer_state(transfer);
if self.check_overrun(transfer_state) {
return Some(Err(Error::BufferOverrun));
}
if self.pos == transfer_state.pos {
return None;
}
compiler_fence(Ordering::SeqCst);
let value = unsafe { *self.ptr.offset(self.pos as isize) };
compiler_fence(Ordering::SeqCst);
let transfer_state = self.transfer_state(transfer);
if self.check_overrun(transfer_state) {
return Some(Err(Error::BufferOverrun));
}
self.pos = self.pos.wrapping_add(1);
if self.pos == 0 || self.pos >= self.len {
self.pos = 0;
self.r_gt_w = false;
}
Some(Ok(value))
}
fn transfer_state<T, C, B>(
&self,
transfer: &dma::Transfer<T, C, B, dma::Started>,
) -> TransferState
where
C: dma::Channel,
{
let (remaining, half, complete) = transfer.state();
transfer.clear_flags();
let pos = self.len - remaining;
TransferState {
pos,
half,
complete,
}
}
fn check_overrun(&mut self, transfer_state: TransferState) -> bool {
let overrun = self.check_overrun_inner(transfer_state);
self.dma_pos = transfer_state.pos;
if overrun {
self.pos = transfer_state.pos;
self.r_gt_w = false;
}
overrun
}
fn check_overrun_inner(&mut self, transfer_state: TransferState) -> bool {
if transfer_state.half && transfer_state.complete {
return true;
}
if transfer_state.complete && self.dma_pos < transfer_state.pos {
return true;
}
if transfer_state.pos < self.dma_pos {
if self.r_gt_w {
return true;
}
self.r_gt_w = true;
}
if self.r_gt_w {
self.pos <= transfer_state.pos
} else {
self.pos > transfer_state.pos
}
}
}
#[derive(Clone, Copy, Debug)]
struct TransferState {
pos: u16,
half: bool,
complete: bool,
}
pub struct ReadAvailable<'r, T, C, B> {
buffer: &'r mut Buffer,
transfer: &'r dma::Transfer<T, C, B, dma::Started>,
}
impl<T, C, B> Iterator for ReadAvailable<'_, T, C, B>
where
C: dma::Channel,
{
type Item = Result<u16, Error>;
fn next(&mut self) -> Option<Self::Item> {
self.buffer.read(self.transfer)
}
}
pub struct DmaToken(());
#[derive(Debug)]
pub enum Error {
AdcOverrun,
BufferOverrun,
}
macro_rules! int_adc {
($($Chan:ident: ($chan:expr, $en:ident)),+ $(,)*) => {
$(
pub struct $Chan;
impl Default for $Chan {
fn default() -> Self {
Self {}
}
}
impl $Chan {
pub fn new() -> Self {
Default::default()
}
pub fn enable(&mut self, adc: &mut Adc<Ready>) {
adc.rb.ccr.modify(|_, w| w.$en().set_bit());
}
pub fn disable(&mut self, adc: &mut Adc<Ready>) {
adc.rb.ccr.modify(|_, w| w.$en().clear_bit());
}
}
impl Channel<Adc<Ready>> for $Chan {
type ID = u8;
fn channel() -> u8 {
$chan
}
}
)+
};
}
macro_rules! adc_pins {
($($Chan:ty: ($pin:ty, $chan:expr)),+ $(,)*) => {
$(
impl Channel<Adc<Ready>> for $pin {
type ID = u8;
fn channel() -> u8 { $chan }
}
)+
};
}
int_adc! {
VTemp: (18, tsen),
VRef: (17, vrefen),
}
adc_pins! {
Channel0: (gpioa::PA0<Analog>, 0u8),
Channel1: (gpioa::PA1<Analog>, 1u8),
Channel2: (gpioa::PA2<Analog>, 2u8),
Channel3: (gpioa::PA3<Analog>, 3u8),
Channel4: (gpioa::PA4<Analog>, 4u8),
Channel5: (gpioa::PA5<Analog>, 5u8),
Channel6: (gpioa::PA6<Analog>, 6u8),
Channel7: (gpioa::PA7<Analog>, 7u8),
Channel8: (gpiob::PB0<Analog>, 8u8),
Channel9: (gpiob::PB1<Analog>, 9u8),
}
#[cfg(all(feature = "stm32l052", any(feature = "lqfp64", feature = "tfbga64",),))]
adc_pins! {
Channel10: (gpioc::PC0<Analog>, 10u8),
Channel11: (gpioc::PC1<Analog>, 11u8),
Channel12: (gpioc::PC2<Analog>, 12u8),
}
#[cfg(all(
feature = "stm32l072",
any(
feature = "lqfp64",
feature = "lqfp100",
feature = "tfbga64",
feature = "ufbga64",
feature = "ufbga100",
feature = "wlcsp49",
),
))]
adc_pins! {
Channel10: (gpioc::PC0<Analog>, 10u8),
Channel11: (gpioc::PC1<Analog>, 11u8),
Channel12: (gpioc::PC2<Analog>, 12u8),
}
#[cfg(all(feature = "stm32l082", feature = "wlcsp49"))]
adc_pins! {
Channel10: (gpioc::PC0<Analog>, 10u8),
Channel11: (gpioc::PC1<Analog>, 11u8),
Channel12: (gpioc::PC2<Analog>, 12u8),
}
#[cfg(all(feature = "stm32l052", feature = "lqfp64"))]
adc_pins! {
Channel13: (gpioc::PC3<Analog>, 13u8),
}
#[cfg(all(
feature = "stm32l072",
any(feature = "lqfp64", feature = "lqfp100", feature = "ufbga100",),
))]
adc_pins! {
Channel13: (gpioc::PC3<Analog>, 13u8),
}
#[cfg(all(feature = "stm32l052", any(feature = "lqfp64", feature = "tfbga64",),))]
adc_pins! {
Channel14: (gpioc::PC4<Analog>, 14u8),
Channel15: (gpioc::PC5<Analog>, 15u8),
}
#[cfg(all(
feature = "stm32l072",
any(
feature = "lqfp64",
feature = "lqfp100",
feature = "tfbga64",
feature = "ufbga64",
feature = "ufbga100",
),
))]
adc_pins! {
Channel14: (gpioc::PC4<Analog>, 14u8),
Channel15: (gpioc::PC5<Analog>, 15u8),
}