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