Moved BFPOWER drivers to Physical
This commit is contained in:
@ -6,12 +6,17 @@ members = [
|
||||
# Device types
|
||||
"node",
|
||||
"commander",
|
||||
# Drivers
|
||||
"drivers/ads1256/types",
|
||||
"drivers/ads1256/driver",
|
||||
# Meta
|
||||
"generate-quantity"
|
||||
"generate-quantity",
|
||||
# Examples
|
||||
"examples/ads1256"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.3.13"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
repository = "https://git.bfpower.io/BFPOWER/physical"
|
||||
readme = "README.md"
|
||||
|
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")]
|
||||
pub mod comms;
|
||||
pub mod spi;
|
||||
#[cfg(feature = "stm32")]
|
||||
pub mod stm32;
|
||||
|
||||
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