Compare commits

...

16 Commits
v0.1.1 ... main

Author SHA1 Message Date
ef2f8d309a add date in changelog
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2024-06-16 16:02:48 +02:00
ba41653564 fix CI file
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2024-06-16 15:58:08 +02:00
c5a5ead52d Merge pull request 'embedded HAL v1 and defmt support' (#2) from bump-embedded-hal into main
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
Reviewed-on: #2
2024-06-16 15:57:05 +02:00
aab40bc027 update CI and docs settings
Some checks are pending
Rust/max116xx-10bit/pipeline/head Build started...
Rust/max116xx-10bit/pipeline/pr-main Build started...
2024-06-16 15:53:27 +02:00
88270085c9
remove chip select management
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
Rust/max116xx-10bit/pipeline/pr-main This commit looks good
2024-06-15 19:30:49 +02:00
a69671ce22
bumped embedded HAL
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2024-06-15 19:05:50 +02:00
c9b5f6a4e9
add Eq derives
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2022-09-13 10:46:30 +02:00
3422801850
small clippy improvement
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2022-06-18 22:40:05 +02:00
8a81b3a721
typo
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2021-12-14 14:36:58 +01:00
0532c1b94e
v0.2.1
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
2021-12-14 14:23:10 +01:00
8eb5b556db Merge pull request 'Several improvements' (#1) from simplified-api into main
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
Reviewed-on: #1
2021-12-14 14:20:04 +01:00
0cd4d7369b
add two more toolchains in jenkinsfile
All checks were successful
Rust/max116xx-10bit/pipeline/pr-main This commit looks good
Rust/max116xx-10bit/pipeline/head This commit looks good
2021-12-14 14:13:45 +01:00
cc0680ea63
update jenkinsfile
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
Rust/max116xx-10bit/pipeline/pr-main This commit looks good
there are now examples
2021-12-14 14:12:10 +01:00
c2255f2da9
bump subversion
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
Rust/max116xx-10bit/pipeline/pr-main This commit looks good
2021-12-14 14:07:13 +01:00
6b416f5684
Merge remote-tracking branch 'origin/main' into simplified-api
All checks were successful
Rust/max116xx-10bit/pipeline/head This commit looks good
Rust/max116xx-10bit/pipeline/pr-main This commit looks good
2021-12-14 14:06:07 +01:00
061cc6e9ee Several improvements
Some checks failed
Rust/max116xx-10bit/pipeline/pr-main There was a failure building this commit
Rust/max116xx-10bit/pipeline/head This commit looks good
Added

- Extended and improved type-level support significantly
- Added implementation for externally clocked mode with wakeup delay
- Added simple implementation for using CNVST pin. This is untested because board did not have
  CNVST connected

Changed

- Improved documentation
- Made library easier to use
2021-12-14 14:01:31 +01:00
8 changed files with 705 additions and 230 deletions

2
.github/bors.toml vendored
View File

@ -1,2 +0,0 @@
status = ["ci"]
delete_merged_branches = true

View File

@ -1,43 +1,64 @@
on: push
name: ci
on: [push, pull_request]
jobs:
ci:
check:
name: Check build
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --release
cross-check:
name: Check Cross-Compilation
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- stable
target:
- x86_64-unknown-linux-gnu
- thumbv6m-none-eabi
- armv7-unknown-linux-gnueabihf
- thumbv7em-none-eabihf
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
components: rustfmt, clippy
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
- run: cargo check --release --target=${{matrix.target}} --no-default-features
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: check
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install nextest
uses: taiki-e/install-action@nextest
- run: cargo nextest run --all-features
- run: cargo test --doc
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
fmt:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo fmt --all -- --check
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: clippy
args: -- -D warnings
docs:
name: Check Documentation Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo clippy -- -D warnings

View File

@ -8,6 +8,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.3.0] 2024-06-16
- Update `embedded-hal` to v1.
- Add optional `defmt` feature.
## [v0.2.1]
- README tweaks
## [v0.2.0]
### Added
- Extended and improved type-level support significantly
- Added implementation for externally clocked mode with wakeup delay
- Added simple implementation for using CNVST pin. This is untested because board did not have
CNVST connected
### Changed
- Improved documentation
- Made library easier to use
## [v0.1.1]
- Added badges and some documentation and README tweaks

View File

@ -1,6 +1,6 @@
[package]
name = "max116xx-10bit"
version = "0.1.1"
version = "0.3.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "Driver crate for the MAX116xx 10-bit ADC devices"
@ -12,5 +12,10 @@ categories = ["embedded", "no-std", "hardware-support"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
embedded-hal = { version = "0.2.6", features = ["unproven"] }
nb = "1.0.0"
embedded-hal = "1"
nb = "1"
defmt = { version = "0.3", optional = true }
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]

View File

@ -9,13 +9,8 @@ This is a platform agnostic Rust driver for the MAX11618-MAX11621, MAX11624 and
[ADC devices](https://www.maximintegrated.com/en/products/analog/data-converters/analog-to-digital-converters/MAX11619.html)
which uses the [`embedded-hal`](https://github.com/rust-embedded/embedded-hal) traits.
This driver implements basic operations to read raw ADC values:
- Read ADC values using the SPI clock as an external clock
- Read ADC values using the End-Of-Conversion (EOC) pin
Currently, the driver only supports operation without a wake-up delay and the EOC read
functionality is still limited. Pull requests to improve this are welcome.
This driver supports most required features but the CNVST pin support is still limited because
the test development board did not have the pin connected. Pull requests to improve this are welcome.
# Usage
@ -25,6 +20,6 @@ the appropriate device.
The crate uses basic type-level support to prevent using the ADC in a wrong way.
The type-level support defaults to an externally clocked device with no wake-up delay.
This crate was tested using the Vorago REB1 development board. You can find an example application
[here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs)
and [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs).
This crate was tested using the Vorago REB1 development board. You can find the example
application [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs)
using a [thin abstraction layer](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs)

View File

@ -7,5 +7,6 @@ RUN apt-get --yes upgrade
# tzdata is a dependency, won't install otherwise
ARG DEBIAN_FRONTEND=noninteractive
RUN rustup target add thumbv6m-none-eabi armv7-unknown-linux-gnueabihf && \
RUN rustup target add thumbv6m-none-eabi armv7-unknown-linux-gnueabihf \
thumbv7em-none-eabihf x86_64-unknown-linux-gnu && \
rustup component add rustfmt clippy

View File

@ -34,18 +34,8 @@ pipeline {
steps {
sh 'cargo check --target thumbv6m-none-eabi'
sh 'cargo check --target armv7-unknown-linux-gnueabihf'
}
}
stage('Check Examples') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
sh 'cargo check --target thumbv6m-none-eabi --examples'
sh 'cargo check --target armv7-unknown-linux-gnueabihf'
sh 'cargo check --target x86_64-unknown-linux-gnu'
sh 'cargo check --target thumbv7em-none-eabihf'
}
}
}

View File

@ -1,17 +1,32 @@
//! The crate uses basic type-level support to prevent using the ADC in a wrong way.
//! The type-level support defaults to an externally clocked device with no wake-up delay.
//! Type-Safe API to use the MAX116xx 10-bit ADC devices
//!
//! ## Usage
//!
//! You can create an initial ADC struct by using the [`Max116xx10Bit::max11618`],
//! [`Max116xx10Bit::max11619`], [`Max116xx10Bit::max11620`], [`Max116xx10Bit::max11621`],
//! [`Max116xx10Bit::max11624`] and [`Max116xx10Bit::max11625`] functions depending on which device
//! you are using. This automatically sets the highest channel number accordingly.
//!
//! The default structs use the externally clocked mode with an external voltage reference.
//! You can modify the operation mode of the ADC by converting the default struct using the
//! following functions
//!
//! - [`Max116xx10Bit::into_ext_clkd_with_int_ref_no_wakeup_delay`]
//! - [`Max116xx10Bit::into_ext_clkd_with_int_ref_wakeup_delay`]
//! - [`Max116xx10Bit::into_int_clkd_int_timed_through_ser_if_with_wakeup`]
//! - [`Max116xx10Bit::into_int_clkd_int_timed_through_ser_if_without_wakeup`]
//!
//! ## Examples
//!
//! You can find an example application [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs)
//! and [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs)
//! You can find an example application [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs)
//! using a [thin abstraction layer](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs)
#![no_std]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
use core::convert::Infallible;
use core::{marker::PhantomData, slice::IterMut};
use embedded_hal::{
blocking::spi::Transfer,
digital::v2::{InputPin, OutputPin},
spi::FullDuplex,
};
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal::spi::SpiDevice;
//==================================================================================================
// Type-level support
@ -21,16 +36,29 @@ pub trait HasChannels: private::Sealed {
const NUM: u8;
}
pub struct Max11618;
pub struct Max11619;
pub struct Max11620;
pub struct Max11621;
pub struct Max11624;
pub struct Max11625;
struct Max11618;
struct Max11619;
struct Max11620;
struct Max11621;
struct Max11624;
struct Max11625;
pub struct WithWakeupDelay;
pub struct WithoutWakeupDelay;
pub trait WakeupDelay: private::Sealed {
const ON: bool;
}
impl WakeupDelay for WithWakeupDelay {
const ON: bool = true;
}
impl private::Sealed for WithWakeupDelay {}
impl WakeupDelay for WithoutWakeupDelay {
const ON: bool = false;
}
impl private::Sealed for WithoutWakeupDelay {}
impl private::Sealed for Max11618 {}
impl HasChannels for Max11618 {
const NUM: u8 = 4;
@ -61,66 +89,68 @@ impl HasChannels for Max11625 {
const NUM: u8 = 16;
}
pub trait Clocked: private::Sealed {
pub trait ClockedProvider: private::Sealed {
const CLK_SEL: ClockMode;
}
pub trait InternallyClocked: Clocked {}
pub trait InternallyClocked: ClockedProvider {}
pub struct InternallyClockedInternallyTimedCnvst {}
impl private::Sealed for InternallyClockedInternallyTimedCnvst {}
impl Clocked for InternallyClockedInternallyTimedCnvst {
impl ClockedProvider for InternallyClockedInternallyTimedCnvst {
const CLK_SEL: ClockMode = ClockMode::InternalClockInternallyTimedCnvst;
}
impl InternallyClocked for InternallyClockedInternallyTimedCnvst {}
type IntClkdIntTmdCnvst = InternallyClockedInternallyTimedCnvst;
pub struct InternallyClockedExternallyTimedCnvst {}
impl private::Sealed for InternallyClockedExternallyTimedCnvst {}
impl Clocked for InternallyClockedExternallyTimedCnvst {
impl ClockedProvider for InternallyClockedExternallyTimedCnvst {
const CLK_SEL: ClockMode = ClockMode::InternalClockExternallyTimedCnvst;
}
impl InternallyClocked for InternallyClockedExternallyTimedCnvst {}
type IntClkdExtTmdCnvst = InternallyClockedExternallyTimedCnvst;
pub struct InternallyClockedInternallyTimedSerialInterface {}
impl private::Sealed for InternallyClockedInternallyTimedSerialInterface {}
impl Clocked for InternallyClockedInternallyTimedSerialInterface {
impl ClockedProvider for InternallyClockedInternallyTimedSerialInterface {
const CLK_SEL: ClockMode = ClockMode::InternalClockInternallyTimedSerialInterface;
}
impl InternallyClocked for InternallyClockedInternallyTimedSerialInterface {}
type IntClkdIntTmdSerIF = InternallyClockedInternallyTimedSerialInterface;
pub struct ExternallyClocked {}
impl private::Sealed for ExternallyClocked {}
impl Clocked for ExternallyClocked {
impl ClockedProvider for ExternallyClocked {
const CLK_SEL: ClockMode = ClockMode::ExternalClockExternallyTimedSclk;
}
type ExtClkd = ExternallyClocked;
//==================================================================================================
// Definitions
//==================================================================================================
#[derive(Debug, PartialEq)]
pub enum PendingOp {
None,
SingleChannel,
MultiChannel,
}
/// Clock modes for the MAX116XX devices
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockMode {
/// Internally timed, CNVST only needs to be pulsed for 40ns.
/// CNVST Configuration: CNVST active low
InternalClockInternallyTimedCnvst = 0b00,
/// Externally timed through CNVST. CNVST Configuration: CNVST active low
/// Externally timed through CNVST. CNVST needs to be held low for the conversion duration,
/// whigh might include the wake-up delay. CNVST Configuration: CNVST active low
InternalClockExternallyTimedCnvst = 0b01,
/// Start conversions using the serial interface instead of CNVST.
/// Default mode at power-up. CNVST Configuration: AIN15/AIN11/AIN7
/// Start conversions using the serial interface instead of CNVST
InternalClockInternallyTimedSerialInterface = 0b10,
/// Use the SPI clock as the conversion clock
ExternalClockExternallyTimedSclk = 0b11,
}
/// Voltage reference modes
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RefMode {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum VoltageRefMode {
/// Auto-Shutdown is on, wake-up delay of 65 us
InternalRefWithWakeupDelay = 0b00,
ExternalSingleEndedNoWakeupDelay = 0b01,
@ -129,6 +159,7 @@ pub enum RefMode {
/// Specifies how many conversions are performed and then averaged for each
/// requested result
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AveragingConversions {
OneConversion = 0b000,
FourConversions = 0b100,
@ -138,6 +169,8 @@ pub enum AveragingConversions {
}
/// Specifies the number of returned result in single scan mode
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AveragingResults {
FourResults = 0b00,
EightResults = 0b01,
@ -145,18 +178,23 @@ pub enum AveragingResults {
SixteenResults = 0b11,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ScanMode {
Scan0ToChannelN = 0b00,
ScanChannelNToHighest = 0b01,
ScanChnanelNRepeatedly = 0b10,
ScanChannelNRepeatedly = 0b10,
ConvertChannelNOnce = 0b11,
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcError {
InvalidChannel,
InvalidRefMode,
CmdBufTooSmall,
ResulBufTooSmall,
/// Other pending operation (possible of other type)
PendingOperation,
NoPendingOperation,
InvalidClockMode,
@ -175,73 +213,217 @@ impl<SpiE, PinE> From<AdcError> for Error<SpiE, PinE> {
}
}
//==================================================================================================
// ADc implementation
//==================================================================================================
pub struct Max116xx10Bit<
SPI,
CS,
MAX: HasChannels,
CLOCKED = ExternallyClocked,
DELAY = WithoutWakeupDelay,
> {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct InternalCfg {
clk_mode: ClockMode,
ref_mode: RefMode,
spi: SPI,
cs: CS,
pending_op: PendingOp,
max: PhantomData<MAX>,
clocked: PhantomData<CLOCKED>,
delay: PhantomData<DELAY>,
ref_mode: VoltageRefMode,
pending_scan_mode: Option<ScanMode>,
max_channels: u8,
results_len: u8,
requested_conversions: usize,
}
impl<SpiE, PinE, CS, SPI, MAX: HasChannels, CLOCKED: Clocked, DELAY>
Max116xx10Bit<SPI, CS, MAX, CLOCKED, DELAY>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
/// Create a new generic MAX116xx instance.
pub fn new(spi: SPI, cs: CS, ref_mode: RefMode) -> Result<Self, Error<SpiE, PinE>> {
let mut max_dev = Max116xx10Bit {
clk_mode: CLOCKED::CLK_SEL,
ref_mode,
//==================================================================================================
// ADC implementation
//==================================================================================================
pub struct Max116xx10Bit<Spi, Clocked = ExtClkd, Wakeup = WithoutWakeupDelay> {
spi: Spi,
cfg: InternalCfg,
clocked: PhantomData<Clocked>,
delay: PhantomData<Wakeup>,
}
pub struct Max116xx10BitEocExt<SPI, EOC, CLOCKED> {
base: Max116xx10Bit<SPI, CLOCKED, WithoutWakeupDelay>,
eoc: EOC,
}
pub struct Max116xx10BitCnvstEocExt<SPI, EOC, CNVST, CLOCKED, WAKEUP = WithoutWakeupDelay> {
base: Max116xx10Bit<SPI, CLOCKED, WAKEUP>,
eoc: EOC,
cnvst: CNVST,
}
//==================================================================================================
// Generic
//==================================================================================================
impl<Spi: SpiDevice> Max116xx10Bit<Spi, ExtClkd, WithoutWakeupDelay> {
pub fn max11618(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11618>(spi)
}
pub fn max11619(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11619>(spi)
}
pub fn max11620(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11620>(spi)
}
pub fn max11621(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11621>(spi)
}
pub fn max11624(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11624>(spi)
}
pub fn max11625(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11625>(spi)
}
/// Create a new generic MAX116xx instance. By default the generated ADC struct is configured
/// for externally clocked mode with conversions timed through the serial interface and
/// an external voltage reference. You can convert the ADC to use other SETUP register
/// configurations using the `into*` functions.
///
/// The corresponding SETUP register is `0b0111_0100`
/// Please note that you still might have to reset and setup the ADC.
pub fn new<MAX: HasChannels>(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
let max_dev = Max116xx10Bit {
spi,
cs,
pending_op: PendingOp::None,
max: PhantomData,
cfg: InternalCfg {
clk_mode: ExtClkd::CLK_SEL,
ref_mode: VoltageRefMode::ExternalSingleEndedNoWakeupDelay,
pending_scan_mode: None,
max_channels: MAX::NUM,
results_len: Self::get_results_len(AveragingResults::FourResults),
requested_conversions: 0,
},
delay: PhantomData,
clocked: PhantomData,
};
max_dev.setup()?;
Ok(max_dev)
}
/// Use internal reference which is off after scan. This means that the device needs a wakeup
/// delay
///
/// The corresponding SETUP register is `0b0111_0000`
pub fn into_ext_clkd_with_int_ref_wakeup_delay(
self,
) -> Max116xx10Bit<Spi, ExtClkd, WithWakeupDelay> {
Max116xx10Bit {
spi: self.spi,
cfg: InternalCfg {
clk_mode: ExtClkd::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithWakeupDelay,
pending_scan_mode: None,
max_channels: self.cfg.max_channels,
results_len: Self::get_results_len(AveragingResults::FourResults),
requested_conversions: 0,
},
clocked: PhantomData,
delay: PhantomData,
}
}
/// Use SPI clock as conversion clock and use the internal voltage reference without a wakeup
/// delay
///
/// The corresponding SETUP register is `0b0111_1000`
pub fn into_ext_clkd_with_int_ref_no_wakeup_delay(
self,
) -> Max116xx10Bit<Spi, ExtClkd, WithoutWakeupDelay> {
Max116xx10Bit {
spi: self.spi,
cfg: InternalCfg {
clk_mode: ExtClkd::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithoutWakeupDelay,
pending_scan_mode: None,
max_channels: self.cfg.max_channels,
results_len: Self::get_results_len(AveragingResults::FourResults),
requested_conversions: 0,
},
clocked: PhantomData,
delay: PhantomData,
}
}
/// Convert into interally clocked mode with internal timing initiated by the serial interface
/// and a wakeup delay. This can be used to reduce power consumption
///
/// The corresponding SETUP register is `0b0110_1100`
pub fn into_int_clkd_int_timed_through_ser_if_with_wakeup<Eoc: InputPin>(
self,
eoc: Eoc,
) -> Max116xx10BitEocExt<Spi, Eoc, IntClkdIntTmdSerIF> {
Max116xx10BitEocExt {
base: Max116xx10Bit {
spi: self.spi,
cfg: InternalCfg {
clk_mode: IntClkdIntTmdSerIF::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithWakeupDelay,
pending_scan_mode: None,
max_channels: self.cfg.max_channels,
results_len: Self::get_results_len(AveragingResults::FourResults),
requested_conversions: 0,
},
clocked: PhantomData,
delay: PhantomData,
},
eoc,
}
}
/// Convert into interally clocked mode with internal timing initiated by the serial interface
/// and no wakeup delay.
///
/// The corresponding SETUP register can be one of the two
/// - External Voltage reference: `0b0110_0100`
/// - Internal Voltage reference always on: `0b0110_1000`
pub fn into_int_clkd_int_timed_through_ser_if_without_wakeup<Eoc: InputPin>(
self,
v_ref: VoltageRefMode,
eoc: Eoc,
) -> Result<Max116xx10BitEocExt<Spi, Eoc, IntClkdIntTmdSerIF>, AdcError> {
if v_ref == VoltageRefMode::InternalRefWithWakeupDelay {
return Err(AdcError::InvalidRefMode);
}
Ok(Max116xx10BitEocExt {
base: Max116xx10Bit {
spi: self.spi,
cfg: InternalCfg {
clk_mode: IntClkdIntTmdSerIF::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithWakeupDelay,
pending_scan_mode: None,
max_channels: self.cfg.max_channels,
results_len: Self::get_results_len(AveragingResults::FourResults),
requested_conversions: 0,
},
clocked: PhantomData,
delay: PhantomData,
},
eoc,
})
}
}
impl<Spi: SpiDevice, Clocked: ClockedProvider, Wakeup> Max116xx10Bit<Spi, Clocked, Wakeup> {
#[inline]
fn send_wrapper(&mut self, byte: u8) -> Result<(), Error<SpiE, PinE>> {
self.cs.set_low().map_err(Error::Pin)?;
nb::block!(self.spi.send(byte)).map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Pin)?;
fn send_wrapper(&mut self, byte: u8) -> Result<(), Error<Spi::Error, Infallible>> {
self.spi.write(&[byte]).map_err(Error::Spi)?;
Ok(())
}
/// Set up the ADC depending on clock and reference configuration
#[inline]
pub fn setup(&mut self) -> Result<(), Error<SpiE, PinE>> {
pub fn setup(&mut self) -> Result<(), Error<Spi::Error, Infallible>> {
self.send_wrapper(self.get_setup_byte())
}
/// Set up the Averaging register. This sets the AVGON, NAVG1, NAVG0, NSCAN1 and NSCAN0
/// bits accordingly
#[inline]
pub fn averaging(
&mut self,
avg_conv: AveragingConversions,
avg_res: AveragingResults,
) -> Result<(), Error<SpiE, PinE>> {
) -> Result<(), Error<Spi::Error, Infallible>> {
self.cfg.results_len = Self::get_results_len(avg_res);
self.send_wrapper(Self::get_averaging_byte(avg_conv, avg_res))
}
#[inline]
pub fn reset_adc(&mut self, fifo_only: bool) -> Result<(), Error<SpiE, PinE>> {
pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error<Spi::Error, Infallible>> {
let mut reset_byte = 0b0001_0000;
if fifo_only {
reset_byte |= 1 << 3;
@ -251,7 +433,7 @@ where
#[inline]
pub fn get_setup_byte(&self) -> u8 {
((1 << 6) as u8) | ((self.clk_mode as u8) << 4) | ((self.ref_mode as u8) << 2)
((1 << 6) as u8) | ((self.cfg.clk_mode as u8) << 4) | ((self.cfg.ref_mode as u8) << 2)
}
#[inline]
@ -260,76 +442,40 @@ where
}
#[inline]
pub fn get_conversion_byte(scan_mode: ScanMode, channel_num: u8) -> Result<u8, AdcError> {
if channel_num > MAX::NUM {
pub fn get_results_len(avg_res: AveragingResults) -> u8 {
(avg_res as u8 + 1) * 4
}
#[inline]
pub fn get_conversion_byte(
&self,
scan_mode: ScanMode,
channel_num: u8,
) -> Result<u8, AdcError> {
if channel_num > self.cfg.max_channels {
return Err(AdcError::InvalidChannel);
}
Ok((1 << 7) | (channel_num << 3) | ((scan_mode as u8) << 1))
}
}
impl<SpiE, PinE, SPI, CS, MAX: HasChannels>
Max116xx10Bit<SPI, CS, MAX, InternallyClockedInternallyTimedSerialInterface, WithoutWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
#[inline]
fn request_wrapper(
/// Generic function which can be used a single result is available
/// when EOC is low
fn internal_read_single_channel<Eoc: InputPin>(
&mut self,
channel_num: u8,
scan_mode: ScanMode,
op_type: PendingOp,
) -> Result<(), Error<SpiE, PinE>> {
if self.pending_op != PendingOp::None {
return Err(Error::Adc(AdcError::PendingOperation));
}
let conv_byte = Self::get_conversion_byte(scan_mode, channel_num).map_err(Error::Adc)?;
self.send_wrapper(conv_byte)?;
self.pending_op = op_type;
Ok(())
}
pub fn request_single_channel(&mut self, channel_num: u8) -> Result<(), Error<SpiE, PinE>> {
self.request_wrapper(
channel_num,
ScanMode::ConvertChannelNOnce,
PendingOp::SingleChannel,
)
}
pub fn request_multiple_channels_0_to_n(&mut self, n: u8) -> Result<(), Error<SpiE, PinE>> {
self.request_wrapper(n, ScanMode::Scan0ToChannelN, PendingOp::MultiChannel)
}
pub fn request_multiple_channels_n_to_highest(
&mut self,
n: u8,
) -> Result<(), Error<SpiE, PinE>> {
self.request_wrapper(n, ScanMode::ScanChannelNToHighest, PendingOp::MultiChannel)
}
pub fn get_single_channel<I: InputPin<Error = PinE>>(
&mut self,
eoc_pin: &mut I,
) -> nb::Result<u16, Error<SpiE, PinE>> {
if self.pending_op != PendingOp::SingleChannel {
eoc: &mut Eoc,
) -> nb::Result<u16, Error<Spi::Error, Eoc::Error>> {
if self.cfg.pending_scan_mode.is_none() {
return Err(nb::Error::Other(Error::Adc(AdcError::NoPendingOperation)));
} else if self.cfg.pending_scan_mode != Some(ScanMode::ConvertChannelNOnce) {
return Err(nb::Error::Other(Error::Adc(AdcError::PendingOperation)));
}
let is_low = match eoc_pin.is_low() {
Ok(low) => low,
Err(e) => {
return Err(nb::Error::Other(Error::Pin(e)));
}
};
if is_low {
let mut dummy_cmd: [u8; 2] = [0; 2];
self.cs.set_low().map_err(Error::Pin)?;
let transfer_result = self.spi.transfer(&mut dummy_cmd);
self.cs.set_high().map_err(Error::Pin)?;
if eoc.is_low().map_err(Error::Pin)? {
let mut reply_buf: [u8; 2] = [0; 2];
let transfer_result = self.spi.read(&mut reply_buf);
match transfer_result {
Ok(reply) => {
self.pending_op = PendingOp::None;
Ok(((reply[0] as u16) << 6) | (reply[1] as u16 >> 2))
Ok(_) => {
self.cfg.pending_scan_mode = None;
Ok(((reply_buf[0] as u16) << 6) | (reply_buf[1] as u16 >> 2))
}
Err(e) => Err(nb::Error::Other(Error::Spi(e))),
}
@ -339,33 +485,68 @@ where
}
}
impl<SpiE, PinE, SPI, CS, MAX: HasChannels>
Max116xx10Bit<SPI, CS, MAX, ExternallyClocked, WithoutWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
macro_rules! ext_impl {
() => {
/// Set up the ADC depending on clock and reference configuration
#[inline]
pub fn setup(&mut self) -> Result<(), Error<Spi::Error, Infallible>> {
self.base.send_wrapper(self.base.get_setup_byte())
}
/// Set up the Averaging register. This sets the AVGON, NAVG1, NAVG0, NSCAN1 and NSCAN0
/// bits accordingly
#[inline]
pub fn averaging(
&mut self,
avg_conv: AveragingConversions,
avg_res: AveragingResults,
) -> Result<(), Error<Spi::Error, Infallible>> {
self.base.cfg.results_len = Max116xx10Bit::<Spi, Clocked>::get_results_len(avg_res);
self.base
.send_wrapper(Max116xx10Bit::<Spi, Clocked>::get_averaging_byte(
avg_conv, avg_res,
))
}
#[inline]
pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error<Spi::Error, Infallible>> {
let mut reset_byte = 0b0001_0000;
if fifo_only {
reset_byte |= 1 << 3;
}
self.base.send_wrapper(reset_byte)
}
};
}
impl<Spi: SpiDevice, Eoc, Clocked: ClockedProvider> Max116xx10BitEocExt<Spi, Eoc, Clocked> {
ext_impl!();
}
impl<Spi: SpiDevice, EOC, CNVST, Clocked: ClockedProvider, DELAY>
Max116xx10BitCnvstEocExt<Spi, EOC, CNVST, Clocked, DELAY>
{
ext_impl!();
}
//==================================================================================================
// External SPI clock used
//==================================================================================================
/// Implementations when using the external SPI clock to time the conversions
impl<Spi: SpiDevice> Max116xx10Bit<Spi, ExtClkd, WithoutWakeupDelay> {
pub fn read_single_channel(
&mut self,
buf: &mut [u8],
channel_num: u8,
) -> Result<u16, Error<SpiE, PinE>> {
) -> Result<u16, Error<Spi::Error, Infallible>> {
if buf.len() < 3 {
return Err(Error::Adc(AdcError::CmdBufTooSmall));
}
match Self::get_conversion_byte(ScanMode::ConvertChannelNOnce, channel_num) {
Ok(byte) => buf[0] = byte,
Err(e) => {
return Err(Error::Adc(e));
}
};
buf[0] = self.get_conversion_byte(ScanMode::ConvertChannelNOnce, channel_num)?;
buf[1] = 0x00;
buf[2] = 0x00;
self.cs.set_low().map_err(Error::Pin)?;
let reply = self.spi.transfer(&mut buf[0..3]).ok().unwrap();
self.cs.set_high().map_err(Error::Pin)?;
Ok(((reply[1] as u16) << 6) | (reply[2] as u16 >> 2))
self.spi.transfer_in_place(&mut buf[0..3]).ok().unwrap();
Ok(((buf[1] as u16) << 6) | (buf[2] as u16 >> 2))
}
pub fn read_multiple_channels_0_to_n(
@ -373,24 +554,21 @@ where
buf: &mut [u8],
result_iter: &mut IterMut<u16>,
n: u8,
) -> Result<(), Error<SpiE, PinE>> {
) -> Result<(), Error<Spi::Error, Infallible>> {
let mut iter = buf.iter_mut();
let mut next_byte: &mut u8;
for idx in 0..n + 1 {
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = Self::get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?;
*next_byte = self.get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?;
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
}
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
self.cs.set_low().map_err(Error::Pin)?;
let reply = self
.spi
.transfer(&mut buf[0..((n + 1) * 2 + 1) as usize])
self.spi
.transfer_in_place(&mut buf[0..((n + 1) * 2 + 1) as usize])
.map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Pin)?;
let mut reply_iter = reply.iter();
let mut reply_iter = buf.iter();
// Skip first reply byte
reply_iter.next().unwrap();
for _ in 0..n + 1 {
@ -408,28 +586,25 @@ where
buf: &mut [u8],
result_iter: &mut IterMut<u16>,
n: u8,
) -> Result<(), Error<SpiE, PinE>> {
) -> Result<(), Error<Spi::Error, Infallible>> {
let mut iter = buf.iter_mut();
let mut next_byte: &mut u8;
if n > MAX::NUM - 1 {
if n > self.cfg.max_channels - 1 {
return Err(Error::Adc(AdcError::InvalidChannel));
}
let conversions = MAX::NUM - n;
for idx in n..MAX::NUM {
let conversions = self.cfg.max_channels - n;
for idx in n..self.cfg.max_channels {
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = Self::get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?;
*next_byte = self.get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?;
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
}
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
self.cs.set_low().map_err(Error::Pin)?;
let reply = self
.spi
.transfer(&mut buf[0..(conversions * 2 + 1) as usize])
self.spi
.transfer_in_place(&mut buf[0..(conversions * 2 + 1) as usize])
.map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Pin)?;
let mut reply_iter = reply.iter();
let mut reply_iter = buf.iter();
// Skip first reply byte
reply_iter.next().unwrap();
for _ in 0..conversions {
@ -443,6 +618,273 @@ where
}
}
/// Implementations when using the external SPI clock to time the conversions but also requiring
/// a wakeup delay
impl<Spi: SpiDevice> Max116xx10Bit<Spi, ExtClkd, WithWakeupDelay> {
pub fn read_single_channel<Delay: DelayNs>(
&mut self,
buf: &mut [u8],
channel_num: u8,
delay: &mut Delay,
) -> Result<u16, Error<Spi::Error, Infallible>> {
if buf.len() < 3 {
return Err(Error::Adc(AdcError::CmdBufTooSmall));
}
buf[0] = self
.get_conversion_byte(ScanMode::ConvertChannelNOnce, channel_num)
.map_err(Error::Adc)?;
self.send_wrapper(buf[0])?;
delay.delay_us(65);
buf[0] = 0x00;
buf[1] = 0x00;
self.spi
.transfer_in_place(&mut buf[0..2])
.map_err(Error::Spi)?;
Ok(((buf[0] as u16) << 6) | (buf[1] as u16 >> 2))
}
pub fn read_multiple_channels_0_to_n<Delay: DelayNs>(
&mut self,
buf: &mut [u8],
result_iter: &mut IterMut<u16>,
n: u8,
delay: &mut Delay,
) -> Result<(), Error<Spi::Error, Infallible>> {
let mut iter = buf.iter_mut();
let mut next_byte: &mut u8;
for idx in 0..n + 1 {
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = self.get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?;
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
}
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
// Write first conversion byte and then wait 65 us to allow internal reference to power up
self.send_wrapper(buf[0])?;
delay.delay_us(65);
self.spi
.transfer_in_place(&mut buf[1..((n + 1) * 2 + 1) as usize])
.map_err(Error::Spi)?;
let mut reply_iter = buf.iter();
for _ in 0..n + 1 {
let next_res = result_iter
.next()
.ok_or(Error::Adc(AdcError::ResulBufTooSmall))?;
*next_res = ((*reply_iter.next().unwrap() as u16) << 6)
| (*reply_iter.next().unwrap() as u16 >> 2);
}
Ok(())
}
pub fn read_multiple_channels_n_to_highest<Delay: DelayNs>(
&mut self,
buf: &mut [u8],
result_iter: &mut IterMut<u16>,
n: u8,
delay: &mut Delay,
) -> Result<(), Error<Spi::Error, Infallible>> {
let mut iter = buf.iter_mut();
let mut next_byte: &mut u8;
if n > self.cfg.max_channels - 1 {
return Err(Error::Adc(AdcError::InvalidChannel));
}
let conversions = self.cfg.max_channels - n;
for idx in n..self.cfg.max_channels {
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = self.get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?;
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
}
next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?;
*next_byte = 0x00;
// Write first conversion byte and then wait 65 us with CS high to allow internal
// reference to power up
self.send_wrapper(buf[0])?;
delay.delay_us(65);
self.spi
.transfer_in_place(&mut buf[1..(conversions * 2 + 1) as usize])
.map_err(Error::Spi)?;
let mut reply_iter = buf.iter();
for _ in 0..conversions {
let next_res = result_iter
.next()
.ok_or(Error::Adc(AdcError::ResulBufTooSmall))?;
*next_res = ((*reply_iter.next().unwrap() as u16) << 6)
| (*reply_iter.next().unwrap() as u16 >> 2);
}
Ok(())
}
}
//==================================================================================================
// Internal clock, EOC pin used
//==================================================================================================
/// Implementations when using the internal clock with a conversion started
/// through the serial interface
impl<Spi: SpiDevice, Eoc: InputPin> Max116xx10BitEocExt<Spi, Eoc, IntClkdIntTmdSerIF> {
#[inline]
fn request_wrapper(
&mut self,
channel_num: u8,
scan_mode: ScanMode,
) -> Result<(), Error<Spi::Error, Infallible>> {
if self.base.cfg.pending_scan_mode.is_some() {
return Err(Error::Adc(AdcError::PendingOperation));
}
let conv_byte = self
.base
.get_conversion_byte(scan_mode, channel_num)
.map_err(Error::Adc)?;
self.base.send_wrapper(conv_byte).ok();
self.base.cfg.pending_scan_mode = Some(scan_mode);
Ok(())
}
pub fn request_single_channel(
&mut self,
channel_num: u8,
) -> Result<(), Error<Spi::Error, Infallible>> {
self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce)
}
/// Request a channel repeatedly, using scan mode 10. The number of scans is determined
/// by the averaging register NSCAN0 and NSCAN1 configuration which can be configured
/// with the [`averaging`](Max116xx10Bit::averaging) function
pub fn request_channel_n_repeatedly(
&mut self,
channel_num: u8,
) -> Result<(), Error<Spi::Error, Infallible>> {
self.request_wrapper(channel_num, ScanMode::ScanChannelNRepeatedly)
}
pub fn request_multiple_channels_0_to_n(
&mut self,
n: u8,
) -> Result<(), Error<Spi::Error, Infallible>> {
self.base.cfg.requested_conversions = n as usize + 1;
self.request_wrapper(n, ScanMode::Scan0ToChannelN)
}
pub fn request_multiple_channels_n_to_highest(
&mut self,
n: u8,
) -> Result<(), Error<Spi::Error, Infallible>> {
self.base.cfg.requested_conversions = self.base.cfg.max_channels as usize + 1 - n as usize;
self.request_wrapper(n, ScanMode::ScanChannelNToHighest)
}
/// This function is used to retrieve the results for a single byte request. The EOC pin
/// needs to be passed explicitely here.
/// If no request was made, [AdcError::NoPendingOperation] is returned.
/// If a request was made for multipel results, [AdcError::PendingOperation] will be returned.
pub fn get_single_channel(&mut self) -> nb::Result<u16, Error<Spi::Error, Eoc::Error>> {
self.base.internal_read_single_channel(&mut self.eoc)
}
/// This function is used to retrieve the results for all functions requesting multiple
/// bytes. If no request was made, [AdcError::NoPendingOperation] is returned.
/// If a request was made for a single channel, [AdcError::PendingOperation] will be returned.
pub fn get_multi_channel(
&mut self,
result_iter: &mut IterMut<u16>,
) -> nb::Result<(), Error<Spi::Error, Eoc::Error>> {
if self.base.cfg.pending_scan_mode.is_none() {
return Err(nb::Error::Other(Error::Adc(AdcError::NoPendingOperation)));
} else if self.base.cfg.pending_scan_mode == Some(ScanMode::ConvertChannelNOnce) {
return Err(nb::Error::Other(Error::Adc(AdcError::PendingOperation)));
}
if self.eoc.is_low().map_err(Error::Pin)? {
// maximum length of reply is 32 for 16 channels
let mut reply_buf: [u8; 32] = [0; 32];
let num_conv: usize =
if self.base.cfg.pending_scan_mode == Some(ScanMode::ScanChannelNRepeatedly) {
self.base.cfg.results_len as usize
} else {
self.base.cfg.requested_conversions
};
self.base.cfg.pending_scan_mode = None;
self.base.cfg.requested_conversions = 0;
self.base
.spi
.read(&mut reply_buf[0..(num_conv * 2)])
.map_err(Error::Spi)?;
let mut reply_iter = reply_buf.iter();
for _ in 0..num_conv {
let next_res = result_iter
.next()
.ok_or(Error::Adc(AdcError::ResulBufTooSmall))?;
*next_res = ((*reply_iter.next().unwrap() as u16) << 6)
| (*reply_iter.next().unwrap() as u16 >> 2);
}
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
//==================================================================================================
// Internal clock, CNVST and EOC pin used
//==================================================================================================
/// Implementations when using the internal clock where CNVST is held low for the duration
/// of the conversion
///
/// TODO: Implement. Unfortunately, the test board used to verify this library did not have
/// the CNVST connected, so I wouldn't be able to test an implementation easily.
impl<Spi: SpiDevice, Eoc: InputPin, Cnvst: OutputPin>
Max116xx10BitCnvstEocExt<Spi, Eoc, Cnvst, IntClkdExtTmdCnvst, WithoutWakeupDelay>
{
}
/// Implementations when using the internal clock where CNVST is only pulsed to start acquisition
/// and conversion
///
/// TODO: Test. Unfortunately, the test board used to verify this library did not have
/// the CNVST connected, so I wouldn't be able to test an implementation easily.
impl<Spi: SpiDevice, Eoc: InputPin<Error = PinE>, Cnvst: OutputPin<Error = PinE>, PinE>
Max116xx10BitCnvstEocExt<Spi, Eoc, Cnvst, IntClkdIntTmdCnvst, WithWakeupDelay>
{
/// The pulse needs to be at least 40ns. A pulse cycle value can be used to increase
/// the width of the pulse
pub fn request_single_channel(
&mut self,
channel_num: u8,
pulse_cycles: u8,
) -> Result<(), Error<Spi::Error, PinE>> {
self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce, pulse_cycles)
}
#[inline]
fn request_wrapper(
&mut self,
channel_num: u8,
scan_mode: ScanMode,
pulse_cycles: u8,
) -> Result<(), Error<Spi::Error, PinE>> {
if self.base.cfg.pending_scan_mode.is_some() {
return Err(Error::Adc(AdcError::PendingOperation));
}
let conv_byte = self
.base
.get_conversion_byte(scan_mode, channel_num)
.map_err(Error::Adc)?;
self.base.send_wrapper(conv_byte).ok();
self.cnvst.set_low().map_err(Error::Pin)?;
for _ in 0..pulse_cycles {}
self.cnvst.set_high().map_err(Error::Pin)?;
self.base.cfg.pending_scan_mode = Some(scan_mode);
Ok(())
}
pub fn get_single_channel(&mut self) -> nb::Result<u16, Error<Spi::Error, PinE>> {
self.base.internal_read_single_channel(&mut self.eoc)
}
}
mod private {
pub trait Sealed {}
}