Moved BFPOWER drivers to Physical

This commit is contained in:
Zachary Sunforge
2024-11-14 12:39:06 -08:00
parent 943c47e65b
commit 5f7b005950
20 changed files with 2822 additions and 3 deletions

View File

@ -6,12 +6,17 @@ members = [
# Device types
"node",
"commander",
# Drivers
"drivers/ads1256/types",
"drivers/ads1256/driver",
# Meta
"generate-quantity"
"generate-quantity",
# Examples
"examples/ads1256"
]
[workspace.package]
version = "0.3.13"
version = "0.4.0"
edition = "2021"
repository = "https://git.bfpower.io/BFPOWER/physical"
readme = "README.md"

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

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

View File

@ -0,0 +1,9 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32F411CEUx with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip STM32F411CEUx"
[build]
target = "thumbv7em-none-eabi"
[env]
DEFMT_LOG = "trace"

View File

@ -0,0 +1,37 @@
[package]
name = "ads1256-examples"
description = "Examples using the ads1256."
version.workspace = true
edition.workspace = true
repository.workspace = true
readme.workspace = true
license.workspace = true
[dependencies.ads1256]
path = "../../drivers/ads1256/driver"
[dependencies.physical]
path = "../.."
features = ["defmt"]
[dependencies.defmt]
workspace = true
[dependencies.defmt-rtt]
workspace = true
[dependencies.cortex-m]
workspace = true
features = ["critical-section-single-core"]
[dependencies.cortex-m-rt]
workspace = true
[dependencies.embassy-stm32]
workspace = true
features = ["stm32f411ce", "memory-x", "time", "exti", "time-driver-any"]
[dependencies.embassy-executor]
workspace = true
[dependencies.embassy-futures]
workspace = true
[dependencies.embassy-time]
workspace = true
features = ["tick-hz-16_000_000"]
[dependencies.panic-probe]
workspace = true
[dependencies]
log = "0.4.20"

View File

@ -0,0 +1,5 @@
fn main() {
println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}

View File

@ -0,0 +1,131 @@
#![no_std]
#![no_main]
use cortex_m::prelude::{_embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs};
use {defmt_rtt as _, panic_probe as _};
use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::{
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DState, DataRate, DigitalIo,
DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, MuxInput, OutputPin, Sdcs,
SpiBus, Status, Wait, BlockingDelay
};
use embassy_time::{Delay, Timer};
use executor::Spawner;
use physical::quantity::Quantity;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed};
use stm32::spi::Spi;
use stm32::time::Hertz;
use stm32::{pac, spi};
use defmt::{debug, error, info, trace, unwrap};
const AUTOCAL_CONF: Config = Config {
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common),
ad_control: AdControl::setting(Gain::X2, Sdcs::Off, ClockOut::Off),
data_rate: DataRate::Sps2_5,
digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()),
};
const ADS1256_DELAY: ads1256::DefaultDelay<Delay> = ads1256::DefaultDelay::new(Delay);
#[embassy_executor::main]
async fn main(spawner: Spawner) {
unsafe {
pac::FLASH.acr().modify(|v| {
v.set_prften(true);
v.set_icen(true);
v.set_dcen(true);
});
}
let p = embassy_stm32::init(Default::default());
let mut spi_conf = spi::Config::default();
spi_conf.mode = spi::MODE_1;
spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let mut spi = Spi::new(
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
);
let mut ads_1256 = Ads1256::new(ADS1256_DELAY, select_ads1256, ads1256_data_ready);
// single_conversion(&mut spi, &mut ads_1256).await;
// ads_1256.delayer.t11_1_delay();
// read_continuous(&mut spi, &mut ads_1256).await;
cycle_multiplexer(&mut spi, &mut ads_1256).await;
}
async fn single_conversion<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
ads_1256.write_config(spi, AUTOCAL_CONF).unwrap();
ads_1256.delayer.t11_1_delay();
ads_1256.conversion_init(spi).unwrap();
let data = ads_1256.cmd_read_data(spi).await.unwrap();
info!("data: {}, volts: {}", data, data.to_voltage(AUTOCAL_CONF.ad_control.gain()).fmt(Some(5)));
}
async fn read_continuous<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
ads_1256.write_config(spi, AUTOCAL_CONF).unwrap();
ads_1256.delayer.t11_1_delay();
ads_1256.start_rdatac(spi).await.unwrap();
loop {
let data = ads_1256.read_data(spi).await.unwrap();
info!("data: {}, volts: {}", data, data.to_voltage(AUTOCAL_CONF.ad_control.gain()).fmt(Some(5)));
}
}
async fn cycle_multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
const INPUT_1: Multiplexer = Multiplexer::setting(MuxInput::AIn1, MuxInput::Common);
const INPUT_2: Multiplexer = Multiplexer::setting(MuxInput::AIn2, MuxInput::Common);
const INPUT_3: Multiplexer = Multiplexer::setting(MuxInput::AIn3, MuxInput::Common);
let ad_control = AUTOCAL_CONF.ad_control;
let status = AUTOCAL_CONF.status;
ads_1256.write_config(spi, AUTOCAL_CONF).unwrap();
ads_1256.delayer.t11_1_delay();
loop {
let ad_control = ad_control.with_gain(Gain::X4);
let data = ads_1256
.autocal_convert(spi, INPUT_1, None, Some(ad_control.with_gain(Gain::X4)), None, false)
.await
.unwrap();
info!("Input 1: data: {}, volts: {}", data, data.to_voltage(ad_control.gain()).fmt(Some(5)));
let ad_control = ad_control.with_gain(Gain::X8);
let data = ads_1256
.autocal_convert(spi, INPUT_2, None, Some(ad_control.with_gain(Gain::X8)), None, false)
.await
.unwrap();
info!("Input 2: data: {}, volts: {}", data, data.to_voltage(ad_control.gain()).fmt(Some(5)));
let ad_control = ad_control.with_gain(Gain::X16);
let data = ads_1256
.autocal_convert(spi, INPUT_3, None, Some(ad_control.with_gain(Gain::X16)), None, false)
.await
.unwrap();
info!("Input 3: data: {}, volts: {}", data, data.to_voltage(ad_control.gain()).fmt(Some(5)));
}
}

View File

@ -0,0 +1,180 @@
#![no_std]
#![no_main]
use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
use {defmt_rtt as _, panic_probe as _};
use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::{
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DState, DataRate, DigitalIo,
DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, MuxInput, OutputPin, Sdcs,
SpiBus, Status, Wait,
};
use embassy_time::Delay;
use executor::Spawner;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed};
use stm32::spi::Spi;
use stm32::time::Hertz;
use stm32::{pac, spi};
use defmt::{debug, error, info, trace, unwrap};
const ADS1256_DELAY: ads1256::DefaultDelay<Delay> = ads1256::DefaultDelay::new(Delay);
#[embassy_executor::main]
async fn main(spawner: Spawner) {
unsafe {
pac::FLASH.acr().modify(|v| {
v.set_prften(true);
v.set_icen(true);
v.set_dcen(true);
});
}
let p = embassy_stm32::init(Default::default());
let mut spi_conf = spi::Config::default();
spi_conf.mode = spi::MODE_1;
spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let mut spi = Spi::new(
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
);
let mut ads_1256 = Ads1256::new(ADS1256_DELAY, select_ads1256, ads1256_data_ready);
// status(&mut spi, &mut ads_1256);
// multiplexer(&mut spi, &mut ads_1256);
// ad_control(&mut spi, &mut ads_1256);
// data_rate(&mut spi, &mut ads_1256);
// gpio(&mut spi, &mut ads_1256);
config(&mut spi, &mut ads_1256);
}
fn status<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
info!("Status register:");
const STATUS_SETTING: Status =
Status::setting(Buffer::Disabled, AutoCal::Disabled, BitOrder::MostSigFirst);
ads_1256.write_status(spi, STATUS_SETTING).unwrap();
let status = ads_1256.read_status(spi).unwrap();
info!("ADS1256 starting status: {}", status);
let new_status_setting = status.with_buffer(Buffer::Enabled);
ads_1256.write_status(spi, new_status_setting).unwrap();
let status = ads_1256.read_status(spi).unwrap();
info!("ADS1256 new status: {}", status);
}
fn multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
info!("Multiplexer register:");
const MULTIPLEXER_SETTING: Multiplexer = Multiplexer::setting(MuxInput::AIn0, MuxInput::Common);
ads_1256
.write_multiplexer(spi, MULTIPLEXER_SETTING)
.unwrap();
let multiplexer = ads_1256.read_multiplexer(spi).unwrap();
info!("ADS1256 starting multiplexer: {}", multiplexer);
let new_multiplexer_setting = multiplexer.with_positive(MuxInput::AIn1);
ads_1256
.write_multiplexer(spi, new_multiplexer_setting)
.unwrap();
let multiplexer = ads_1256.read_multiplexer(spi).unwrap();
info!("ADS1256 new ad_control: {}", multiplexer);
}
fn ad_control<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
info!("AD control register:");
const AD_CONTROL_SETTING: AdControl = AdControl::setting(Gain::X64, Sdcs::Off, ClockOut::Off);
ads_1256.write_ad_control(spi, AD_CONTROL_SETTING).unwrap();
let ad_control = ads_1256.read_ad_control(spi).unwrap();
info!("ADS1256 starting ad_control: {}", ad_control);
let new_ad_control_setting = ad_control.with_gain(Gain::X1);
ads_1256
.write_ad_control(spi, new_ad_control_setting)
.unwrap();
let ad_control = ads_1256.read_ad_control(spi).unwrap();
info!("ADS1256 new ad_control: {}", ad_control);
}
fn data_rate<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
info!("Data rate register:");
const DATA_RATE: DataRate = DataRate::Sps2_5;
ads_1256.write_data_rate(spi, DATA_RATE).unwrap();
let data_rate = ads_1256.read_data_rate(spi).unwrap();
info!("ADS1256 starting data rate: {}", data_rate);
ads_1256.write_data_rate(spi, DataRate::Sps60).unwrap();
let data_rate = ads_1256.read_data_rate(spi).unwrap();
info!("ADS1256 new data rate: {}", data_rate);
}
fn gpio<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
info!("GPIO register:");
const GPIO: DigitalIo = DigitalIo::setting(
DigitalIoState::new(DState::Low, DState::Low, DState::Low, DState::Low),
DigitalIoDirection::new(
DioDirection::Output,
DioDirection::Input,
DioDirection::Input,
DioDirection::Output,
),
);
ads_1256.write_gpio(spi, GPIO).unwrap();
let gpio = ads_1256.read_gpio(spi).unwrap();
info!("ADS1256 starting gpio: {}", gpio);
let new_gpio_setting = gpio.with_io3_state(DState::High);
ads_1256.write_gpio(spi, new_gpio_setting).unwrap();
let gpio = ads_1256.read_gpio(spi).unwrap();
info!("ADS1256 new gpio: {}", gpio);
}
fn config<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
) {
info!("Config:");
let config = ads_1256.read_config(spi).unwrap();
info!("ADS1256 starting config: {}", config);
const CONFIG: Config = Config {
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
multiplexer: Multiplexer::setting(MuxInput::AIn4, MuxInput::AIn5),
ad_control: AdControl::setting(Gain::X16, Sdcs::Off, ClockOut::Off),
data_rate: DataRate::Sps10,
digital_io: DigitalIo::setting(
DigitalIoState::new(DState::Low, DState::Low, DState::Low, DState::Low),
DigitalIoDirection::new(
DioDirection::Output,
DioDirection::Input,
DioDirection::Input,
DioDirection::Input,
),
),
};
ads_1256.write_config(spi, CONFIG).unwrap();
let config = ads_1256.read_config(spi).unwrap();
info!("ADS1256 new config: {:#?}", config);
}

View File

@ -2,7 +2,11 @@
#[cfg(feature = "comms")]
pub mod comms;
pub mod spi;
#[cfg(feature = "stm32")]
pub mod stm32;
pub use physical::CriticalError;
pub use physical::CriticalError;
pub const GPIO_ERROR_MSG: &'static str =
"Driver does not support GPIO pins with expected failure states";

52
node/src/spi.rs Normal file
View File

@ -0,0 +1,52 @@
use crate::GPIO_ERROR_MSG;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi;
use embedded_hal::spi::SpiBus;
/// End the SPI operation if the result was an error.
/// This function will attempt to flush the SPI bus if the result being inspected was an error.
/// If the flush fails, the flush error will be ignored and the original error will be returned.
#[inline]
pub fn end_spi_if_err<OkT, SpiT: SpiBus>(
slave_select: &mut impl OutputPin,
spi: &mut SpiT,
result: Result<OkT, <SpiT as spi::ErrorType>::Error>,
) -> Result<OkT, <SpiT as spi::ErrorType>::Error> {
match result {
Ok(_) => result,
Err(_) => {
// Ignore flush error and return the original error
slave_select.set_high().expect(GPIO_ERROR_MSG);
let _ = spi.flush();
result
},
}
}
/// End the series of SPI operations and forward the error of the final operation if there was
/// one. Error handling:
/// * If there was an error in the SPI operation and an error flushing the bus, returns the original
/// SPI error, not the bus error.
/// * If there was not an error in the SPI operation but was an error flushing the bus, returns the
/// bus flush error.
/// * If there was an error in the SPI operation and no error flushing the bus, returns the original
/// SPI error.
#[inline]
pub fn end_spi<OkT, SpiT: SpiBus>(
slave_select: &mut impl OutputPin,
spi: &mut SpiT,
result: Result<OkT, <SpiT as spi::ErrorType>::Error>,
) -> Result<OkT, <SpiT as spi::ErrorType>::Error> {
match result {
Ok(_) => {
slave_select.set_high().expect(GPIO_ERROR_MSG);
spi.flush()?;
result
},
Err(_) => {
slave_select.set_high().expect(GPIO_ERROR_MSG);
let _ = spi.flush();
result
},
}
}