Compare commits

...

10 Commits
v0.2.0 ... 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
6 changed files with 222 additions and 249 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,15 @@ 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

View File

@ -1,6 +1,6 @@
[package]
name = "max116xx-10bit"
version = "0.2.0"
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

@ -4,7 +4,7 @@
//!
//! You can create an initial ADC struct by using the [`Max116xx10Bit::max11618`],
//! [`Max116xx10Bit::max11619`], [`Max116xx10Bit::max11620`], [`Max116xx10Bit::max11621`],
//! [`Max116xx10Bit::max11624`] and [`Max116xx10Bit::max11625`] functionsdepending on which device
//! [`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.
@ -21,13 +21,12 @@
//! 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::delay::DelayUs,
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
@ -90,15 +89,15 @@ 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 {}
@ -106,7 +105,7 @@ 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 {}
@ -114,7 +113,7 @@ 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 {}
@ -122,7 +121,7 @@ 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;
@ -132,7 +131,8 @@ type ExtClkd = ExternallyClocked;
//==================================================================================================
/// 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
@ -148,7 +148,8 @@ pub enum ClockMode {
}
/// Voltage reference modes
#[derive(Debug, PartialEq, Clone, Copy)]
#[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,
@ -158,6 +159,7 @@ pub enum VoltageRefMode {
/// 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,
@ -167,7 +169,8 @@ pub enum AveragingConversions {
}
/// Specifies the number of returned result in single scan mode
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AveragingResults {
FourResults = 0b00,
EightResults = 0b01,
@ -175,7 +178,8 @@ pub enum AveragingResults {
SixteenResults = 0b11,
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ScanMode {
Scan0ToChannelN = 0b00,
ScanChannelNToHighest = 0b01,
@ -184,6 +188,7 @@ pub enum ScanMode {
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcError {
InvalidChannel,
InvalidRefMode,
@ -208,6 +213,8 @@ impl<SpiE, PinE> From<AdcError> for Error<SpiE, PinE> {
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct InternalCfg {
clk_mode: ClockMode,
ref_mode: VoltageRefMode,
@ -216,25 +223,25 @@ struct InternalCfg {
results_len: u8,
requested_conversions: usize,
}
//==================================================================================================
// ADC implementation
//==================================================================================================
pub struct Max116xx10Bit<SPI, CS, CLOCKED = ExtClkd, WAKEUP = WithoutWakeupDelay> {
spi: SPI,
cs: CS,
pub struct Max116xx10Bit<Spi, Clocked = ExtClkd, Wakeup = WithoutWakeupDelay> {
spi: Spi,
cfg: InternalCfg,
clocked: PhantomData<CLOCKED>,
delay: PhantomData<WAKEUP>,
clocked: PhantomData<Clocked>,
delay: PhantomData<Wakeup>,
}
pub struct Max116xx10BitEocExt<SPI, CS, EOC, CLOCKED> {
base: Max116xx10Bit<SPI, CS, CLOCKED, WithoutWakeupDelay>,
pub struct Max116xx10BitEocExt<SPI, EOC, CLOCKED> {
base: Max116xx10Bit<SPI, CLOCKED, WithoutWakeupDelay>,
eoc: EOC,
}
pub struct Max116xx10BitCnvstEocExt<SPI, CS, EOC, CNVST, CLOCKED, WAKEUP = WithoutWakeupDelay> {
base: Max116xx10Bit<SPI, CS, CLOCKED, WAKEUP>,
pub struct Max116xx10BitCnvstEocExt<SPI, EOC, CNVST, CLOCKED, WAKEUP = WithoutWakeupDelay> {
base: Max116xx10Bit<SPI, CLOCKED, WAKEUP>,
eoc: EOC,
cnvst: CNVST,
}
@ -243,28 +250,24 @@ pub struct Max116xx10BitCnvstEocExt<SPI, CS, EOC, CNVST, CLOCKED, WAKEUP = Witho
// Generic
//==================================================================================================
impl<SpiE, PinE, CS, SPI> Max116xx10Bit<SPI, CS, ExtClkd, WithoutWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
pub fn max11618(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
Self::new::<Max11618>(spi, cs)
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, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
Self::new::<Max11619>(spi, cs)
pub fn max11619(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11619>(spi)
}
pub fn max11620(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
Self::new::<Max11620>(spi, cs)
pub fn max11620(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11620>(spi)
}
pub fn max11621(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
Self::new::<Max11621>(spi, cs)
pub fn max11621(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11621>(spi)
}
pub fn max11624(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
Self::new::<Max11624>(spi, cs)
pub fn max11624(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
Self::new::<Max11624>(spi)
}
pub fn max11625(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
Self::new::<Max11625>(spi, cs)
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
@ -274,10 +277,9 @@ where
///
/// 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, cs: CS) -> Result<Self, Error<SpiE, PinE>> {
pub fn new<MAX: HasChannels>(spi: Spi) -> Result<Self, Error<Spi::Error, Infallible>> {
let max_dev = Max116xx10Bit {
spi,
cs,
cfg: InternalCfg {
clk_mode: ExtClkd::CLK_SEL,
ref_mode: VoltageRefMode::ExternalSingleEndedNoWakeupDelay,
@ -298,10 +300,9 @@ where
/// The corresponding SETUP register is `0b0111_0000`
pub fn into_ext_clkd_with_int_ref_wakeup_delay(
self,
) -> Max116xx10Bit<SPI, CS, ExtClkd, WithWakeupDelay> {
) -> Max116xx10Bit<Spi, ExtClkd, WithWakeupDelay> {
Max116xx10Bit {
spi: self.spi,
cs: self.cs,
cfg: InternalCfg {
clk_mode: ExtClkd::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithWakeupDelay,
@ -321,10 +322,9 @@ where
/// The corresponding SETUP register is `0b0111_1000`
pub fn into_ext_clkd_with_int_ref_no_wakeup_delay(
self,
) -> Max116xx10Bit<SPI, CS, ExtClkd, WithoutWakeupDelay> {
) -> Max116xx10Bit<Spi, ExtClkd, WithoutWakeupDelay> {
Max116xx10Bit {
spi: self.spi,
cs: self.cs,
cfg: InternalCfg {
clk_mode: ExtClkd::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithoutWakeupDelay,
@ -342,14 +342,13 @@ where
/// 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<Error = PinE>>(
pub fn into_int_clkd_int_timed_through_ser_if_with_wakeup<Eoc: InputPin>(
self,
eoc: EOC,
) -> Max116xx10BitEocExt<SPI, CS, EOC, IntClkdIntTmdSerIF> {
eoc: Eoc,
) -> Max116xx10BitEocExt<Spi, Eoc, IntClkdIntTmdSerIF> {
Max116xx10BitEocExt {
base: Max116xx10Bit {
spi: self.spi,
cs: self.cs,
cfg: InternalCfg {
clk_mode: IntClkdIntTmdSerIF::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithWakeupDelay,
@ -371,18 +370,17 @@ where
/// 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<Error = PinE>>(
pub fn into_int_clkd_int_timed_through_ser_if_without_wakeup<Eoc: InputPin>(
self,
v_ref: VoltageRefMode,
eoc: EOC,
) -> Result<Max116xx10BitEocExt<SPI, CS, EOC, IntClkdIntTmdSerIF>, AdcError> {
eoc: Eoc,
) -> Result<Max116xx10BitEocExt<Spi, Eoc, IntClkdIntTmdSerIF>, AdcError> {
if v_ref == VoltageRefMode::InternalRefWithWakeupDelay {
return Err(AdcError::InvalidRefMode);
}
Ok(Max116xx10BitEocExt {
base: Max116xx10Bit {
spi: self.spi,
cs: self.cs,
cfg: InternalCfg {
clk_mode: IntClkdIntTmdSerIF::CLK_SEL,
ref_mode: VoltageRefMode::InternalRefWithWakeupDelay,
@ -399,22 +397,16 @@ where
}
}
impl<SpiE, PinE, CS, SPI, CLOCKED: Clocked, WAKEUP> Max116xx10Bit<SPI, CS, CLOCKED, WAKEUP>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
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())
}
@ -425,13 +417,13 @@ where
&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(&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;
@ -468,24 +460,22 @@ where
/// Generic function which can be used a single result is available
/// when EOC is low
fn internal_read_single_channel<EOC: InputPin<Error = PinE>>(
fn internal_read_single_channel<Eoc: InputPin>(
&mut self,
eoc: &mut EOC,
) -> nb::Result<u16, Error<SpiE, PinE>> {
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)));
}
if eoc.is_low().map_err(Error::Pin)? {
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)?;
let mut reply_buf: [u8; 2] = [0; 2];
let transfer_result = self.spi.read(&mut reply_buf);
match transfer_result {
Ok(reply) => {
Ok(_) => {
self.cfg.pending_scan_mode = None;
Ok(((reply[0] as u16) << 6) | (reply[1] as u16 >> 2))
Ok(((reply_buf[0] as u16) << 6) | (reply_buf[1] as u16 >> 2))
}
Err(e) => Err(nb::Error::Other(Error::Spi(e))),
}
@ -499,7 +489,7 @@ macro_rules! ext_impl {
() => {
/// 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.base.send_wrapper(self.base.get_setup_byte())
}
@ -510,16 +500,16 @@ macro_rules! ext_impl {
&mut self,
avg_conv: AveragingConversions,
avg_res: AveragingResults,
) -> Result<(), Error<SpiE, PinE>> {
self.base.cfg.results_len = Max116xx10Bit::<SPI, CS, CLOCKED>::get_results_len(avg_res);
) -> Result<(), Error<Spi::Error, Infallible>> {
self.base.cfg.results_len = Max116xx10Bit::<Spi, Clocked>::get_results_len(avg_res);
self.base
.send_wrapper(Max116xx10Bit::<SPI, CS, CLOCKED>::get_averaging_byte(
.send_wrapper(Max116xx10Bit::<Spi, Clocked>::get_averaging_byte(
avg_conv, avg_res,
))
}
#[inline]
pub fn reset(&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;
@ -528,19 +518,12 @@ macro_rules! ext_impl {
}
};
}
impl<SpiE, PinE, CS, SPI, EOC, CLOCKED: Clocked> Max116xx10BitEocExt<SPI, CS, EOC, CLOCKED>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
impl<Spi: SpiDevice, Eoc, Clocked: ClockedProvider> Max116xx10BitEocExt<Spi, Eoc, Clocked> {
ext_impl!();
}
impl<SpiE, PinE, CS, SPI, EOC, CNVST, CLOCKED: Clocked, DELAY>
Max116xx10BitCnvstEocExt<SPI, CS, EOC, CNVST, CLOCKED, DELAY>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
impl<Spi: SpiDevice, EOC, CNVST, Clocked: ClockedProvider, DELAY>
Max116xx10BitCnvstEocExt<Spi, EOC, CNVST, Clocked, DELAY>
{
ext_impl!();
}
@ -550,26 +533,20 @@ where
//==================================================================================================
/// Implementations when using the external SPI clock to time the conversions
impl<SpiE, PinE, SPI, CS> Max116xx10Bit<SPI, CS, ExtClkd, WithoutWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
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));
}
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(
@ -577,7 +554,7 @@ 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 {
@ -588,13 +565,10 @@ where
}
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 {
@ -612,7 +586,7 @@ 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 > self.cfg.max_channels - 1 {
@ -627,13 +601,10 @@ where
}
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 {
@ -649,17 +620,13 @@ where
/// Implementations when using the external SPI clock to time the conversions but also requiring
/// a wakeup delay
impl<SpiE, PinE, SPI, CS> Max116xx10Bit<SPI, CS, ExtClkd, WithWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
{
pub fn read_single_channel<DELAY: DelayUs<u8>>(
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<SpiE, PinE>> {
delay: &mut Delay,
) -> Result<u16, Error<Spi::Error, Infallible>> {
if buf.len() < 3 {
return Err(Error::Adc(AdcError::CmdBufTooSmall));
}
@ -670,19 +637,19 @@ where
delay.delay_us(65);
buf[0] = 0x00;
buf[1] = 0x00;
self.cs.set_low().map_err(Error::Pin)?;
let reply = self.spi.transfer(&mut buf[0..2]).map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Pin)?;
Ok(((reply[0] as u16) << 6) | (reply[1] as u16 >> 2))
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: DelayUs<u8>>(
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<SpiE, PinE>> {
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 {
@ -697,13 +664,10 @@ where
self.send_wrapper(buf[0])?;
delay.delay_us(65);
self.cs.set_low().map_err(Error::Pin)?;
let reply = self
.spi
.transfer(&mut buf[1..((n + 1) * 2 + 1) as usize])
self.spi
.transfer_in_place(&mut buf[1..((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();
for _ in 0..n + 1 {
let next_res = result_iter
.next()
@ -714,13 +678,13 @@ where
Ok(())
}
pub fn read_multiple_channels_n_to_highest<DELAY: DelayUs<u8>>(
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<SpiE, PinE>> {
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 {
@ -739,13 +703,10 @@ where
// reference to power up
self.send_wrapper(buf[0])?;
delay.delay_us(65);
self.cs.set_low().map_err(Error::Pin)?;
let reply = self
.spi
.transfer(&mut buf[1..(conversions * 2 + 1) as usize])
self.spi
.transfer_in_place(&mut buf[1..(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();
for _ in 0..conversions {
let next_res = result_iter
.next()
@ -763,18 +724,13 @@ where
/// Implementations when using the internal clock with a conversion started
/// through the serial interface
impl<SpiE, PinE, SPI, CS, EOC> Max116xx10BitEocExt<SPI, CS, EOC, IntClkdIntTmdSerIF>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
EOC: InputPin<Error = PinE>,
{
impl<Spi: SpiDevice, Eoc: InputPin> Max116xx10BitEocExt<Spi, Eoc, IntClkdIntTmdSerIF> {
#[inline]
fn request_wrapper(
&mut self,
channel_num: u8,
scan_mode: ScanMode,
) -> Result<(), Error<SpiE, PinE>> {
) -> Result<(), Error<Spi::Error, Infallible>> {
if self.base.cfg.pending_scan_mode.is_some() {
return Err(Error::Adc(AdcError::PendingOperation));
}
@ -782,12 +738,15 @@ where
.base
.get_conversion_byte(scan_mode, channel_num)
.map_err(Error::Adc)?;
self.base.send_wrapper(conv_byte)?;
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<SpiE, PinE>> {
pub fn request_single_channel(
&mut self,
channel_num: u8,
) -> Result<(), Error<Spi::Error, Infallible>> {
self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce)
}
@ -797,11 +756,14 @@ where
pub fn request_channel_n_repeatedly(
&mut self,
channel_num: u8,
) -> Result<(), Error<SpiE, PinE>> {
) -> 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<SpiE, PinE>> {
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)
}
@ -809,7 +771,7 @@ where
pub fn request_multiple_channels_n_to_highest(
&mut self,
n: u8,
) -> Result<(), Error<SpiE, PinE>> {
) -> 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)
}
@ -818,7 +780,7 @@ where
/// 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<SpiE, PinE>> {
pub fn get_single_channel(&mut self) -> nb::Result<u16, Error<Spi::Error, Eoc::Error>> {
self.base.internal_read_single_channel(&mut self.eoc)
}
@ -828,7 +790,7 @@ where
pub fn get_multi_channel(
&mut self,
result_iter: &mut IterMut<u16>,
) -> nb::Result<(), Error<SpiE, PinE>> {
) -> 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) {
@ -836,32 +798,28 @@ where
}
if self.eoc.is_low().map_err(Error::Pin)? {
// maximum length of reply is 32 for 16 channels
let mut dummy_cmd: [u8; 32] = [0; 32];
let num_conv: usize;
if self.base.cfg.pending_scan_mode == Some(ScanMode::ScanChannelNRepeatedly) {
num_conv = self.base.cfg.results_len as usize;
} else {
num_conv = self.base.cfg.requested_conversions;
}
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.cs.set_low().map_err(Error::Pin)?;
let transfer_result = self.base.spi.transfer(&mut dummy_cmd[0..(num_conv * 2)]);
self.base.cs.set_high().map_err(Error::Pin)?;
match transfer_result {
Ok(reply) => {
let mut reply_iter = reply.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(())
}
Err(e) => Err(nb::Error::Other(Error::Spi(e))),
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)
}
@ -877,17 +835,9 @@ where
///
/// 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<SpiE, PinE, SPI, CS, EOC, CNVST>
Max116xx10BitCnvstEocExt<SPI, CS, EOC, CNVST, IntClkdExtTmdCnvst, WithoutWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
EOC: InputPin<Error = PinE>,
CNVST: OutputPin<Error = PinE>,
impl<Spi: SpiDevice, Eoc: InputPin, Cnvst: OutputPin>
Max116xx10BitCnvstEocExt<Spi, Eoc, Cnvst, IntClkdExtTmdCnvst, WithoutWakeupDelay>
{
pub fn dummy() {
todo!("Implement this")
}
}
/// Implementations when using the internal clock where CNVST is only pulsed to start acquisition
@ -895,13 +845,8 @@ where
///
/// 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<SpiE, PinE, SPI, CS, EOC, CNVST>
Max116xx10BitCnvstEocExt<SPI, CS, EOC, CNVST, IntClkdIntTmdCnvst, WithWakeupDelay>
where
SPI: Transfer<u8, Error = SpiE> + FullDuplex<u8, Error = SpiE>,
CS: OutputPin<Error = PinE>,
EOC: InputPin<Error = PinE>,
CNVST: OutputPin<Error = PinE>,
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
@ -909,7 +854,7 @@ where
&mut self,
channel_num: u8,
pulse_cycles: u8,
) -> Result<(), Error<SpiE, PinE>> {
) -> Result<(), Error<Spi::Error, PinE>> {
self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce, pulse_cycles)
}
@ -919,7 +864,7 @@ where
channel_num: u8,
scan_mode: ScanMode,
pulse_cycles: u8,
) -> Result<(), Error<SpiE, PinE>> {
) -> Result<(), Error<Spi::Error, PinE>> {
if self.base.cfg.pending_scan_mode.is_some() {
return Err(Error::Adc(AdcError::PendingOperation));
}
@ -927,7 +872,7 @@ where
.base
.get_conversion_byte(scan_mode, channel_num)
.map_err(Error::Adc)?;
self.base.send_wrapper(conv_byte)?;
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)?;
@ -935,7 +880,7 @@ where
Ok(())
}
pub fn get_single_channel(&mut self) -> nb::Result<u16, Error<SpiE, PinE>> {
pub fn get_single_channel(&mut self) -> nb::Result<u16, Error<Spi::Error, PinE>> {
self.base.internal_read_single_channel(&mut self.eoc)
}
}