use crate::peripherals::Lsadc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdcChannel {
Channel0 = 0,
Channel1 = 1,
Channel2 = 2,
Channel3 = 3,
Channel4 = 4,
Channel5 = 5,
}
impl AdcChannel {
pub fn from_index(idx: u8) -> Option<Self> {
match idx {
0 => Some(Self::Channel0),
1 => Some(Self::Channel1),
2 => Some(Self::Channel2),
3 => Some(Self::Channel3),
4 => Some(Self::Channel4),
5 => Some(Self::Channel5),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Averaging {
One = 0,
Two = 1,
Four = 2,
Eight = 3,
}
#[derive(Debug, Clone, Copy)]
pub struct AdcConfig {
pub averaging: Averaging,
pub sample_count: u8,
pub start_count: u8,
pub cast_count: u8,
}
impl Default for AdcConfig {
fn default() -> Self {
Self { averaging: Averaging::Eight, sample_count: 0x8, start_count: 0x18, cast_count: 0x0 }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AdcSample {
pub data: u16,
pub channel: u8,
}
#[inline]
const fn parse_sample(raw: u32) -> AdcSample {
AdcSample { data: (raw & 0x3FFF) as u16, channel: ((raw >> 14) & 0x07) as u8 }
}
pub struct LsAdc<'d> {
_lsadc: Lsadc<'d>,
}
impl<'d> LsAdc<'d> {
pub fn new(lsadc: Lsadc<'d>) -> Self {
Self { _lsadc: lsadc }
}
fn regs(&self) -> &'static ws63_pac::lsadc::RegisterBlock {
unsafe { &*Lsadc::ptr() }
}
pub fn enable(&mut self) {
self.regs().lsadc_ctrl_11().modify(|_, w| w.da_lsadc_rstn().set_bit());
}
pub fn disable(&mut self) {
self.regs().lsadc_ctrl_11().modify(|_, w| w.da_lsadc_rstn().clear_bit());
}
pub fn set_analog_enable(&mut self, bits: u16) {
self.regs().lsadc_ctrl_11().modify(|_, w| unsafe { w.da_lsadc_en().bits(bits) });
}
pub fn configure_scan(&mut self, channel: AdcChannel, config: &AdcConfig) {
self.regs().lsadc_ctrl_0().modify(|r, w| {
let ch = r.channel().bits() | (1 << (channel as u8));
unsafe {
w.channel().bits(ch);
w.equ_model_sel().bits(config.averaging as u8);
w.sample_cnt().bits(config.sample_count & 0x1F);
w.start_cnt().bits(config.start_count);
w.cast_cnt().bits(config.cast_count & 0x7F)
}
});
}
pub fn start_scan(&mut self) {
self.regs().lsadc_ctrl_8().write(|w| w.lsadc_start().set_bit());
}
pub fn stop_scan(&mut self) {
self.regs().lsadc_ctrl_8().write(|w| w.lsadc_stop().set_bit());
}
pub fn set_fifo_waterline(&mut self, level: u8) {
self.regs().lsadc_ctrl_1().modify(|_, w| unsafe { w.rxintsize().bits(level & 0x07) });
}
pub fn data_ready(&self) -> bool {
self.regs().lsadc_ctrl_1().read().rne().bit_is_set()
}
pub fn read_sample(&self) -> Option<AdcSample> {
if !self.data_ready() {
return None;
}
Some(parse_sample(self.regs().lsadc_ctrl_9().read().bits()))
}
pub fn enable_cic_filter(&mut self, oversampling_ratio: u8) {
let r = self.regs();
unsafe {
r.cfg_cic_osr().write(|w| w.cic_osr().bits(oversampling_ratio));
}
r.cfg_cic_filter_en().write(|w| w.cic_filter_en().set_bit());
}
pub fn disable_cic_filter(&mut self) {
self.regs().cfg_cic_filter_en().write(|w| w.cic_filter_en().clear_bit());
}
pub fn set_offset(&mut self, offset: u16) {
self.regs().cfg_offset().write(|w| unsafe { w.offset().bits(offset) });
}
pub fn set_gain(&mut self, gain: u16) {
self.regs().cfg_gain().write(|w| unsafe { w.gain().bits(gain) });
}
pub fn set_data_select(&mut self, processed: bool) {
self.regs().cfg_data_sel().write(|w| w.data_sel().bit(processed));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_sample_data_and_channel() {
let raw: u32 = (3 << 14) | 0x0AAA;
let s = parse_sample(raw);
assert_eq!(s.data, 0x0AAA);
assert_eq!(s.channel, 3);
}
#[test]
fn test_parse_sample_max_code() {
let s = parse_sample(0x3FFF); assert_eq!(s.data, 0x3FFF);
assert_eq!(s.channel, 0);
}
#[test]
fn test_parse_sample_channel_field_is_3_bits() {
let raw: u32 = 0xFFFF_FFFF;
let s = parse_sample(raw);
assert_eq!(s.channel, 0x07);
assert_eq!(s.data, 0x3FFF);
}
#[test]
fn test_channel_from_index() {
assert_eq!(AdcChannel::from_index(0), Some(AdcChannel::Channel0));
assert_eq!(AdcChannel::from_index(5), Some(AdcChannel::Channel5));
assert_eq!(AdcChannel::from_index(6), None);
assert_eq!(AdcChannel::from_index(255), None);
}
#[test]
fn test_default_config_matches_sdk() {
let c = AdcConfig::default();
assert_eq!(c.averaging as u8, 3); assert_eq!(c.sample_count, 0x8);
assert_eq!(c.start_count, 0x18);
assert_eq!(c.cast_count, 0x0);
}
}
#[cfg(test)]
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn parse_channel_always_3_bits(raw in any::<u32>()) {
prop_assert!(parse_sample(raw).channel <= 0x07);
}
#[test]
fn parse_data_always_14_bits(raw in any::<u32>()) {
prop_assert!(parse_sample(raw).data <= 0x3FFF);
}
#[test]
fn parse_sample_lanes(raw in any::<u32>()) {
let s = parse_sample(raw);
prop_assert_eq!(s.data, (raw & 0x3FFF) as u16);
prop_assert_eq!(s.channel, ((raw >> 14) & 0x07) as u8);
}
#[test]
fn channel_from_index_coverage(idx in any::<u8>()) {
prop_assert_eq!(AdcChannel::from_index(idx).is_some(), idx <= 5);
}
}
}
#[cfg(feature = "async")]
mod asynch_impl {
use super::{AdcSample, LsAdc};
use crate::asynch::IrqSignal;
use crate::interrupt::{self, Interrupt};
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
static LSADC_SIGNAL: IrqSignal = IrqSignal::new();
pub fn on_interrupt() {
LSADC_SIGNAL.signal();
interrupt::clear_pending(Interrupt::LSADC_INTR);
}
struct ConvFuture;
impl Future for ConvFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if LSADC_SIGNAL.take_fired() {
Poll::Ready(())
} else {
LSADC_SIGNAL.register(cx.waker());
Poll::Pending
}
}
}
impl LsAdc<'_> {
pub async fn read_async(&mut self) -> Option<AdcSample> {
LSADC_SIGNAL.reset();
unsafe { interrupt::enable(Interrupt::LSADC_INTR) };
self.start_scan();
if !self.data_ready() {
ConvFuture.await;
}
self.read_sample()
}
}
}
#[cfg(feature = "async")]
pub use asynch_impl::on_interrupt;