Initial proof of concept
This commit is contained in:
14
drivers/ads1256/types/Cargo.toml
Normal file
14
drivers/ads1256/types/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "ads1256-types"
|
||||
description = "ADS1256 data types."
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies.physical]
|
||||
path = "../../.."
|
||||
[dependencies.defmt]
|
||||
workspace = true
|
||||
optional = true
|
304
drivers/ads1256/types/src/constants.rs
Normal file
304
drivers/ads1256/types/src/constants.rs
Normal file
@ -0,0 +1,304 @@
|
||||
pub const REFERENCE_VOLTS: f32 = 2.5;
|
||||
pub const MAX_CONVERSION_VALUE: i32 = 8_388_607;
|
||||
|
||||
pub mod defaults {
|
||||
//----- Public ----------------------------------
|
||||
pub const SPI_CLK_HZ: u32 = 1_920_000;
|
||||
}
|
||||
|
||||
pub mod opcodes {
|
||||
/// Completes SYNC and Exits Standby Mode.
|
||||
pub const WAKEUP: u8 = 0b00000000;
|
||||
/// Read Data.
|
||||
pub const RDATA: u8 = 0b00000001;
|
||||
/// Read Data Continuously.
|
||||
pub const RDATAC: u8 = 0b00000011;
|
||||
/// Stop Read Data Continuously
|
||||
pub const SDATAC: u8 = 0b00001111;
|
||||
/// Read from register at rrrr. The command is only the first 4 bits, the last 4 bits need to be
|
||||
/// changed to the register address.
|
||||
pub const RREG: u8 = 0b0001_0000;
|
||||
/// Write to register at rrrr. The command is only the first 4 bits, the last 4 bits need to be
|
||||
/// changed to the register address.
|
||||
pub const WREG: u8 = 0b0101_0000;
|
||||
/// Offset and Gain Self-Calibration.
|
||||
pub const SELFCAL: u8 = 0b11110000;
|
||||
/// Offset Self-Calibration.
|
||||
pub const SELFOCAL: u8 = 0b11110001;
|
||||
/// Gain Self-Calibration.
|
||||
pub const SELFGCAL: u8 = 0b11110010;
|
||||
/// System Offset Calibration.
|
||||
pub const SYSOCAL: u8 = 0b11110011;
|
||||
/// System Gain Calibration.
|
||||
pub const SYSGCAL: u8 = 0b11110100;
|
||||
/// Synchronize the A/D Conversion.
|
||||
pub const SYNC: u8 = 0b11111100;
|
||||
/// Begin Standby Mode.
|
||||
pub const STANDBY: u8 = 0b11111101;
|
||||
/// Reset to Power-Up Values.
|
||||
pub const RESET: u8 = 0b11111110;
|
||||
}
|
||||
|
||||
/// Status register.
|
||||
pub mod status {
|
||||
/// Address of the STATUS register.
|
||||
pub const ADDRESS: u8 = 0b0000_0000;
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Buffer {
|
||||
Enabled = 0b000000_1_0,
|
||||
Disabled = 0,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub const MASK: u8 = 0b000000_1_0;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum AutoCal {
|
||||
Enabled = 0b00000_1_00,
|
||||
Disabled = 0,
|
||||
}
|
||||
|
||||
impl AutoCal {
|
||||
pub const MASK: u8 = 0b00000_1_00;
|
||||
}
|
||||
|
||||
/// Input data is always shifted in most significant byte and bit first. Output data is always
|
||||
/// shifted out most significant byte first. The [BitOrder] only controls the bit order of the
|
||||
/// output data within the byte.
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum BitOrder {
|
||||
/// Most significant bit first.
|
||||
MostSigFirst = 0,
|
||||
/// Least significant bit first.
|
||||
LeastSigFirst = 0b0000_1_000,
|
||||
}
|
||||
|
||||
impl BitOrder {
|
||||
pub const MASK: u8 = 0b0000_1_000;
|
||||
}
|
||||
}
|
||||
|
||||
/// Input multiplexer control register.
|
||||
pub mod mux {
|
||||
/// address of the MUX register.
|
||||
pub const ADDRESS: u8 = 0b0000_0001;
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum MuxInput {
|
||||
AIn0 = 0b0000_0000,
|
||||
AIn1 = 0b0000_0001,
|
||||
AIn2 = 0b0000_0010,
|
||||
AIn3 = 0b0000_0011,
|
||||
AIn4 = 0b0000_0100,
|
||||
AIn5 = 0b0000_0101,
|
||||
AIn6 = 0b0000_0110,
|
||||
AIn7 = 0b0000_0111,
|
||||
Common = 0b0000_1000,
|
||||
}
|
||||
|
||||
impl MuxInput {
|
||||
pub const NEGATIVE_MASK: u8 = 0b0000_1111;
|
||||
pub const POSITIVE_MASK: u8 = 0b1111_0000;
|
||||
}
|
||||
}
|
||||
|
||||
/// A/D control register.
|
||||
pub mod adcon {
|
||||
/// Address of the ADCON register.
|
||||
pub const ADDRESS: u8 = 0b0000_0010;
|
||||
|
||||
//TODO: Fitting the value and encoding in 16 bits can probably be done more cleanly if enum
|
||||
// variants become types in a later version of Rust.
|
||||
/// **Warning:** casting to an integer will likely not yield expected result. Use `value()` or
|
||||
/// `encoding()` methods instead.
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Gain {
|
||||
X1 = 0b00000_000,
|
||||
X2 = 0b00000_001,
|
||||
X4 = 0b00000_010,
|
||||
X8 = 0b00000_011,
|
||||
X16 = 0b00000_100,
|
||||
X32 = 0b00000_101,
|
||||
X64 = 0b00000_110,
|
||||
}
|
||||
|
||||
impl Gain {
|
||||
/// Value to use binary & operator on the full ADCON byte to isolate the gain.
|
||||
pub const MASK: u8 = 0b00000_111;
|
||||
pub const ALT_X64: u8 = 0b00000_111;
|
||||
|
||||
/// The integer value of this [Gain].
|
||||
///
|
||||
/// ```
|
||||
/// use ads1256_types::Gain;
|
||||
///
|
||||
/// assert_eq!(Gain::X1.value(), 1);
|
||||
/// assert_eq!(Gain::X2.value(), 2);
|
||||
/// assert_eq!(Gain::X4.value(), 4);
|
||||
/// assert_eq!(Gain::X8.value(), 8);
|
||||
/// assert_eq!(Gain::X16.value(), 16);
|
||||
/// assert_eq!(Gain::X32.value(), 32);
|
||||
/// assert_eq!(Gain::X64.value(), 64);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub const fn value(self) -> u8 {
|
||||
// Multiply 1 by 2 the number of times specified in the encoding (e.g. for gain of 32,
|
||||
// (1 * 2) 5 times (5 is the encoding for 32) is 32).
|
||||
1 << (self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sensor detect current sources.
|
||||
/// For testing that sensor is still connected and able to pass current.
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Sdcs {
|
||||
Off = 0b000_00_000,
|
||||
/// 0.5µA
|
||||
C05 = 0b000_01_000,
|
||||
/// 2µA
|
||||
C2 = 0b000_10_000,
|
||||
/// 10µA
|
||||
C10 = 0b000_11_000,
|
||||
}
|
||||
|
||||
impl Sdcs {
|
||||
/// Value to use binary & operator on the full ADCON byte to isolate the SDCS.
|
||||
pub const MASK: u8 = 0b000_11_000;
|
||||
}
|
||||
|
||||
/// Ads1256 master clock cycle frequency outputted through GPIO.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum ClockOut {
|
||||
Off = 0b0_00_00000,
|
||||
/// Equal to clock in frequency.
|
||||
ClkIn = 0b0_01_00000,
|
||||
/// Equal to clock in frequency / 2.
|
||||
ClkIn_2 = 0b0_10_00000,
|
||||
/// Equal to clock in frequency / 4.
|
||||
ClkIn_4 = 0b0_11_00000,
|
||||
}
|
||||
|
||||
impl ClockOut {
|
||||
/// Value to use binary & operator on the full ADCON byte to isolate the clock out
|
||||
/// frequency.
|
||||
pub const MASK: u8 = 0b0_11_00000;
|
||||
}
|
||||
}
|
||||
|
||||
/// A/D data rate register.
|
||||
pub mod drate {
|
||||
/// Address of the DRATE register.
|
||||
pub const ADDRESS: u8 = 0b0000_0011;
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum DataRate {
|
||||
Sps2_5 = 0b00000011,
|
||||
Sps5 = 0b00010011,
|
||||
Sps10 = 0b00100011,
|
||||
Sps15 = 0b00110011,
|
||||
Sps25 = 0b01000011,
|
||||
Sps30 = 0b01010011,
|
||||
Sps50 = 0b01100011,
|
||||
Sps60 = 0b01110010,
|
||||
Sps100 = 0b10000010,
|
||||
Sps500 = 0b10010010,
|
||||
Sps1000 = 0b10100001,
|
||||
Sps2000 = 0b10110000,
|
||||
Sps3750 = 0b11000000,
|
||||
Sps7500 = 0b11010000,
|
||||
Sps15000 = 0b11100000,
|
||||
Sps30000 = 0b11110000,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod gpio {
|
||||
/// Address of the IO register.
|
||||
pub const ADDRESS: u8 = 0b0000_0100;
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum DioDirection {
|
||||
Output = 0,
|
||||
Input = 1,
|
||||
}
|
||||
|
||||
impl DioDirection {
|
||||
pub const ALL_MASK: u8 = 0b1111_0000;
|
||||
pub const IO0_MASK: u8 = 0b10000;
|
||||
pub const IO1_MASK: u8 = 0b100000;
|
||||
pub const IO2_MASK: u8 = 0b1000000;
|
||||
pub const IO3_MASK: u8 = 0b10000000;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum DState {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
impl DState {
|
||||
pub const ALL_MASK: u8 = 0b0000_1111;
|
||||
pub const IO0_MASK: u8 = 0b1;
|
||||
pub const IO1_MASK: u8 = 0b10;
|
||||
pub const IO2_MASK: u8 = 0b100;
|
||||
pub const IO3_MASK: u8 = 0b1000;
|
||||
}
|
||||
}
|
||||
|
||||
/// Offset calibration byte 0.
|
||||
pub mod ofc0 {
|
||||
/// Address of the OFC0 register.
|
||||
pub const ADDRESS: u8 = 0b0000_0101;
|
||||
}
|
||||
|
||||
/// Offset calibration byte 1.
|
||||
pub mod ofc1 {
|
||||
/// Address of the OFC1 register.
|
||||
pub const ADDRESS: u8 = 0b0000_0110;
|
||||
}
|
||||
|
||||
/// Offset calibration byte 2.
|
||||
pub mod ofc2 {
|
||||
/// Address of the OFC2 register.
|
||||
pub const ADDRESS: u8 = 0b0000_0111;
|
||||
}
|
||||
|
||||
/// Full scale calibration byte 0
|
||||
pub mod fsc0 {
|
||||
/// Address of the FSC0 register.
|
||||
pub const ADDRESS: u8 = 0b0000_1000;
|
||||
}
|
||||
|
||||
/// Full scale calibration byte 0
|
||||
pub mod fsc1 {
|
||||
/// Address of the FSC1 register.
|
||||
pub const ADDRESS: u8 = 0b0000_1001;
|
||||
}
|
||||
|
||||
/// Full scale calibration byte 0
|
||||
pub mod fsc2 {
|
||||
/// Address of the FSC2 register.
|
||||
pub const ADDRESS: u8 = 0b0000_1010;
|
||||
}
|
34
drivers/ads1256/types/src/conversion.rs
Normal file
34
drivers/ads1256/types/src/conversion.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::{Gain, MAX_CONVERSION_VALUE, REFERENCE_VOLTS};
|
||||
use physical::quantity::Volts;
|
||||
|
||||
/// Raw digital value resulting from the conversion of an analog signal by the ADS1256.
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[repr(transparent)]
|
||||
pub struct Conversion(pub i32);
|
||||
|
||||
impl Conversion {
|
||||
/// Process the conversion byte reading and return the raw conversion value.
|
||||
#[inline]
|
||||
pub fn from_reading(reading: [u8; 3]) -> Self {
|
||||
let lsb = reading[2] as i32;
|
||||
let mb = reading[1] as i32;
|
||||
let msb = (reading[0] as i8) as i32; // double cast for sign extension
|
||||
|
||||
Self((msb << 16) | (mb << 8) | lsb)
|
||||
}
|
||||
|
||||
pub fn to_voltage(self, gain: Gain) -> Volts<f32> {
|
||||
let volts = ((2.0 * REFERENCE_VOLTS) / (MAX_CONVERSION_VALUE as f32))
|
||||
* (self.0 as f32 / gain.value() as f32);
|
||||
|
||||
Volts(volts)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Conversion {
|
||||
#[inline(always)]
|
||||
fn from(value: i32) -> Self {
|
||||
Conversion(value)
|
||||
}
|
||||
}
|
15
drivers/ads1256/types/src/lib.rs
Normal file
15
drivers/ads1256/types/src/lib.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#![no_std]
|
||||
|
||||
mod constants;
|
||||
mod registers;
|
||||
pub mod standard;
|
||||
mod conversion;
|
||||
|
||||
pub use crate::constants::adcon::{ClockOut, Gain, Sdcs};
|
||||
pub use crate::constants::drate::DataRate;
|
||||
pub use crate::constants::gpio::{DState, DioDirection};
|
||||
pub use crate::constants::mux::MuxInput;
|
||||
pub use crate::constants::status::{AutoCal, BitOrder, Buffer};
|
||||
pub use crate::constants::*;
|
||||
pub use crate::registers::*;
|
||||
pub use crate::conversion::*;
|
645
drivers/ads1256/types/src/registers.rs
Normal file
645
drivers/ads1256/types/src/registers.rs
Normal file
@ -0,0 +1,645 @@
|
||||
use crate::{
|
||||
ofc0, opcodes, AutoCal, BitOrder, Buffer, ClockOut, DState, DataRate, DioDirection, Gain,
|
||||
MuxInput, Sdcs,
|
||||
};
|
||||
use core::mem;
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Calibration ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct OffsetCalibration(pub [u8; 3]);
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct GainCalibration(pub [u8; 3]);
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct AllCalibration {
|
||||
pub offset: OffsetCalibration,
|
||||
pub gain: GainCalibration,
|
||||
}
|
||||
|
||||
impl From<[u8; 6]> for AllCalibration {
|
||||
/// This array must be formatted as offset cal bytes [0..2] followed by gain cal bytes [0..2]
|
||||
/// for resulting [AllCalibration] to be meaningful.
|
||||
#[inline(always)]
|
||||
fn from(array: [u8; 6]) -> Self {
|
||||
unsafe { mem::transmute::<[u8; 6], Self>(array) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 6]> for AllCalibration {
|
||||
#[inline(always)]
|
||||
fn into(self) -> [u8; 6] {
|
||||
unsafe { mem::transmute::<Self, [u8; 6]>(self) }
|
||||
}
|
||||
}
|
||||
|
||||
/// The complete formatted command to write all calibration values to ADS1256 registers.
|
||||
/// Prepending the write command bytes to the calibration value is a fairly costly operation and in
|
||||
/// practice the only real use of calibration values is to store them and write them to the ADS1256.
|
||||
/// When multiplexing inputs we store calibration infrequently and write it frequently so it's
|
||||
/// better to take the overhead of prepending the command bytes when we store, not when we write.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct CalibrationCommand {
|
||||
command: [u8; 2],
|
||||
calibration: AllCalibration,
|
||||
}
|
||||
|
||||
impl From<AllCalibration> for CalibrationCommand {
|
||||
fn from(calibration: AllCalibration) -> Self {
|
||||
Self {
|
||||
command: [opcodes::WREG | ofc0::ADDRESS, 5],
|
||||
calibration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 8]> for CalibrationCommand {
|
||||
#[inline(always)]
|
||||
fn into(self) -> [u8; 8] {
|
||||
unsafe { mem::transmute::<Self, [u8; 8]>(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<&'a [u8]> for &'a CalibrationCommand {
|
||||
#[inline(always)]
|
||||
fn into(self) -> &'a [u8] {
|
||||
unsafe { mem::transmute::<Self, &'a [u8; 8]>(self) }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Config ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Config {
|
||||
pub status: Status,
|
||||
pub multiplexer: Multiplexer,
|
||||
pub ad_control: AdControl,
|
||||
pub data_rate: DataRate,
|
||||
pub digital_io: DigitalIo,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
// We want this internal and are only using it in [Ads1256::read_config].
|
||||
#[inline]
|
||||
pub const fn from_bytes(bytes: [u8; 5]) -> Self {
|
||||
Self {
|
||||
status: Status(bytes[0]),
|
||||
multiplexer: Multiplexer(bytes[1]),
|
||||
ad_control: AdControl(bytes[2]),
|
||||
data_rate: DataRate::from_byte(bytes[3]),
|
||||
digital_io: DigitalIo(bytes[4]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for Config {
|
||||
#[rustfmt::skip]
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"Config {{\n\
|
||||
\t{},\n\
|
||||
\t{},\n\
|
||||
\t{},\n\
|
||||
\tdate_rate: {},\n\
|
||||
\t{}\n\
|
||||
}}",
|
||||
self.status,
|
||||
self.multiplexer,
|
||||
self.ad_control,
|
||||
self.data_rate,
|
||||
self.digital_io
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Status ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Status(pub u8);
|
||||
|
||||
impl Status {
|
||||
/// Creates a new [Status] with all writable properties explicitly set.
|
||||
#[inline(always)]
|
||||
pub const fn setting(buffer: Buffer, auto_calibration: AutoCal, bit_order: BitOrder) -> Status {
|
||||
Status(buffer as u8 | auto_calibration as u8 | bit_order as u8)
|
||||
}
|
||||
|
||||
/// returns a copy of the [Status] with the buffer setting replaced by the [setting] parameter.
|
||||
#[inline(always)]
|
||||
pub const fn with_buffer(self, setting: Buffer) -> Status {
|
||||
let zeroed_setting = self.0 & !Buffer::MASK;
|
||||
Status(zeroed_setting | setting as u8)
|
||||
}
|
||||
|
||||
/// returns a copy of the [Status] with the auto-calibration setting replaced by the [setting]
|
||||
/// parameter.
|
||||
#[inline(always)]
|
||||
pub const fn with_auto_cal(self, setting: AutoCal) -> Status {
|
||||
let zeroed_setting = self.0 & !AutoCal::MASK;
|
||||
Status(zeroed_setting | setting as u8)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn data_ready(self) -> bool {
|
||||
const MASK: u8 = 0b1;
|
||||
unsafe { mem::transmute::<u8, bool>(self.0 & MASK) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn buffer(self) -> Buffer {
|
||||
unsafe { mem::transmute::<u8, Buffer>(self.0 & Buffer::MASK) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn auto_calibration(self) -> AutoCal {
|
||||
unsafe { mem::transmute::<u8, AutoCal>(self.0 & AutoCal::MASK) }
|
||||
}
|
||||
|
||||
/// Data output bit order.
|
||||
///
|
||||
/// Input data is always shifted in most significant byte and bit first. Output data is always
|
||||
/// shifted out most significant byte first. The ORDER bit only controls the bit order of the
|
||||
/// output data within the byte.
|
||||
#[inline(always)]
|
||||
pub const fn data_output_bit_order(self) -> BitOrder {
|
||||
unsafe { mem::transmute::<u8, BitOrder>(self.0 & BitOrder::MASK) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn id(self) -> u8 {
|
||||
self.0 >> 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Status {
|
||||
#[inline(always)]
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for Status {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"Status(data_ready: {}, buffer: {}, auto_calibration: {}, data_output_bit_order: {}, \
|
||||
id: {})",
|
||||
self.data_ready(),
|
||||
self.buffer(),
|
||||
self.auto_calibration(),
|
||||
self.data_output_bit_order(),
|
||||
self.id()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
#[inline(always)]
|
||||
pub const fn is_enabled(self) -> bool {
|
||||
match self {
|
||||
Buffer::Enabled => true,
|
||||
Buffer::Disabled => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AutoCal {
|
||||
#[inline(always)]
|
||||
pub const fn is_enabled(self) -> bool {
|
||||
match self {
|
||||
AutoCal::Enabled => true,
|
||||
AutoCal::Disabled => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Input Multiplexer ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Multiplexer(pub u8);
|
||||
|
||||
impl Multiplexer {
|
||||
#[inline(always)]
|
||||
pub const fn setting(positive_input: MuxInput, negative_input: MuxInput) -> Multiplexer {
|
||||
Multiplexer(((positive_input as u8) << 4) | negative_input as u8)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_positive(self, input: MuxInput) -> Multiplexer {
|
||||
let zeroed_positive = self.0 & !MuxInput::POSITIVE_MASK;
|
||||
Multiplexer(zeroed_positive | ((input as u8) << 4))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_negative(self, input: MuxInput) -> Multiplexer {
|
||||
let zeroed_negative = self.0 & !MuxInput::NEGATIVE_MASK;
|
||||
Multiplexer(zeroed_negative | input as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn positive_input(self) -> MuxInput {
|
||||
const A_IN0: u8 = (MuxInput::AIn0 as u8) << 4;
|
||||
const A_IN1: u8 = (MuxInput::AIn1 as u8) << 4;
|
||||
const A_IN2: u8 = (MuxInput::AIn2 as u8) << 4;
|
||||
const A_IN3: u8 = (MuxInput::AIn3 as u8) << 4;
|
||||
const A_IN4: u8 = (MuxInput::AIn4 as u8) << 4;
|
||||
const A_IN5: u8 = (MuxInput::AIn5 as u8) << 4;
|
||||
const A_IN6: u8 = (MuxInput::AIn6 as u8) << 4;
|
||||
const A_IN7: u8 = (MuxInput::AIn7 as u8) << 4;
|
||||
|
||||
match self.0 & MuxInput::POSITIVE_MASK {
|
||||
A_IN0 => MuxInput::AIn0,
|
||||
A_IN1 => MuxInput::AIn1,
|
||||
A_IN2 => MuxInput::AIn2,
|
||||
A_IN3 => MuxInput::AIn3,
|
||||
A_IN4 => MuxInput::AIn4,
|
||||
A_IN5 => MuxInput::AIn5,
|
||||
A_IN6 => MuxInput::AIn6,
|
||||
A_IN7 => MuxInput::AIn7,
|
||||
_ => MuxInput::Common,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn negative_input(self) -> MuxInput {
|
||||
const A_IN0: u8 = MuxInput::AIn0 as u8;
|
||||
const A_IN1: u8 = MuxInput::AIn1 as u8;
|
||||
const A_IN2: u8 = MuxInput::AIn2 as u8;
|
||||
const A_IN3: u8 = MuxInput::AIn3 as u8;
|
||||
const A_IN4: u8 = MuxInput::AIn4 as u8;
|
||||
const A_IN5: u8 = MuxInput::AIn5 as u8;
|
||||
const A_IN6: u8 = MuxInput::AIn6 as u8;
|
||||
const A_IN7: u8 = MuxInput::AIn7 as u8;
|
||||
|
||||
match self.0 & MuxInput::NEGATIVE_MASK {
|
||||
A_IN0 => MuxInput::AIn0,
|
||||
A_IN1 => MuxInput::AIn1,
|
||||
A_IN2 => MuxInput::AIn2,
|
||||
A_IN3 => MuxInput::AIn3,
|
||||
A_IN4 => MuxInput::AIn4,
|
||||
A_IN5 => MuxInput::AIn5,
|
||||
A_IN6 => MuxInput::AIn6,
|
||||
A_IN7 => MuxInput::AIn7,
|
||||
_ => MuxInput::Common,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Multiplexer {
|
||||
#[inline(always)]
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for Multiplexer {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"Multiplexer(positive_input: {}, negative_input: {})",
|
||||
self.positive_input(),
|
||||
self.negative_input()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- A/D Control ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct AdControl(pub u8);
|
||||
|
||||
impl AdControl {
|
||||
#[inline(always)]
|
||||
pub const fn setting(
|
||||
gain: Gain,
|
||||
sensor_detect_current: Sdcs,
|
||||
clock_out: ClockOut,
|
||||
) -> AdControl {
|
||||
AdControl(gain as u8 | sensor_detect_current as u8 | clock_out as u8)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_gain(self, setting: Gain) -> AdControl {
|
||||
let zeroed_setting = self.0 & !Gain::MASK;
|
||||
AdControl(zeroed_setting | setting as u8)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_sensor_detect_current(self, setting: Sdcs) -> AdControl {
|
||||
let zeroed_setting = self.0 & !Sdcs::MASK;
|
||||
AdControl(zeroed_setting | setting as u8)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_clock_out(self, setting: ClockOut) -> AdControl {
|
||||
let zeroed_setting = self.0 & !ClockOut::MASK;
|
||||
AdControl(zeroed_setting | setting as u8)
|
||||
}
|
||||
|
||||
/// Programmable gain amplifier setting.
|
||||
/// *Note: On the ADS1256 0b110 and 0b111 both represent a gain amplification setting of X64 but
|
||||
/// in this driver it will always be stored as 0b110.*
|
||||
#[inline]
|
||||
pub const fn gain(self) -> Gain {
|
||||
let masked = self.0 & Gain::MASK;
|
||||
if masked == Gain::ALT_X64 {
|
||||
Gain::X64
|
||||
} else {
|
||||
unsafe { mem::transmute::<u8, Gain>(masked) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Sensor detect current sources. For testing that sensor is still connected and able to pass
|
||||
/// current.
|
||||
#[inline(always)]
|
||||
pub const fn sensor_detect_current(self) -> Sdcs {
|
||||
unsafe { mem::transmute::<u8, Sdcs>(self.0 & Sdcs::MASK) }
|
||||
}
|
||||
|
||||
/// Ads1256 master clock cycle frequency outputted through GPIO.
|
||||
#[inline(always)]
|
||||
pub const fn clock_out(self) -> ClockOut {
|
||||
unsafe { mem::transmute::<u8, ClockOut>(self.0 & ClockOut::MASK) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for AdControl {
|
||||
#[inline(always)]
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for AdControl {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"AdControl(gain: {}, sensor_detect_current: {}, clock_out: {})",
|
||||
self.gain(),
|
||||
self.sensor_detect_current(),
|
||||
self.clock_out()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Data Rate ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
impl DataRate {
|
||||
pub const fn from_byte(byte: u8) -> DataRate {
|
||||
const SPS2_5: u8 = DataRate::Sps2_5 as u8;
|
||||
const SPS5: u8 = DataRate::Sps5 as u8;
|
||||
const SPS10: u8 = DataRate::Sps10 as u8;
|
||||
const SPS15: u8 = DataRate::Sps15 as u8;
|
||||
const SPS25: u8 = DataRate::Sps25 as u8;
|
||||
const SPS30: u8 = DataRate::Sps30 as u8;
|
||||
const SPS50: u8 = DataRate::Sps50 as u8;
|
||||
const SPS60: u8 = DataRate::Sps60 as u8;
|
||||
const SPS100: u8 = DataRate::Sps100 as u8;
|
||||
const SPS500: u8 = DataRate::Sps500 as u8;
|
||||
const SPS1000: u8 = DataRate::Sps1000 as u8;
|
||||
const SPS2000: u8 = DataRate::Sps2000 as u8;
|
||||
const SPS3750: u8 = DataRate::Sps3750 as u8;
|
||||
const SPS7500: u8 = DataRate::Sps7500 as u8;
|
||||
const SPS15000: u8 = DataRate::Sps15000 as u8;
|
||||
const SPS30000: u8 = DataRate::Sps30000 as u8;
|
||||
|
||||
match byte {
|
||||
SPS2_5 => DataRate::Sps2_5,
|
||||
SPS5 => DataRate::Sps5,
|
||||
SPS10 => DataRate::Sps10,
|
||||
SPS15 => DataRate::Sps15,
|
||||
SPS25 => DataRate::Sps25,
|
||||
SPS30 => DataRate::Sps30,
|
||||
SPS50 => DataRate::Sps50,
|
||||
SPS60 => DataRate::Sps60,
|
||||
SPS100 => DataRate::Sps100,
|
||||
SPS500 => DataRate::Sps500,
|
||||
SPS1000 => DataRate::Sps1000,
|
||||
SPS2000 => DataRate::Sps2000,
|
||||
SPS3750 => DataRate::Sps3750,
|
||||
SPS7500 => DataRate::Sps7500,
|
||||
SPS15000 => DataRate::Sps15000,
|
||||
SPS30000 => DataRate::Sps30000,
|
||||
_ => panic!("Invalid ADS1256 data rate."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- GPIO ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct DigitalIo(pub u8);
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct DigitalIoState(pub u8);
|
||||
|
||||
impl DigitalIoState {
|
||||
#[inline]
|
||||
pub const fn new(io0: DState, io1: DState, io2: DState, io3: DState) -> DigitalIoState {
|
||||
DigitalIoState(io0 as u8 | ((io1 as u8) << 1) | ((io2 as u8) << 2) | ((io3 as u8) << 3))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn default() -> DigitalIoState {
|
||||
DigitalIoState::new(DState::Low, DState::Low, DState::Low, DState::Low)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for DigitalIoState {
|
||||
#[inline(always)]
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct DigitalIoDirection(pub u8);
|
||||
|
||||
impl DigitalIoDirection {
|
||||
#[inline]
|
||||
pub const fn new(
|
||||
io0: DioDirection,
|
||||
io1: DioDirection,
|
||||
io2: DioDirection,
|
||||
io3: DioDirection,
|
||||
) -> DigitalIoDirection {
|
||||
DigitalIoDirection(
|
||||
((io0 as u8) << 4) | ((io1 as u8) << 5) | ((io2 as u8) << 6) | ((io3 as u8) << 7),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn default() -> DigitalIoDirection {
|
||||
DigitalIoDirection::new(
|
||||
DioDirection::Output,
|
||||
DioDirection::Input,
|
||||
DioDirection::Input,
|
||||
DioDirection::Input,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for DigitalIoDirection {
|
||||
#[inline(always)]
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DigitalIo {
|
||||
#[inline(always)]
|
||||
pub const fn setting(io_state: DigitalIoState, io_direction: DigitalIoDirection) -> DigitalIo {
|
||||
DigitalIo(io_state.0 | io_direction.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_io_state(self, state: DigitalIoState) -> DigitalIo {
|
||||
let zeroed_setting = self.0 & !DState::ALL_MASK;
|
||||
DigitalIo(zeroed_setting | state.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_io_direction(self, direction: DigitalIoDirection) -> DigitalIo {
|
||||
let zeroed_setting = self.0 & !DioDirection::ALL_MASK;
|
||||
DigitalIo(zeroed_setting | direction.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_io0_state(self, state: DState) -> DigitalIo {
|
||||
let zeroed_setting = self.0 & !DState::IO0_MASK;
|
||||
DigitalIo(zeroed_setting | state as u8)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_io1_state(self, state: DState) -> DigitalIo {
|
||||
let zeroed_setting = self.0 & !DState::IO1_MASK;
|
||||
DigitalIo(zeroed_setting | ((state as u8) << 1))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_io2_state(self, state: DState) -> DigitalIo {
|
||||
let zeroed_setting = self.0 & !DState::IO2_MASK;
|
||||
DigitalIo(zeroed_setting | ((state as u8) << 2))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn with_io3_state(self, state: DState) -> DigitalIo {
|
||||
let zeroed_setting = self.0 & !DState::IO3_MASK;
|
||||
DigitalIo(zeroed_setting | ((state as u8) << 3))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io0_state(self) -> DState {
|
||||
match self.0 & DState::IO0_MASK {
|
||||
DState::IO0_MASK => DState::High,
|
||||
_ => DState::Low,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io1_state(self) -> DState {
|
||||
match self.0 & DState::IO1_MASK {
|
||||
DState::IO1_MASK => DState::High,
|
||||
_ => DState::Low,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io2_state(self) -> DState {
|
||||
match self.0 & DState::IO2_MASK {
|
||||
DState::IO2_MASK => DState::High,
|
||||
_ => DState::Low,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io3_state(self) -> DState {
|
||||
match self.0 & DState::IO3_MASK {
|
||||
DState::IO3_MASK => DState::High,
|
||||
_ => DState::Low,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io0_direction(self) -> DioDirection {
|
||||
match self.0 & DioDirection::IO0_MASK {
|
||||
DioDirection::IO0_MASK => DioDirection::Input,
|
||||
_ => DioDirection::Output,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io1_direction(self) -> DioDirection {
|
||||
match self.0 & DioDirection::IO1_MASK {
|
||||
DioDirection::IO1_MASK => DioDirection::Input,
|
||||
_ => DioDirection::Output,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io2_direction(self) -> DioDirection {
|
||||
match self.0 & DioDirection::IO2_MASK {
|
||||
DioDirection::IO2_MASK => DioDirection::Input,
|
||||
_ => DioDirection::Output,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn io3_direction(self) -> DioDirection {
|
||||
match self.0 & DioDirection::IO3_MASK {
|
||||
DioDirection::IO3_MASK => DioDirection::Input,
|
||||
_ => DioDirection::Output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for DigitalIo {
|
||||
#[inline(always)]
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for DigitalIo {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"DigitalIo(io0: {}, {}, io1: {}, {}, io2: {}, {}, io3: {}, {})",
|
||||
self.io0_direction(),
|
||||
self.io0_state(),
|
||||
self.io1_direction(),
|
||||
self.io1_state(),
|
||||
self.io2_direction(),
|
||||
self.io2_state(),
|
||||
self.io3_direction(),
|
||||
self.io3_state()
|
||||
)
|
||||
}
|
||||
}
|
41
drivers/ads1256/types/src/standard.rs
Normal file
41
drivers/ads1256/types/src/standard.rs
Normal file
@ -0,0 +1,41 @@
|
||||
pub mod input {
|
||||
use crate::{Buffer, Config, DataRate, Gain, Multiplexer, MuxInput};
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Differential {
|
||||
AIn0 = Multiplexer::setting(MuxInput::AIn0, MuxInput::AIn1).0,
|
||||
AIn1 = Multiplexer::setting(MuxInput::AIn2, MuxInput::AIn3).0,
|
||||
AIn2 = Multiplexer::setting(MuxInput::AIn4, MuxInput::AIn5).0,
|
||||
AIn3 = Multiplexer::setting(MuxInput::AIn6, MuxInput::AIn7).0,
|
||||
}
|
||||
|
||||
impl Into<Multiplexer> for Differential {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Multiplexer {
|
||||
Multiplexer(self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum SingleEnded {
|
||||
AIn0 = Multiplexer::setting(MuxInput::AIn0, MuxInput::Common).0,
|
||||
AIn1 = Multiplexer::setting(MuxInput::AIn1, MuxInput::Common).0,
|
||||
AIn2 = Multiplexer::setting(MuxInput::AIn2, MuxInput::Common).0,
|
||||
AIn3 = Multiplexer::setting(MuxInput::AIn3, MuxInput::Common).0,
|
||||
AIn4 = Multiplexer::setting(MuxInput::AIn4, MuxInput::Common).0,
|
||||
AIn5 = Multiplexer::setting(MuxInput::AIn5, MuxInput::Common).0,
|
||||
AIn6 = Multiplexer::setting(MuxInput::AIn6, MuxInput::Common).0,
|
||||
AIn7 = Multiplexer::setting(MuxInput::AIn7, MuxInput::Common).0,
|
||||
}
|
||||
|
||||
impl Into<Multiplexer> for SingleEnded {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Multiplexer {
|
||||
Multiplexer(self as u8)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user