Initial proof of concept

This commit is contained in:
Zachary Levy
2025-03-09 12:13:14 -07:00
commit e06e76e46b
55 changed files with 4508 additions and 0 deletions

View 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

View 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;
}

View 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)
}
}

View 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::*;

View 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()
)
}
}

View 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)
}
}
}