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,28 @@
[package]
name = "ads1256"
description = "Driver for ADS1256."
version.workspace = true
edition.workspace = true
repository.workspace = true
readme.workspace = true
license.workspace = true
#---------------------------------------------------------------------------------------------------------------------
#----- Dependencies ------------------------
#---------------------------------------------------------------------------------------------------------------------
[dependencies.ads1256-types]
path = "../types"
features = ["defmt"]
[dependencies.physical]
path = "../../.."
[dependencies.physical-node]
path = "../../../node"
[dependencies.embedded-hal]
workspace = true
[dependencies.embedded-hal-async]
workspace = true
[dependencies.defmt]
workspace = true
[dependencies.embassy-sync]
workspace = true
optional = true

View File

@ -0,0 +1,562 @@
use crate::{
drate, mux, opcodes, status, AdControl, Ads1256, BlockingDelay, CalibrationCommand, Conversion,
DataRate, Gain, Multiplexer, Status,
};
use embedded_hal::digital::OutputPin;
use embedded_hal::spi;
use embedded_hal::spi::SpiBus;
use embedded_hal_async::digital::Wait;
use physical_node::GPIO_ERROR_MSG;
use physical_node::spi::{end_spi, end_spi_if_err};
impl<DelayerT, SST, DrdyT> Ads1256<DelayerT, SST, DrdyT>
where
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
{
/// Enter read data continuous mode (wait for drdy low, issue RDATAC command)
#[inline]
pub async fn start_rdatac<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = spi.write(&[opcodes::RDATAC]);
end_spi(&mut self.slave_select, spi, spi_op_result)?;
Ok(())
}
/// Exit read data continuous mode (wait for drdy low, issue SDATAC command)
#[inline]
pub async fn stop_rdatac<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = spi.write(&[opcodes::SDATAC]);
end_spi(&mut self.slave_select, spi, spi_op_result)?;
Ok(())
}
pub async fn read_data<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let mut buffer = [0u8; 3];
let spi_op_result = spi.read(&mut buffer);
end_spi(&mut self.slave_select, spi, spi_op_result)?;
Ok(Conversion::from_reading(buffer))
}
/// Issues RDATA command and receives result, does not handle any housekeeping such as setting
/// slave select or flushing the bus on successful completion.
#[inline]
pub fn raw_cmd_read_data<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
let spi_op_result = spi.write(&[opcodes::RDATA]);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
self.delayer.t6_delay();
let mut buffer = [0u8; 3];
spi.read(&mut buffer)?;
Ok(Conversion::from_reading(buffer))
}
/// Reads the digital conversion value.
/// Action sequence:
/// 1. Wait for data_ready to go low
/// 1. Issue RDATA command
/// 1. Read the conversion value
#[inline]
pub async fn cmd_read_data<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = self.raw_cmd_read_data(spi);
end_spi(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub fn conversion_init<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
// Issue sync command
let spi_op_result = spi.write(&[opcodes::SYNC]);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
// Issue wakeup command
let spi_op_result = spi.write(&[opcodes::WAKEUP]);
end_spi(&mut self.slave_select, spi, spi_op_result)
}
//----- Configure Convert Functions ----------------------------------
/// Function for configuring and reading AD conversions using auto-calibration. If
/// only the inputs are changed and no other configuration, auto-calibration will not be
/// run.
///
/// Action sequence:
/// 1. Switch inputs and optionally adjust different configuration parameters.
/// 1. If only the input was switched without configuration changes.
/// 1. Issue sync command followed by wakeup command
/// 1. Else, auto-calibration will take place
/// 1. Wait for data_ready low
/// 1. RDATA command (read the conversion value)
/// 1. Optionally enter standby mode
///
/// **WARNING:** Auto-calibration must be enabled for intended functionality when changing
/// [Status], [AdControl], or [DataRate]. Furthermore if setting [Status] or [AdControl], their
/// [Buffer] and [Gain] settings must be modified respectively to trigger auto-calibration.
#[inline]
pub async fn autocal_convert<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
status: Option<Status>,
ad_control: Option<AdControl>,
data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
match (status, ad_control, data_rate) {
// Only modifying the multiplexer, not changing any configuration
(None, None, None) => {
self._none_config(spi, input)?;
self._manual_conversion_init(spi)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer) and changing multiplexer
(Some(status), None, None) => {
self._status_config(spi, input, status)?;
self._read_when_rdy(spi, standby).await
},
// Modifying AD control (gain) and changing multiplexer
(None, Some(ad_control), None) => {
self._ad_config(spi, input, ad_control)?;
self._read_when_rdy(spi, standby).await
},
// Modifying data rate and change multiplexer
(None, None, Some(data_rate)) => {
self._drate_config(spi, input, data_rate)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer), AD control (gain), and changing multiplexer
(Some(status), Some(ad_control), None) => {
self._status_ad_config(spi, input, status, ad_control)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer), data rate, and changing multiplexer
(Some(status), None, Some(data_rate)) => {
self._status_drate_config(spi, input, status, data_rate)?;
self._read_when_rdy(spi, standby).await
},
// Modifying AD control (gain), data rate, and changing multiplexer
(None, Some(ad_control), Some(data_rate)) => {
self._ad_drate_config(spi, input, ad_control, data_rate)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer), AD control (gain), data rate, and changing
// multiplexer
(Some(status), Some(ad_control), Some(data_rate)) => {
self._all_config(spi, input, status, ad_control, data_rate)?;
self._read_when_rdy(spi, standby).await
},
}
}
/// Function for periodically configuring and reading AD conversions using stored calibration
/// values. If only the inputs are changed and no other configuration, auto-calibration will
/// not be run. For immediate continuous multiplexing see [Ads1256::loadcal_convert_next].
///
/// Action sequence:
/// 1. Switch inputs and optionally adjust different configuration parameters.
/// 1. Issue sync command followed by wakeup command
/// 1. Wait for data_ready low
/// 1. RDATA command (read the conversion value)
/// 1. Optionally enter standby mode
///
/// **WARNING:** Auto-calibration must be disabled for intended functionality when changing
/// [Status], [AdControl], or [DataRate].
#[inline]
pub async fn loadcal_convert<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
calibration: Option<&CalibrationCommand>,
status: Option<Status>,
ad_control: Option<AdControl>,
data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
match (status, ad_control, data_rate) {
// Only modifying the multiplexer, not changing any configuration
(None, None, None) => {
self._none_config(spi, input)?;
self._manual_conversion_init(spi)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer) and changing multiplexer
(Some(status), None, None) => {
self._status_config(spi, input, status)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
// Modifying AD control (gain) and changing multiplexer
(None, Some(ad_control), None) => {
self._ad_config(spi, input, ad_control)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
// Modifying data rate and change multiplexer
(None, None, Some(data_rate)) => {
self._drate_config(spi, input, data_rate)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer), AD control (gain), and changing multiplexer
(Some(status), Some(ad_control), None) => {
self._status_ad_config(spi, input, status, ad_control)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer), data rate, and changing multiplexer
(Some(status), None, Some(data_rate)) => {
self._status_drate_config(spi, input, status, data_rate)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
// Modifying AD control (gain), data rate, and changing multiplexer
(None, Some(ad_control), Some(data_rate)) => {
self._ad_drate_config(spi, input, ad_control, data_rate)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
// Modifying status (toggle buffer), AD control (gain), data rate, and changing
// multiplexer
(Some(status), Some(ad_control), Some(data_rate)) => {
self._all_config(spi, input, status, ad_control, data_rate)?;
self._loadcal_init(spi, calibration)?;
self._read_when_rdy(spi, standby).await
},
}
}
/// Function for rapidly configuring and reading AD conversions using stored calibration
/// values. If only the inputs are changed and no other configuration, auto-calibration will
/// not be run. For periodic reading see [Ads1256::loadcal_convert].
///
/// Action sequence:
/// 1. Wait for data_ready low
/// 1. RDATA command (read the conversion value)
/// 1. Switch inputs and optionally adjust different configuration parameters.
/// 1. Issue sync command followed by wakeup command
///
/// **WARNING:** Auto-calibration must be disabled for intended functionality when changing
/// [Status], [AdControl], or [DataRate].
#[inline]
pub async fn loadcal_convert_next<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
next_input: Multiplexer,
next_calibration: Option<&CalibrationCommand>,
next_status: Option<Status>,
next_ad_control: Option<AdControl>,
next_data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self._loadcal_convert_next(
spi,
next_input,
next_calibration,
next_status,
next_ad_control,
next_data_rate,
standby,
)
.await
}
#[inline(always)]
pub(crate) async fn _loadcal_convert_next<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
next_input: Multiplexer,
next_calibration: Option<&CalibrationCommand>,
next_status: Option<Status>,
next_ad_control: Option<AdControl>,
next_data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
match (next_status, next_ad_control, next_data_rate) {
// Only modifying the multiplexer, not changing any configuration
(None, None, None) => {
self._none_config(spi, next_input)?;
self._manual_conversion_init(spi)?;
self._read_mux_conversion(spi, standby)
},
// Modifying status (toggle buffer) and changing multiplexer
(Some(status), None, None) => {
self._status_config(spi, next_input, status)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
// Modifying AD control (gain) and changing multiplexer
(None, Some(ad_control), None) => {
self._ad_config(spi, next_input, ad_control)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
// Modifying data rate and change multiplexer
(None, None, Some(data_rate)) => {
self._drate_config(spi, next_input, data_rate)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
// Modifying status (toggle buffer), AD control (gain), and changing multiplexer
(Some(status), Some(ad_control), None) => {
self._status_ad_config(spi, next_input, status, ad_control)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
// Modifying status (toggle buffer), data rate, and changing multiplexer
(Some(status), None, Some(data_rate)) => {
self._status_drate_config(spi, next_input, status, data_rate)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
// Modifying AD control (gain), data rate, and changing multiplexer
(None, Some(ad_control), Some(data_rate)) => {
self._ad_drate_config(spi, next_input, ad_control, data_rate)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
// Modifying status (toggle buffer), AD control (gain), data rate, and changing
// multiplexer
(Some(status), Some(ad_control), Some(data_rate)) => {
self._all_config(spi, next_input, status, ad_control, data_rate)?;
self._loadcal_init(spi, next_calibration)?;
self._read_mux_conversion(spi, standby)
},
}
}
/// Read a pre-defined number of samples. This will use read continuous command, the ADS1256 must be
/// properly configure before calling this function. This uses [BlockingDelay::t11_1_delay] before running so as it's
/// almost always used after configuring the ADS1256 for a specific channel.
pub async fn multi_sample<SpiT: SpiBus, const NUM_SAMPLES: usize>(
&mut self,
spi: &mut SpiT,
) -> Result<[Conversion; NUM_SAMPLES], <SpiT as spi::ErrorType>::Error> {
let mut samples: [Conversion; NUM_SAMPLES] = [0.into(); NUM_SAMPLES];
self.delayer.t11_1_delay();
self.start_rdatac(spi).await?;
for i in 0..NUM_SAMPLES {
samples[i] = self.read_data(spi).await?;
}
self.delayer.t6_delay();
self.stop_rdatac(spi).await?;
Ok(samples)
}
#[inline]
pub(crate) fn _none_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Change inputs
let buffer = [0u8, 0u8, input.0];
let spi_op_result = self.raw_write_registers(spi, mux::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _loadcal_init<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
calibration: Option<&CalibrationCommand>,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.delayer.t11_1_delay();
if let Some(calibration) = calibration {
let spi_op_result = spi.write(calibration.into());
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
}
self._manual_conversion_init(spi)
}
#[inline]
pub(crate) fn _manual_conversion_init<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.delayer.t11_1_delay();
// Since we did not change configuration, ADS1256 will not auto-calibrate so we
// need to sync, wakeup
// Issue sync command
let spi_op_result = spi.write(&[opcodes::SYNC]);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
// Issue wakeup command
let spi_op_result = spi.write(&[opcodes::WAKEUP]);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _status_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
status: Status,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let buffer = [0u8, 0u8, status.0, input.0];
let spi_op_result = self.raw_write_registers(spi, status::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _ad_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
ad_control: AdControl,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let buffer = [0u8, 0u8, input.0, ad_control.0];
let spi_op_result = self.raw_write_registers(spi, mux::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _drate_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
data_rate: DataRate,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Switch inputs
let mut buffer = [0u8, 0u8, input.0];
let spi_op_result = self.raw_write_registers(spi, mux::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
self.delayer.t11_1_delay();
// Modify data rate
buffer[2] = data_rate as u8;
let spi_op_result = self.raw_write_registers(spi, drate::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _status_ad_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
status: Status,
ad_control: AdControl,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let buffer = [0u8, 0u8, status.0, input.0, ad_control.0];
let spi_op_result = self.raw_write_registers(spi, status::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _status_drate_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
status: Status,
data_rate: DataRate,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Modify status and switch inputs
let buffer = [0u8, 0u8, status.0, input.0];
let spi_op_result = self.raw_write_registers(spi, status::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
self.delayer.t11_1_delay();
// Modify data rate
let buffer = [0u8, 0u8, data_rate as u8];
let spi_op_result = self.raw_write_registers(spi, drate::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _ad_drate_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
ad_control: AdControl,
data_rate: DataRate,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let buffer = [0u8, 0u8, input.0, ad_control.0, data_rate as u8];
let spi_op_result = self.raw_write_registers(spi, mux::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub(crate) fn _all_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
status: Status,
ad_control: AdControl,
data_rate: DataRate,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let buffer = [0u8, 0u8, status.0, input.0, ad_control.0, data_rate as u8];
let spi_op_result = self.raw_write_registers(spi, status::ADDRESS, buffer);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
async fn _read_when_rdy<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
// Wait for data ready low
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
// Read data
self._read_mux_conversion(spi, standby)
}
#[inline]
pub(crate) fn _read_mux_conversion<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
// Read data
let spi_op_result = self.raw_cmd_read_data(spi);
let cvalue = end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
// if standby is true enter standby mode
if standby {
let spi_op_result = spi.write(&[opcodes::STANDBY]);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
}
// end SPI
self.slave_select.set_high().expect(GPIO_ERROR_MSG);
spi.flush()?;
Ok(cvalue)
}
}

View File

@ -0,0 +1,44 @@
use embedded_hal::delay::DelayNs;
//TODO: Change to maybe async when available instead of always blocking
pub trait BlockingDelay {
/// Delay between spi write and read in a read data command
/// Total 50 master clock cycles
fn t6_delay(&mut self);
/// Delay after RREG, WREG, RDATA
/// Total 4 master clock cycles
fn t11_1_delay(&mut self);
/// Delay after RDATAC, RESET, SYNC
/// Total 24 master clock cycles
fn t11_2_delay(&mut self);
}
pub struct DefaultDelay<DelayT: DelayNs> {
delayer: DelayT,
}
impl<DelayT: DelayNs> DefaultDelay<DelayT> {
#[inline(always)]
pub const fn new(delayer: DelayT) -> Self {
Self { delayer }
}
}
impl<DelayT: DelayNs> BlockingDelay for DefaultDelay<DelayT> {
#[inline(always)]
fn t6_delay(&mut self) {
self.delayer.delay_us(7);
}
#[inline(always)]
fn t11_1_delay(&mut self) {
self.delayer.delay_us(1);
}
#[inline(always)]
fn t11_2_delay(&mut self) {
self.delayer.delay_us(4);
}
}

View File

@ -0,0 +1,386 @@
use crate::{
adcon, drate, fsc0, fsc2, gpio, mux, ofc0, opcodes, status, AdControl, Ads1256, AllCalibration,
BlockingDelay, CalibrationCommand, Config, DataRate, DigitalIo, GainCalibration, Multiplexer,
OffsetCalibration, Status,
};
use embedded_hal::digital::OutputPin;
use embedded_hal::spi;
use embedded_hal::spi::SpiBus;
use embedded_hal_async::digital::Wait;
use physical_node::GPIO_ERROR_MSG;
use physical_node::spi::{end_spi, end_spi_if_err};
impl<DelayerT, SST, DrdyT> Ads1256<DelayerT, SST, DrdyT>
where
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
{
//----- Base register read/write ----------------------------------
/// [buffer] - The data to send the the ADS1256 starting at index 2, the first two bytes are
/// reserved as the command bytes.
#[inline]
pub fn raw_write_registers<SpiT: SpiBus, const BUF_SIZE: usize>(
&mut self,
spi: &mut SpiT,
start_address: u8,
mut buffer: [u8; BUF_SIZE],
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let num_registers = BUF_SIZE - 2;
assert!(start_address <= fsc2::ADDRESS, "Invalid starting register address.");
//TODO: Change to compile time assertion or bound in future Rust version.
assert!(num_registers <= 11, "Cannot write more than the total number of registers.");
assert!(num_registers >= 1, "Must write at least one register.");
// num_registers represents the total number of registers to write, including the one at the
// provided address. Adjust values based on it accordingly.
// First command byte = 4 bits for the write register opcode, followed by 4 bits for the
// starting register address.
let cmd_start = opcodes::WREG | start_address;
// Second byte = number of registers to write in addition to the register at the starting
// address.
let num_additional = num_registers as u8 - 1;
buffer[0] = cmd_start;
buffer[1] = num_additional;
spi.write(&buffer)
}
/// [buffer] - The data to send the the ADS1256 starting at index 2, the first two bytes are
/// reserved as the command bytes.
#[inline]
pub fn write_registers<SpiT: SpiBus, const BUF_SIZE: usize>(
&mut self,
spi: &mut SpiT,
start_address: u8,
buffer: [u8; BUF_SIZE],
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = self.raw_write_registers(spi, start_address, buffer);
end_spi(&mut self.slave_select, spi, spi_op_result)
}
fn read_registers<SpiT: SpiBus, const NUM: usize>(
&mut self,
spi: &mut SpiT,
start_address: u8,
) -> Result<[u8; NUM], <SpiT as spi::ErrorType>::Error> {
assert!(start_address <= fsc2::ADDRESS, "Invalid starting register address.");
//TODO: Change to compile time assertion or bound.
assert!(NUM <= 11, "Cannot read more than the total number of registers.");
assert!(NUM >= 1, "Must read at least one register.");
// NUM represents the total number of registers to read, including the one at the provided
// address. Adjust values based on it accordingly.
// First command byte = 4 bits for the read register opcode, followed by 4 bits for the
// starting register address.
let cmd_start = opcodes::RREG | start_address;
// Second byte = number of registers to read in addition to the register at the starting
// address.
let num_additional = NUM as u8 - 1;
let mut buffer = [0u8; NUM];
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = spi.write(&[cmd_start, num_additional]);
end_spi_if_err(&mut self.slave_select, spi, spi_op_result)?;
self.delayer.t6_delay();
let spi_op_result = spi.read(&mut buffer);
end_spi(&mut self.slave_select, spi, spi_op_result)?;
Ok(buffer)
}
//----- Standalone commands ----------------------------------
#[inline]
fn standalone_command<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
opcode: u8,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = spi.write(&[opcode]);
end_spi(&mut self.slave_select, spi, spi_op_result)
}
#[inline(always)]
pub fn standby<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.standalone_command(spi, opcodes::STANDBY)
}
/// Self calibration is performed after reset, therefore additional commands should not be sent
/// until data ready pin goes low indicating the calibration is complete.
#[inline(always)]
pub fn reset<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.standalone_command(spi, opcodes::RESET)
}
#[inline(always)]
pub fn wake<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
self.standalone_command(spi, opcodes::WAKEUP)
}
/// Perform self offset and gain calibration.
#[inline]
pub async fn self_calibrate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let result = self.standalone_command(spi, opcodes::SELFCAL);
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
result
}
/// Perform self offset calibration.
#[inline]
pub async fn self_offset_calibrate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let result = self.standalone_command(spi, opcodes::SELFOCAL);
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
result
}
/// Perform self gain calibration.
#[inline]
pub async fn self_gain_calibrate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let result = self.standalone_command(spi, opcodes::SELFGCAL);
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
result
}
/// Perform system offset calibration.
#[inline]
pub async fn system_offset_calibrate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let result = self.standalone_command(spi, opcodes::SYSOCAL);
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
result
}
/// Perform system gain calibration.
#[inline]
pub async fn system_gain_calibrate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let result = self.standalone_command(spi, opcodes::SYSGCAL);
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
result
}
//----- Public register read/write ----------------------------------
#[inline]
pub fn read_status<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<Status, <SpiT as spi::ErrorType>::Error> {
Ok(Status(self.read_registers::<_, 1>(spi, status::ADDRESS)?[0]))
}
#[inline]
pub fn write_status<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: Status,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data value to status
// byte.
let buffer: [u8; 3] = [0, 0, setting.0];
self.write_registers(spi, status::ADDRESS, buffer)
}
#[inline]
pub fn read_multiplexer<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<Multiplexer, <SpiT as spi::ErrorType>::Error> {
Ok(Multiplexer(self.read_registers::<_, 1>(spi, mux::ADDRESS)?[0]))
}
#[inline]
pub fn write_multiplexer<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: Multiplexer,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data value to status
// byte.
let buffer: [u8; 3] = [0, 0, setting.0];
self.write_registers(spi, mux::ADDRESS, buffer)
}
#[inline]
pub fn read_ad_control<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<AdControl, <SpiT as spi::ErrorType>::Error> {
Ok(AdControl(self.read_registers::<_, 1>(spi, adcon::ADDRESS)?[0]))
}
#[inline]
pub fn write_ad_control<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: AdControl,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data value to status
// byte.
let buffer: [u8; 3] = [0, 0, setting.0];
self.write_registers(spi, adcon::ADDRESS, buffer)
}
/// Combined function to write the ADC and multiplexer registers at the same time since this is such a common
/// occurrence.
#[inline]
pub fn write_mux_adc<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
input: Multiplexer,
ad_control: AdControl,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let buffer = [0u8, 0u8, input.0, ad_control.0];
self.write_registers(spi, mux::ADDRESS, buffer)
}
#[inline]
pub fn read_data_rate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<DataRate, <SpiT as spi::ErrorType>::Error> {
Ok(DataRate::from_byte(self.read_registers::<_, 1>(spi, drate::ADDRESS)?[0]))
}
#[inline]
pub fn write_data_rate<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: DataRate,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data value to status
// byte.
let buffer: [u8; 3] = [0, 0, setting as u8];
self.write_registers(spi, drate::ADDRESS, buffer)
}
#[inline]
pub fn read_gpio<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<DigitalIo, <SpiT as spi::ErrorType>::Error> {
Ok(DigitalIo(self.read_registers::<_, 1>(spi, gpio::ADDRESS)?[0]))
}
#[inline]
pub fn write_gpio<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: DigitalIo,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data value to status
// byte.
let buffer: [u8; 3] = [0, 0, setting.0];
self.write_registers(spi, gpio::ADDRESS, buffer)
}
#[inline]
pub fn read_offset_calibration<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<OffsetCalibration, <SpiT as spi::ErrorType>::Error> {
Ok(OffsetCalibration(self.read_registers(spi, ofc0::ADDRESS)?))
}
#[inline]
pub fn write_offset_calibration<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: OffsetCalibration,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data values to offset
// calibration bytes
let buffer = [0, 0, setting.0[0], setting.0[1], setting.0[2]];
self.write_registers(spi, ofc0::ADDRESS, buffer)
}
#[inline]
pub fn read_gain_calibration<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<GainCalibration, <SpiT as spi::ErrorType>::Error> {
Ok(GainCalibration(self.read_registers(spi, fsc0::ADDRESS)?))
}
#[inline]
pub fn write_gain_calibration<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: GainCalibration,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data values to gain
// calibration bytes
let buffer = [0, 0, setting.0[0], setting.0[1], setting.0[2]];
self.write_registers(spi, fsc0::ADDRESS, buffer)
}
/// Reads all calibration registers. Bytes 0 to 2 are offset calibration, bytes 3 to 5 are gain
/// calibration.
#[inline]
pub fn read_all_calibration<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<AllCalibration, <SpiT as spi::ErrorType>::Error> {
Ok(self.read_registers(spi, ofc0::ADDRESS)?.into())
}
#[inline]
pub fn exec_cal_command<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
command: &CalibrationCommand,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
let spi_op_result = spi.write(command.into());
end_spi(&mut self.slave_select, spi, spi_op_result)
}
#[inline]
pub fn read_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
) -> Result<Config, <SpiT as spi::ErrorType>::Error> {
let bytes = self.read_registers::<_, 5>(spi, status::ADDRESS)?;
Ok(Config::from_bytes(bytes))
}
#[inline]
pub fn write_config<SpiT: SpiBus>(
&mut self,
spi: &mut SpiT,
setting: Config,
) -> Result<(), <SpiT as spi::ErrorType>::Error> {
// Create full command buffer, initialize command bytes to 0 and set data value to status
// byte.
let buffer: [u8; 7] = [
0,
0,
setting.status.0,
setting.multiplexer.0,
setting.ad_control.0,
setting.data_rate as u8,
setting.digital_io.0,
];
self.write_registers(spi, status::ADDRESS, buffer)
}
}

View File

@ -0,0 +1,52 @@
#![no_std]
mod adc;
mod delay;
mod io;
#[cfg(feature = "embassy-sync")]
mod mutex;
pub use crate::adc::*;
pub use crate::delay::*;
pub use crate::io::*;
#[cfg(feature = "embassy-sync")]
pub use crate::mutex::*;
pub use ads1256_types::adcon::{ClockOut, Gain, Sdcs};
pub use ads1256_types::drate::DataRate;
pub use ads1256_types::gpio::{DState, DioDirection};
pub use ads1256_types::mux::MuxInput;
pub use ads1256_types::status::{AutoCal, BitOrder, Buffer};
pub use ads1256_types::*;
pub use embedded_hal::digital::OutputPin;
pub use embedded_hal::spi::SpiBus;
pub use embedded_hal_async::digital::Wait;
// ---------------------------------------------------------------------------------------------------------------------
// ----- Ads1256 ------------------------
// ---------------------------------------------------------------------------------------------------------------------
/// **WARNING:** All [Ads1256] methods only ever wait in the middle of a multi-command method, in
/// some cases you may need to use [BlockingDelay::t11_1_delay] or [BlockingDelay::t11_2_delay]
/// between methods that issue commands if they are done one immediately following the other.
pub struct Ads1256<DelayerT, SST, DrdyT> {
pub delayer: DelayerT,
slave_select: SST,
pub data_ready: DrdyT,
}
impl<DelayerT, SST, DrdyT> Ads1256<DelayerT, SST, DrdyT>
where
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
{
//----- New ----------------------------------
#[inline(always)]
pub fn new(delayer: DelayerT, slave_select: SST, data_ready: DrdyT) -> Self {
Self {
delayer,
slave_select,
data_ready,
}
}
}

View File

@ -0,0 +1,271 @@
use crate::{AdControl, Ads1256, BlockingDelay, CalibrationCommand, Conversion,
DataRate, Multiplexer, Status,
};
use core::ops::DerefMut;
use embassy_sync::blocking_mutex::raw::RawMutex;
use physical_node::GPIO_ERROR_MSG;
use physical_node::spi::end_spi;
use embassy_sync::mutex::Mutex;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi;
use embedded_hal::spi::SpiBus;
use embedded_hal_async::digital::Wait;
impl<DelayerT, SST, DrdyT> Ads1256<DelayerT, SST, DrdyT>
where
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
{
/// Functionally the same as [Ads1256::cmd_read_data] but exercises fine-grained control
/// over the [Mutex] of a [SpiBus] in cases where one is used. This function will unlock the
/// [Mutex] while it is waiting for data from the ADS1256.
///
/// Action sequence:
/// 1. Wait for data_ready to go low
/// 1. Lock mutex, mutably borrow SPI
/// 1. Read the conversion value
#[inline]
pub async fn cmd_read_data_m<MutexT: RawMutex, SpiT: SpiBus>(
&mut self,
spi: &Mutex<MutexT, SpiT>,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
let mut spi_guard = spi.lock().await;
let spi = spi_guard.deref_mut();
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
let spi_op_result = self.raw_cmd_read_data(spi);
end_spi(&mut self.slave_select, spi, spi_op_result)
}
/// Functionally the same as [Ads1256::autocal_convert] but exercises fine-grained control
/// over the [Mutex] of a [SpiBus] in cases where one is used. This function will unlock the
/// [Mutex] while it is waiting for data from the ADS1256.
///
/// Action sequence:
/// 1. Switch inputs and optionally adjust different configuration parameters.
/// 1. If only the input was switched without configuration changes.
/// 1. Issue sync command followed by wakeup command
/// 1. Else, auto-calibration will take place
/// 1. Wait for data_ready low
/// 1. RDATA command (read the conversion value)
/// 1. Optionally enter standby mode
///
/// **WARNING:** Auto-calibration must be enabled for intended functionality when changing
/// [Status], [AdControl], or [DataRate]. Furthermore if setting [Status] or [AdControl], their
/// [Buffer] and [Gain] settings must be modified respectively to trigger auto-calibration.
#[inline]
pub async fn autocal_convert_m<MutexT: RawMutex, SpiT: SpiBus>(
&mut self,
spi_mutex: &Mutex<MutexT, SpiT>,
input: Multiplexer,
status: Option<Status>,
ad_control: Option<AdControl>,
data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
// Acquire SPI lock
let mut spi_guard = spi_mutex.lock().await;
let spi = spi_guard.deref_mut();
// Slave select low
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
match (status, ad_control, data_rate) {
// Only modifying the multiplexer, not changing any configuration
(None, None, None) => {
self._none_config(spi, input)?;
self._manual_conversion_init(spi)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer) and changing multiplexer
(Some(status), None, None) => {
self._status_config(spi, input, status)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying AD control (gain) and changing multiplexer
(None, Some(ad_control), None) => {
self._ad_config(spi, input, ad_control)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying data rate and change multiplexer
(None, None, Some(data_rate)) => {
self._drate_config(spi, input, data_rate)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer), AD control (gain), and changing multiplexer
(Some(status), Some(ad_control), None) => {
self._status_ad_config(spi, input, status, ad_control)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer), data rate, and changing multiplexer
(Some(status), None, Some(data_rate)) => {
self._status_drate_config(spi, input, status, data_rate)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying AD control (gain), data rate, and changing multiplexer
(None, Some(ad_control), Some(data_rate)) => {
self._ad_drate_config(spi, input, ad_control, data_rate)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer), AD control (gain), data rate, and changing
// multiplexer
(Some(status), Some(ad_control), Some(data_rate)) => {
self._all_config(spi, input, status, ad_control, data_rate)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
}
}
/// Functionally the same as [Ads1256::loadcal_convert] but exercises fine-grained control
/// over the [Mutex] of a [SpiBus] in cases where one is used. This function will unlock the
/// [Mutex] while it is waiting for data from the ADS1256.
///
/// Action sequence:
/// 1. Switch inputs and optionally adjust different configuration parameters.
/// 1. Issue sync command followed by wakeup command
/// 1. Wait for data_ready low
/// 1. RDATA command (read the conversion value)
/// 1. Optionally enter standby mode
///
/// **WARNING:** Auto-calibration must be disabled for intended functionality when changing
/// [Status], [AdControl], or [DataRate].
#[inline]
pub async fn loadcal_convert_m<MutexT: RawMutex, SpiT: SpiBus>(
&mut self,
spi_mutex: &Mutex<MutexT, SpiT>,
input: Multiplexer,
calibration: Option<&CalibrationCommand>,
status: Option<Status>,
ad_control: Option<AdControl>,
data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
// Acquire SPI lock
let mut spi_guard = spi_mutex.lock().await;
let spi = spi_guard.deref_mut();
// Slave select low
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
match (status, ad_control, data_rate) {
// Only modifying the multiplexer, not changing any configuration
(None, None, None) => {
self._none_config(spi, input)?;
self._manual_conversion_init(spi)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer) and changing multiplexer
(Some(status), None, None) => {
self._status_config(spi, input, status)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying AD control (gain) and changing multiplexer
(None, Some(ad_control), None) => {
self._ad_config(spi, input, ad_control)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying data rate and change multiplexer
(None, None, Some(data_rate)) => {
self._drate_config(spi, input, data_rate)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer), AD control (gain), and changing multiplexer
(Some(status), Some(ad_control), None) => {
self._status_ad_config(spi, input, status, ad_control)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer), data rate, and changing multiplexer
(Some(status), None, Some(data_rate)) => {
self._status_drate_config(spi, input, status, data_rate)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying AD control (gain), data rate, and changing multiplexer
(None, Some(ad_control), Some(data_rate)) => {
self._ad_drate_config(spi, input, ad_control, data_rate)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
// Modifying status (toggle buffer), AD control (gain), data rate, and changing
// multiplexer
(Some(status), Some(ad_control), Some(data_rate)) => {
self._all_config(spi, input, status, ad_control, data_rate)?;
self._loadcal_init(spi, calibration)?;
spi.flush()?;
drop(spi_guard);
self._read_when_rdy_m(spi_mutex, standby).await
},
}
}
#[inline]
pub async fn loadcal_convert_next_m<MutexT: RawMutex, SpiT: SpiBus>(
&mut self,
spi_mutex: &Mutex<MutexT, SpiT>,
next_input: Multiplexer,
next_calibration: Option<&CalibrationCommand>,
next_status: Option<Status>,
next_ad_control: Option<AdControl>,
next_data_rate: Option<DataRate>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self._loadcal_convert_next(
spi_mutex.lock().await.deref_mut(),
next_input,
next_calibration,
next_status,
next_ad_control,
next_data_rate,
standby,
)
.await
}
#[inline]
async fn _read_when_rdy_m<MutexT: RawMutex, SpiT: SpiBus>(
&mut self,
spi_mutex: &Mutex<MutexT, SpiT>,
standby: bool,
) -> Result<Conversion, <SpiT as spi::ErrorType>::Error> {
self.slave_select.set_high().expect(GPIO_ERROR_MSG);
// Wait for data ready low
self.data_ready.wait_for_low().await.expect(GPIO_ERROR_MSG);
self.slave_select.set_low().expect(GPIO_ERROR_MSG);
// Reacquire lock on SPI mutex and read mux conversion
self._read_mux_conversion(spi_mutex.lock().await.deref_mut(), standby)
}
}