Initial proof of concept
This commit is contained in:
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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user