Removed a bunch of over abstracted bullshit.
This commit is contained in:
11
Cargo.toml
11
Cargo.toml
@ -6,18 +6,10 @@ members = [
|
||||
# Device types
|
||||
"node",
|
||||
"commander",
|
||||
# Peripherals
|
||||
|
||||
# Peripheral components
|
||||
"peripheral-components/ads1256/*",
|
||||
# Macros
|
||||
"macros/node-poll-variants",
|
||||
# Examples
|
||||
"examples/ads1256"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
repository = "https://git.bfpower.io/BFPOWER/physical"
|
||||
readme = "README.md"
|
||||
@ -115,6 +107,7 @@ license.workspace = true
|
||||
thermocouple_k = []
|
||||
lm35 = []
|
||||
pid = []
|
||||
stm32 = []
|
||||
|
||||
[dependencies]
|
||||
uom = { workspace = true }
|
||||
|
@ -1,14 +0,0 @@
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# replace STM32F429ZITx 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"
|
@ -1,47 +0,0 @@
|
||||
[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.physical]
|
||||
path = "../.."
|
||||
features = ["thermocouple_k", "lm35"]
|
||||
[dependencies.physical-node]
|
||||
path = "../../node"
|
||||
features = ["embassy-sync"]
|
||||
[dependencies.physical-ads1256]
|
||||
path = "../../peripheral-components/ads1256/node"
|
||||
features = ["poll"]
|
||||
[dependencies.static_cell]
|
||||
workspace = true
|
||||
[dependencies.ads1256]
|
||||
workspace = true
|
||||
[dependencies.uom]
|
||||
workspace = true
|
||||
[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.embassy-sync]
|
||||
workspace = true
|
||||
[dependencies.panic-probe]
|
||||
workspace = true
|
@ -1,5 +0,0 @@
|
||||
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");
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::Cell;
|
||||
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::standard::input::SingleEnded;
|
||||
use ads1256::{
|
||||
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DataRate, DigitalIo,
|
||||
DigitalIoDirection, DigitalIoState, Gain, Multiplexer, MuxInput, Sdcs, Status,
|
||||
};
|
||||
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 uom::si::electric_potential::volt;
|
||||
use uom::si::f32;
|
||||
|
||||
use defmt::{debug, error, info, trace, unwrap};
|
||||
use static_cell::StaticCell;
|
||||
use embassy_stm32::peripherals::{PA1, PA3, SPI1};
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::pubsub::PubSubChannel;
|
||||
use physical_node::transducer::{Publish, StatefulPublisher};
|
||||
|
||||
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()),
|
||||
};
|
||||
|
||||
struct Ads1256Delay;
|
||||
|
||||
impl ads1256::BlockingDelay for Ads1256Delay {
|
||||
#[inline]
|
||||
fn t6_delay(&mut self) {
|
||||
Delay.delay_us(1u32);
|
||||
}
|
||||
|
||||
fn t11_1_delay(&mut self) {}
|
||||
|
||||
fn t11_2_delay(&mut self) {}
|
||||
}
|
||||
|
||||
struct Inputs {
|
||||
ai0: StatefulPublisher<f32::ElectricPotential, NoopRawMutex, 10, 1>,
|
||||
ai1: StatefulPublisher<f32::ElectricPotential, NoopRawMutex, 10, 1>,
|
||||
ai2: StatefulPublisher<f32::ElectricPotential, NoopRawMutex, 10, 1>,
|
||||
}
|
||||
|
||||
// Inputs
|
||||
static ANALOG_INPUTS: StaticCell<Inputs> = StaticCell::new();
|
||||
static ADS_1256: StaticCell<Ads1256<Ads1256Delay, Output<PA1>, ExtiInput<PA3>>> = StaticCell::new();
|
||||
static SPI: StaticCell<Spi<SPI1, NoDma, NoDma>> = StaticCell::new();
|
||||
|
||||
#[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 spi = SPI.init(Spi::new(
|
||||
p.SPI1,
|
||||
p.PA5,
|
||||
p.PA7,
|
||||
p.PA6,
|
||||
NoDma,
|
||||
NoDma,
|
||||
spi_conf,
|
||||
));
|
||||
|
||||
let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready));
|
||||
ads_1256.write_config(spi, AUTOCAL_CONF).unwrap();
|
||||
|
||||
let inputs = &*ANALOG_INPUTS.init(Inputs {
|
||||
ai0: StatefulPublisher::new(
|
||||
Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
|
||||
PubSubChannel::new().into(),
|
||||
),
|
||||
ai1: StatefulPublisher::new(
|
||||
Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
|
||||
PubSubChannel::new().into(),
|
||||
),
|
||||
ai2: StatefulPublisher::new(
|
||||
Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
|
||||
PubSubChannel::new().into(),
|
||||
),
|
||||
});
|
||||
|
||||
spawner.spawn(log_task(inputs)).unwrap();
|
||||
spawner
|
||||
.spawn(drive_inputs_task(ads_1256, spi, inputs))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn drive_inputs_task(
|
||||
ads_1256: &'static mut Ads1256<Ads1256Delay, Output<'static, PA1>, ExtiInput<'static, PA3>>,
|
||||
spi: &'static mut Spi<'static, SPI1, NoDma, NoDma>,
|
||||
inputs: &'static Inputs,
|
||||
) {
|
||||
let Inputs { ai0, ai1, ai2 } = inputs;
|
||||
|
||||
loop {
|
||||
let mut accumulator = f32::ElectricPotential::new::<volt>(0.0);
|
||||
|
||||
let voltage = ads_1256
|
||||
.autocal_convert(spi, SingleEnded::AIn0.into(), None, None, None, false)
|
||||
.await
|
||||
.unwrap()
|
||||
.to_voltage(AUTOCAL_CONF.ad_control.gain());
|
||||
ai0.update(voltage);
|
||||
accumulator += voltage;
|
||||
|
||||
let voltage = ads_1256
|
||||
.autocal_convert(spi, SingleEnded::AIn1.into(), None, None, None, false)
|
||||
.await
|
||||
.unwrap()
|
||||
.to_voltage(AUTOCAL_CONF.ad_control.gain());
|
||||
ai1.update(voltage);
|
||||
accumulator += voltage;
|
||||
|
||||
let voltage = ads_1256
|
||||
.autocal_convert(spi, SingleEnded::AIn2.into(), None, None, None, false)
|
||||
.await
|
||||
.unwrap()
|
||||
.to_voltage(AUTOCAL_CONF.ad_control.gain());
|
||||
ai2.update(voltage);
|
||||
accumulator += voltage;
|
||||
|
||||
let accum_volts = accumulator.get::<volt>();
|
||||
info!("Immediate loop iteration result, combined volts: {}", accum_volts);
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn log_task(inputs: &'static Inputs) {
|
||||
let Inputs { ai0, ai1, ai2 } = inputs;
|
||||
let mut ai0_sub = ai0.subscribe().unwrap();
|
||||
let mut ai1_sub = ai1.subscribe().unwrap();
|
||||
let mut ai2_sub = ai2.subscribe().unwrap();
|
||||
|
||||
loop {
|
||||
let msg = ai0_sub.next_message_pure().await.get::<volt>();
|
||||
info!("Log task ai0: {}", msg);
|
||||
|
||||
let msg = ai1_sub.next_message_pure().await.get::<volt>();
|
||||
info!("Log task ai1: {}", msg);
|
||||
|
||||
let msg = ai2_sub.next_message_pure().await.get::<volt>();
|
||||
info!("Log task ai2: {}", msg);
|
||||
}
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::Cell;
|
||||
|
||||
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::standard::input::SingleEnded;
|
||||
use ads1256::{
|
||||
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DState, DataRate, DigitalIo,
|
||||
DigitalIoDirection, DigitalIoState, Gain, Multiplexer, MuxInput, Sdcs, Status,
|
||||
};
|
||||
use embassy_time::{Delay, Duration, Timer};
|
||||
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 uom::si::electric_potential::volt;
|
||||
use uom::si::f32;
|
||||
|
||||
use defmt::info;
|
||||
use static_cell::StaticCell;
|
||||
use embassy_stm32::peripherals::{PA1, PA3, SPI1};
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::pubsub::PubSubChannel;
|
||||
use physical_ads1256::standard::multiplexer::poll::{
|
||||
AutocalPoll, AutocalPollStatePub, ModInputOnly,
|
||||
};
|
||||
use physical_node::transducer::input::Poll;
|
||||
use physical_node::transducer::{Publish, Publisher, StatefulPublisher};
|
||||
|
||||
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()),
|
||||
};
|
||||
|
||||
struct Ads1256Delay;
|
||||
|
||||
impl ads1256::BlockingDelay for Ads1256Delay {
|
||||
#[inline]
|
||||
fn t6_delay(&mut self) {
|
||||
Delay.delay_us(1u32);
|
||||
}
|
||||
|
||||
fn t11_1_delay(&mut self) {}
|
||||
|
||||
fn t11_2_delay(&mut self) {}
|
||||
}
|
||||
|
||||
type ExampleInput = AutocalPollStatePub<
|
||||
'static,
|
||||
NoopRawMutex,
|
||||
NoopRawMutex,
|
||||
ModInputOnly,
|
||||
Ads1256Delay,
|
||||
Output<'static, PA1>,
|
||||
ExtiInput<'static, PA3>,
|
||||
Spi<'static, SPI1, NoDma, NoDma>,
|
||||
10,
|
||||
1,
|
||||
>;
|
||||
|
||||
struct Inputs {
|
||||
ai1: ExampleInput,
|
||||
ai2: ExampleInput,
|
||||
ai3: ExampleInput,
|
||||
}
|
||||
|
||||
// Inputs
|
||||
static ANALOG_INPUTS: StaticCell<Inputs> = StaticCell::new();
|
||||
static ADS_1256: StaticCell<
|
||||
Mutex<NoopRawMutex, Ads1256<Ads1256Delay, Output<PA1>, ExtiInput<PA3>>>,
|
||||
> = StaticCell::new();
|
||||
static SPI: StaticCell<Mutex<NoopRawMutex, Spi<SPI1, NoDma, NoDma>>> = StaticCell::new();
|
||||
|
||||
#[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 spi = SPI.init(Mutex::new(
|
||||
Spi::new(
|
||||
p.SPI1,
|
||||
p.PA5,
|
||||
p.PA7,
|
||||
p.PA6,
|
||||
NoDma,
|
||||
NoDma,
|
||||
spi_conf,
|
||||
)
|
||||
));
|
||||
|
||||
let ads_1256 =
|
||||
ADS_1256.init(Mutex::new(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)));
|
||||
|
||||
ads_1256
|
||||
.get_mut()
|
||||
.write_config(spi.get_mut(), AUTOCAL_CONF)
|
||||
.unwrap();
|
||||
|
||||
let inputs = &*ANALOG_INPUTS.init(Inputs {
|
||||
ai1: AutocalPollStatePub {
|
||||
poll: AutocalPoll::new(
|
||||
ModInputOnly {
|
||||
gain: Gain::X2,
|
||||
multiplexer: SingleEnded::AIn1.into(),
|
||||
},
|
||||
ads_1256,
|
||||
spi,
|
||||
),
|
||||
publisher: PubSubChannel::new().into(),
|
||||
state: Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
|
||||
},
|
||||
ai2: AutocalPollStatePub {
|
||||
poll: AutocalPoll::new(
|
||||
ModInputOnly {
|
||||
gain: Gain::X2,
|
||||
multiplexer: SingleEnded::AIn2.into(),
|
||||
},
|
||||
ads_1256,
|
||||
spi,
|
||||
),
|
||||
publisher: PubSubChannel::new().into(),
|
||||
state: Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
|
||||
},
|
||||
ai3: AutocalPollStatePub {
|
||||
poll: AutocalPoll::new(
|
||||
ModInputOnly {
|
||||
gain: Gain::X2,
|
||||
multiplexer: SingleEnded::AIn3.into(),
|
||||
},
|
||||
ads_1256,
|
||||
spi,
|
||||
),
|
||||
publisher: PubSubChannel::new().into(),
|
||||
state: Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
|
||||
},
|
||||
});
|
||||
spawner.spawn(log_task(&inputs.ai1, 1)).unwrap();
|
||||
spawner.spawn(log_task(&inputs.ai2, 2)).unwrap();
|
||||
spawner.spawn(log_task(&inputs.ai3, 3)).unwrap();
|
||||
spawner
|
||||
.spawn(poll_task(&inputs.ai1, 1, Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
spawner
|
||||
.spawn(poll_task(&inputs.ai2, 2, Duration::from_secs(10)))
|
||||
.unwrap();
|
||||
spawner
|
||||
.spawn(poll_task(&inputs.ai3, 3, Duration::from_secs(20)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[embassy_executor::task(pool_size = 3)]
|
||||
async fn poll_task(input: &'static ExampleInput, input_num: u8, every: Duration) {
|
||||
loop {
|
||||
Timer::after(every).await;
|
||||
let result = input.poll().await.unwrap().get::<volt>();
|
||||
info!("ai{} poll result: {} volts", input_num, result);
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task(pool_size = 3)]
|
||||
async fn log_task(input: &'static ExampleInput, input_num: u8) {
|
||||
let mut subscriber = input.subscribe().unwrap();
|
||||
loop {
|
||||
let msg = subscriber.next_message_pure().await.get::<volt>();
|
||||
info!("Log task ai{}: {}", input_num, msg);
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||
// Necessary unused import.
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
use {embassy_executor as executor, embassy_stm32 as stm32};
|
||||
|
||||
use ads1256::standard::input::{Differential, SingleEnded};
|
||||
use ads1256::{
|
||||
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DataRate, DigitalIo,
|
||||
DigitalIoDirection, DigitalIoState, Gain, Multiplexer, MuxInput, Sdcs, Status,
|
||||
};
|
||||
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 uom::si::electric_potential::{millivolt, volt};
|
||||
|
||||
use defmt::info;
|
||||
use physical::transducer::{lm35, thermocouple_k};
|
||||
use uom::si::thermodynamic_temperature::degree_celsius;
|
||||
|
||||
const AUTOCAL_CONF: Config = Config {
|
||||
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
|
||||
multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::AIn1),
|
||||
ad_control: AdControl::setting(Gain::X64, Sdcs::Off, ClockOut::Off),
|
||||
data_rate: DataRate::Sps2_5,
|
||||
digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()),
|
||||
};
|
||||
|
||||
struct Ads1256Delay;
|
||||
|
||||
impl ads1256::BlockingDelay for Ads1256Delay {
|
||||
#[inline]
|
||||
fn t6_delay(&mut self) {
|
||||
Delay.delay_us(1u32);
|
||||
}
|
||||
|
||||
fn t11_1_delay(&mut self) {}
|
||||
|
||||
fn t11_2_delay(&mut self) {}
|
||||
}
|
||||
|
||||
#[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(Ads1256Delay, select_ads1256, ads1256_data_ready);
|
||||
ads_1256.write_config(&mut spi, AUTOCAL_CONF).unwrap();
|
||||
ads_1256.self_calibrate(&mut spi).await.unwrap();
|
||||
|
||||
loop {
|
||||
let gain = Gain::X2;
|
||||
let reference = ads_1256
|
||||
.autocal_convert(
|
||||
&mut spi,
|
||||
SingleEnded::AIn2.into(),
|
||||
None,
|
||||
Some(AUTOCAL_CONF.ad_control.with_gain(gain)),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.to_voltage(gain);
|
||||
let reference_volts = reference.get::<volt>();
|
||||
let reference = lm35::convert(reference).unwrap();
|
||||
let reference_celsius = reference.get::<degree_celsius>();
|
||||
info!(
|
||||
"Reference junction temperature: {}°C, from voltage reading: {}V",
|
||||
reference_celsius, reference_volts
|
||||
);
|
||||
|
||||
let gain = Gain::X64;
|
||||
let voltage = ads_1256
|
||||
.autocal_convert(
|
||||
&mut spi,
|
||||
Differential::AIn0.into(),
|
||||
None,
|
||||
Some(AUTOCAL_CONF.ad_control.with_gain(gain)),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.to_voltage(AUTOCAL_CONF.ad_control.gain());
|
||||
let mv = voltage.get::<millivolt>();
|
||||
let temperature = thermocouple_k::convert_direct(voltage, reference).unwrap();
|
||||
let celsius = temperature.get::<degree_celsius>();
|
||||
info!("Thermocouple temperature: {}°C, from reading: {}mV", celsius, mv);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
[package]
|
||||
name = "node-poll-variants"
|
||||
description = "Macros for physical nodes."
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
path = "tests/test_build.rs"
|
||||
|
||||
[dependencies.syn]
|
||||
workspace = true
|
||||
[dependencies.quote]
|
||||
workspace = true
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
workspace = true
|
||||
[dev-dependencies.physical-node]
|
||||
path = "../../node"
|
||||
features = ["embassy-sync"]
|
||||
[dev-dependencies.embassy-sync]
|
||||
workspace = true
|
@ -1,294 +0,0 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use std::ops::Deref;
|
||||
use quote::__private::parse_spanned;
|
||||
use syn::__private::{str, Span};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::{Colon, Comma, PathSep, Plus};
|
||||
use syn::{parse_macro_input, parse_quote, parse_quote_spanned, parse_str, Data, DeriveInput, Expr, GenericParam, Generics, Ident, Lit, LitStr, Meta, Path, PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, Type};
|
||||
|
||||
#[proc_macro_derive(PollVariants, attributes(value_type, error_type))]
|
||||
pub fn poll_variant_macro(input: TokenStream) -> TokenStream {
|
||||
// ----- Parse input ----------------------------------
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let attrs = &input.attrs;
|
||||
let vis = &input.vis;
|
||||
let ident = &input.ident;
|
||||
let data = &input.data;
|
||||
let og_generics = &input.generics;
|
||||
let (og_impl_generics, og_type_generics, og_where_clause) = &og_generics.split_for_impl();
|
||||
|
||||
// ----- Check that item the macro was used on is a struct ----------------------------------
|
||||
match data {
|
||||
Data::Struct(struct_data) => struct_data,
|
||||
Data::Enum(_) => panic!("Stateful struct cannot be derived from an enum."),
|
||||
Data::Union(_) => panic!("Stateful struct cannot be derived from a union."),
|
||||
};
|
||||
|
||||
// ----- Extract attribute information ----------------------------------
|
||||
const VALUE_T_NAME: &str = "value_type";
|
||||
const ERROR_T_NAME: &str = "error_type";
|
||||
let mut value_type: Option<Type> = None;
|
||||
let mut error_type: Option<Type> = None;
|
||||
for attribute in attrs.iter() {
|
||||
// if the attribute is a named value
|
||||
if let Meta::NameValue(meta) = &attribute.meta {
|
||||
// if the name of the attribute is value_type
|
||||
if meta.path.segments[0].ident == VALUE_T_NAME {
|
||||
// if the value of the attribute is a literal
|
||||
if let Expr::Lit(lit) = &meta.value {
|
||||
// if the literal is a string
|
||||
if let Lit::Str(lit) = &lit.lit {
|
||||
let span = lit.span();
|
||||
let string = lit.token().to_string();
|
||||
let string = string.trim_matches('"').to_string();
|
||||
let _value_type: Type = parse_str(string.deref()).unwrap();
|
||||
let _value_type: Type = parse_quote_spanned!(span=> #_value_type);
|
||||
|
||||
value_type = Some(_value_type);
|
||||
|
||||
} else {
|
||||
panic!("{VALUE_T_NAME} must be set with a string literal.")
|
||||
}
|
||||
} else {
|
||||
panic!("{VALUE_T_NAME} must be set with a literal.")
|
||||
}
|
||||
}
|
||||
|
||||
// if the name of the attribute is error_type
|
||||
if meta.path.segments[0].ident == ERROR_T_NAME {
|
||||
// if the value of the attribute is a literal
|
||||
if let Expr::Lit(lit) = &meta.value {
|
||||
// if the literal is a string
|
||||
if let Lit::Str(lit) = &lit.lit {
|
||||
let span = lit.span();
|
||||
let string = lit.token().to_string();
|
||||
let string = string.trim_matches('"').to_string();
|
||||
let _error_type: Type = parse_str(string.deref()).unwrap();
|
||||
let _error_type: Type = parse_quote_spanned!(span=> #_error_type);
|
||||
|
||||
error_type = Some(_error_type);
|
||||
} else {
|
||||
panic!("{ERROR_T_NAME} must be set with a string literal.")
|
||||
}
|
||||
} else {
|
||||
panic!("{ERROR_T_NAME} must be set with a literal.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let value_type = value_type
|
||||
.expect(format!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]").deref());
|
||||
let error_type = error_type
|
||||
.expect(format!("Need attribute {ERROR_T_NAME}: #[{ERROR_T_NAME} = \"type\"]").deref());
|
||||
|
||||
// ----- Add publisher generics ----------------------------------
|
||||
// MutexT
|
||||
const MUTEX_T_NAME: &str = "PublishMutexT";
|
||||
let mutex_t_ident = Ident::new(MUTEX_T_NAME, Span::call_site());
|
||||
const CAPACITY_NAME: &str = "CAPACITY";
|
||||
let capacity_ident = Ident::new(CAPACITY_NAME, Span::call_site());
|
||||
const NUM_SUBS_NAME: &str = "NUM_SUBS";
|
||||
let num_subs_ident = Ident::new(NUM_SUBS_NAME, Span::call_site());
|
||||
let mut num_lifetimes: usize = 0;
|
||||
let mut num_types: usize = 0;
|
||||
let mut num_const: usize = 0;
|
||||
let mut has_mutex_t = false;
|
||||
for param in og_generics.params.iter() {
|
||||
match param {
|
||||
GenericParam::Lifetime(_) => num_lifetimes += 1,
|
||||
GenericParam::Type(param) => {
|
||||
num_types += 1;
|
||||
|
||||
// If the generic parameter is MutexT
|
||||
if param.ident == MUTEX_T_NAME {
|
||||
has_mutex_t = true;
|
||||
}
|
||||
},
|
||||
GenericParam::Const(_) => num_const += 1,
|
||||
}
|
||||
}
|
||||
|
||||
let mut publish_generics = og_generics.clone();
|
||||
// If MutexT is not a generic parameter, add it
|
||||
if !has_mutex_t {
|
||||
let mutex_t_param: GenericParam =
|
||||
parse_quote!(#mutex_t_ident: embassy_sync::blocking_mutex::raw::RawMutex);
|
||||
|
||||
let num_generics = num_lifetimes + num_types + num_const;
|
||||
// If there are generics
|
||||
if num_generics > 0 {
|
||||
// If all generics are lifetimes
|
||||
if num_lifetimes == num_generics {
|
||||
// Add MutexT after the lifetimes
|
||||
publish_generics.params.push(mutex_t_param);
|
||||
// If no generics are lifetimes
|
||||
} else if num_lifetimes == 0 {
|
||||
// Insert MutexT at the front
|
||||
publish_generics.params.insert(0, mutex_t_param);
|
||||
// If there are lifetimes and other generics
|
||||
} else {
|
||||
// Insert MutexT after the lifetimes
|
||||
publish_generics.params.insert(num_lifetimes, mutex_t_param);
|
||||
}
|
||||
// If there are no generics
|
||||
} else {
|
||||
// Add MutexT
|
||||
publish_generics.params.push(mutex_t_param);
|
||||
}
|
||||
}
|
||||
// const generics
|
||||
let capacity_param: GenericParam = parse_quote!(const #capacity_ident: usize);
|
||||
let num_subs_param: GenericParam = parse_quote!(const #num_subs_ident: usize);
|
||||
publish_generics.params.push(capacity_param);
|
||||
publish_generics.params.push(num_subs_param);
|
||||
|
||||
let (publ_impl_generics, publ_type_generics, publ_where_clause) =
|
||||
&publish_generics.split_for_impl();
|
||||
|
||||
let pubsub_error_path: Path = parse_quote!(embassy_sync::pubsub::Error);
|
||||
let pubsub_sub_path: Path = parse_quote!(embassy_sync::pubsub::Subscriber);
|
||||
|
||||
let stateful_variant_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span());
|
||||
let publish_variant_ident = Ident::new(format!("{ident}Publish").deref(), ident.span());
|
||||
let state_pub_variant_ident = Ident::new(format!("{ident}StatePub").deref(), ident.span());
|
||||
|
||||
let poll_path: Path = parse_quote!(physical_node::transducer::input::Poll);
|
||||
let stateful_path: Path = parse_quote!(physical_node::transducer::Stateful);
|
||||
let publish_path: Path = parse_quote!(physical_node::transducer::Publish);
|
||||
|
||||
let state_path: Path = parse_quote!(physical_node::transducer::State);
|
||||
let publisher_path: Path = parse_quote!(physical_node::transducer::Publisher);
|
||||
let cellview_path: Path = parse_quote!(physical_node::cell::CellView);
|
||||
|
||||
let error_path: Path = parse_quote!(physical_node::CriticalError);
|
||||
|
||||
let expanded = quote! {
|
||||
// ----- Stateful struct ----------------------------------
|
||||
#vis struct #stateful_variant_ident #og_generics #og_where_clause {
|
||||
pub poll: #ident #og_type_generics,
|
||||
pub state: #state_path<#value_type>,
|
||||
}
|
||||
|
||||
// ----- Stateful impls ----------------------------------
|
||||
impl #og_impl_generics #poll_path for #stateful_variant_ident #og_type_generics #og_where_clause {
|
||||
type Value = #value_type;
|
||||
type Error = #error_type;
|
||||
|
||||
#[inline]
|
||||
async fn poll(&self) -> Result<Self::Value, Self::Error> {
|
||||
let result = self.poll.poll().await;
|
||||
if let Ok(value) = result {
|
||||
self.state.update(value);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl #og_impl_generics #stateful_path for #stateful_variant_ident #og_type_generics #og_where_clause {
|
||||
type Value = #value_type;
|
||||
|
||||
#[inline(always)]
|
||||
fn state_cell(&self) -> #cellview_path<Self::Value> {
|
||||
self.state.state_cell()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn state(&self) -> Self::Value {
|
||||
self.state.state()
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Publish struct ----------------------------------
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
#vis struct #publish_variant_ident #publish_generics #publ_where_clause {
|
||||
pub poll: #ident #og_type_generics,
|
||||
pub publisher: #publisher_path<#value_type, #mutex_t_ident, #capacity_ident, #num_subs_ident>,
|
||||
}
|
||||
|
||||
// ----- Publish impl ----------------------------------
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause {
|
||||
type Value = #value_type;
|
||||
type Error = #error_type;
|
||||
|
||||
#[inline]
|
||||
async fn poll(&self) -> Result<Self::Value, Self::Error> {
|
||||
let result = self.poll.poll().await;
|
||||
if let Ok(value) = result {
|
||||
self.publisher.update(value);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause {
|
||||
type Value = #value_type;
|
||||
type Mutex = #mutex_t_ident;
|
||||
|
||||
#[inline(always)]
|
||||
fn subscribe(
|
||||
&self,
|
||||
) -> Result<#pubsub_sub_path<Self::Mutex, Self::Value, #capacity_ident, #num_subs_ident, 0>, #pubsub_error_path> {
|
||||
self.publisher.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
// ----- StatePub struct ----------------------------------
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
#vis struct #state_pub_variant_ident #publish_generics #publ_where_clause {
|
||||
pub poll: #ident #og_type_generics,
|
||||
pub state: #state_path<#value_type>,
|
||||
pub publisher: #publisher_path<#value_type, #mutex_t_ident, #capacity_ident, #num_subs_ident>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
impl #publ_impl_generics #poll_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause {
|
||||
type Value = #value_type;
|
||||
type Error = #error_type;
|
||||
|
||||
#[inline]
|
||||
async fn poll(&self) -> Result<Self::Value, Self::Error> {
|
||||
let result = self.poll.poll().await;
|
||||
if let Ok(value) = result {
|
||||
self.state.update(value);
|
||||
self.publisher.update(value);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
impl #publ_impl_generics #stateful_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause {
|
||||
type Value = #value_type;
|
||||
|
||||
#[inline(always)]
|
||||
fn state_cell(&self) -> #cellview_path<Self::Value> {
|
||||
self.state.state_cell()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn state(&self) -> Self::Value {
|
||||
self.state.state()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #state_pub_variant_ident #publ_type_generics #publ_where_clause {
|
||||
type Value = #value_type;
|
||||
type Mutex = #mutex_t_ident;
|
||||
|
||||
#[inline(always)]
|
||||
fn subscribe(
|
||||
&self,
|
||||
) -> Result<#pubsub_sub_path<Self::Mutex, Self::Value, #capacity_ident, #num_subs_ident, 0>, #pubsub_error_path> {
|
||||
self.publisher.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
#![feature(async_fn_in_trait, impl_trait_projections, never_type)]
|
||||
|
||||
use node_poll_variants::PollVariants;
|
||||
use physical_node::transducer::input::Poll;
|
||||
|
||||
#[derive(PollVariants)]
|
||||
#[value_type = "SecondT"]
|
||||
#[error_type = "!"]
|
||||
struct ExamplePoll<'a, FirstT, SecondT>
|
||||
where
|
||||
SecondT: Copy,
|
||||
{
|
||||
a: &'a i32,
|
||||
b: i32,
|
||||
first: FirstT,
|
||||
second: SecondT,
|
||||
}
|
||||
|
||||
impl<'a, FirstT, SecondT> Poll for ExamplePoll<'a, FirstT, SecondT>
|
||||
where
|
||||
SecondT: Copy,
|
||||
{
|
||||
type Value = SecondT;
|
||||
type Error = !;
|
||||
|
||||
async fn poll(&self) -> Result<Self::Value, Self::Error> {
|
||||
Ok(self.second)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,5 +0,0 @@
|
||||
#[test]
|
||||
fn tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/generate.rs");
|
||||
}
|
@ -17,6 +17,3 @@ workspace = true
|
||||
workspace = true
|
||||
[dependencies.uom]
|
||||
workspace = true
|
||||
[dependencies.embassy-sync]
|
||||
workspace = true
|
||||
optional = true
|
@ -1,9 +1,3 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod transducer;
|
||||
|
||||
pub mod cell {
|
||||
pub use physical::cell::*;
|
||||
}
|
||||
|
||||
pub use physical::CriticalError;
|
||||
pub use physical::CriticalError;
|
@ -1 +0,0 @@
|
||||
pub use physical::transducer::input::*;
|
@ -1,8 +0,0 @@
|
||||
pub mod input;
|
||||
pub mod output;
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
mod sync;
|
||||
|
||||
pub use physical::transducer::*;
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
pub use sync::*;
|
@ -1 +0,0 @@
|
||||
pub use physical::transducer::output::*;
|
@ -1,61 +0,0 @@
|
||||
use crate::cell::CellView;
|
||||
use crate::transducer::{Publish, Publisher};
|
||||
use core::cell::Cell;
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber};
|
||||
use physical::transducer::{State, Stateful};
|
||||
|
||||
pub struct StatefulPublisher<
|
||||
T: Copy,
|
||||
MutexT: RawMutex,
|
||||
const CAPACITY: usize,
|
||||
const NUM_SUBS: usize,
|
||||
> {
|
||||
pub state: State<T>,
|
||||
pub publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>,
|
||||
}
|
||||
|
||||
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
|
||||
StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
|
||||
{
|
||||
#[inline(always)]
|
||||
pub fn new(state: State<T>, publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>) -> Self {
|
||||
Self { state, publisher }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn update(&self, value: T) {
|
||||
self.state.update(value);
|
||||
self.publisher.update(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> Stateful
|
||||
for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn state_cell(&self) -> CellView<Self::Value> {
|
||||
self.state.state_cell()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn state(&self) -> Self::Value {
|
||||
self.state.state()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
|
||||
Publish<CAPACITY, NUM_SUBS> for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
|
||||
{
|
||||
type Value = T;
|
||||
type Mutex = MutexT;
|
||||
|
||||
#[inline(always)]
|
||||
fn subscribe(
|
||||
&self,
|
||||
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, Error> {
|
||||
self.publisher.subscribe()
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
mod input;
|
||||
|
||||
pub use input::*;
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::pubsub;
|
||||
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber};
|
||||
|
||||
pub trait Publish<const CAPACITY: usize, const NUM_SUBS: usize> {
|
||||
type Value: Copy;
|
||||
type Mutex: RawMutex;
|
||||
|
||||
fn subscribe(
|
||||
&self,
|
||||
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error>;
|
||||
}
|
||||
|
||||
pub struct Publisher<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> {
|
||||
channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>,
|
||||
}
|
||||
|
||||
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
|
||||
Publisher<T, MutexT, CAPACITY, NUM_SUBS>
|
||||
{
|
||||
#[inline(always)]
|
||||
pub fn new(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
|
||||
Self { channel }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn update(&self, value: T) {
|
||||
self.channel.publish_immediate(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
|
||||
Publish<CAPACITY, NUM_SUBS> for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
|
||||
{
|
||||
type Value = T;
|
||||
type Mutex = MutexT;
|
||||
|
||||
#[inline(always)]
|
||||
fn subscribe(
|
||||
&self,
|
||||
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error> {
|
||||
self.channel.subscriber()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
|
||||
From<PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>>
|
||||
for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
|
||||
Publisher::new(channel)
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
[package]
|
||||
name = "physical-ads1256"
|
||||
description = "Shared node abstractions for ADS1256 components."
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
embassy-sync = ["dep:embassy-sync", "ads1256/embassy-sync", "physical-node/embassy-sync"]
|
||||
poll = ["standard-multiplexer", "embassy-sync"]
|
||||
standard-multiplexer = []
|
||||
|
||||
[dependencies.physical-node]
|
||||
path = "../../../node"
|
||||
[dependencies.node-poll-variants]
|
||||
path = "../../../macros/node-poll-variants"
|
||||
[dependencies.ads1256]
|
||||
workspace = true
|
||||
[dependencies.embedded-hal]
|
||||
workspace = true
|
||||
[dependencies.embedded-hal-async]
|
||||
workspace = true
|
||||
[dependencies.defmt]
|
||||
workspace = true
|
||||
[dependencies.uom]
|
||||
workspace = true
|
||||
[dependencies.embassy-sync]
|
||||
workspace = true
|
||||
optional = true
|
@ -1,3 +0,0 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod standard;
|
@ -1,2 +0,0 @@
|
||||
#[cfg(feature = "standard-multiplexer")]
|
||||
pub mod multiplexer;
|
@ -1,4 +0,0 @@
|
||||
mod sync;
|
||||
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
pub use sync::*;
|
@ -1,2 +0,0 @@
|
||||
#[cfg(feature = "poll")]
|
||||
pub mod poll;
|
@ -1,382 +0,0 @@
|
||||
use ads1256::{
|
||||
AdControl, Ads1256, BlockingDelay, DataRate, Gain, Multiplexer, OutputPin, SpiBus, Status, Wait,
|
||||
};
|
||||
use core::ops::DerefMut;
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use node_poll_variants::PollVariants;
|
||||
use physical_node::transducer::input::Poll;
|
||||
use physical_node::CriticalError;
|
||||
use uom::si::f32;
|
||||
|
||||
#[derive(PollVariants)]
|
||||
#[value_type = "f32::ElectricPotential"]
|
||||
#[error_type = "CriticalError"]
|
||||
pub struct AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
|
||||
where
|
||||
DeviceMutexT: RawMutex,
|
||||
ModInT: ModInput,
|
||||
DelayerT: BlockingDelay,
|
||||
SST: OutputPin,
|
||||
DrdyT: Wait,
|
||||
SpiT: SpiBus,
|
||||
{
|
||||
input_mod: ModInT,
|
||||
ads1256: &'a Mutex<DeviceMutexT, Ads1256<DelayerT, SST, DrdyT>>,
|
||||
spi: &'a Mutex<DeviceMutexT, SpiT>,
|
||||
}
|
||||
|
||||
impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
|
||||
AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
|
||||
where
|
||||
DeviceMutexT: RawMutex,
|
||||
ModInT: ModInput,
|
||||
DelayerT: BlockingDelay,
|
||||
SST: OutputPin,
|
||||
DrdyT: Wait,
|
||||
SpiT: SpiBus,
|
||||
{
|
||||
#[inline(always)]
|
||||
pub fn new(
|
||||
input_mod: ModInT,
|
||||
ads1256: &'a Mutex<DeviceMutexT, Ads1256<DelayerT, SST, DrdyT>>,
|
||||
spi: &'a Mutex<DeviceMutexT, SpiT>,
|
||||
) -> Self {
|
||||
Self {
|
||||
input_mod,
|
||||
ads1256,
|
||||
spi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> Poll
|
||||
for AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
|
||||
where
|
||||
DeviceMutexT: RawMutex,
|
||||
ModInT: ModInput,
|
||||
DelayerT: BlockingDelay,
|
||||
SST: OutputPin,
|
||||
DrdyT: Wait,
|
||||
SpiT: SpiBus,
|
||||
{
|
||||
type Value = f32::ElectricPotential;
|
||||
type Error = CriticalError;
|
||||
|
||||
async fn poll(&self) -> Result<Self::Value, CriticalError> {
|
||||
let mut ads1256_guard = self.ads1256.lock().await;
|
||||
let ads1256 = ads1256_guard.deref_mut();
|
||||
//TODO: ADS1256 documentation seems to say we should be waiting for drdy low after
|
||||
// issuing standby command but it never goes low.
|
||||
|
||||
let result = ads1256
|
||||
.autocal_convert_m(
|
||||
self.spi,
|
||||
self.input_mod.multiplexer(),
|
||||
self.input_mod.status(),
|
||||
self.input_mod.ad_control(),
|
||||
self.input_mod.data_rate(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(conversion) => Ok(conversion.to_voltage(self.input_mod.gain())),
|
||||
Err(_) => Err(CriticalError::Communication),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModInput: Copy {
|
||||
fn multiplexer(self) -> Multiplexer;
|
||||
|
||||
fn gain(self) -> Gain;
|
||||
|
||||
fn status(self) -> Option<Status>;
|
||||
|
||||
fn ad_control(self) -> Option<AdControl>;
|
||||
|
||||
fn data_rate(self) -> Option<DataRate>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInputOnly {
|
||||
pub multiplexer: Multiplexer,
|
||||
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
|
||||
/// will not *be* set unlike the other fields.
|
||||
pub gain: Gain,
|
||||
}
|
||||
|
||||
impl ModInput for ModInputOnly {
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
fn gain(self) -> Gain {
|
||||
self.gain
|
||||
}
|
||||
|
||||
fn status(self) -> Option<Status> {
|
||||
None
|
||||
}
|
||||
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
None
|
||||
}
|
||||
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// buffer
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInStatus {
|
||||
pub multiplexer: Multiplexer,
|
||||
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
|
||||
/// will not *be* set unlike the other fields.
|
||||
pub gain: Gain,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
impl ModInput for ModInStatus {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.gain
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
Some(self.status)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// gain
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInAdControl {
|
||||
pub multiplexer: Multiplexer,
|
||||
pub ad_control: AdControl,
|
||||
}
|
||||
|
||||
impl ModInput for ModInAdControl {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.ad_control.gain()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
Some(self.ad_control)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// data rate
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInDataRate {
|
||||
pub multiplexer: Multiplexer,
|
||||
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
|
||||
/// will not *be* set unlike the other fields.
|
||||
pub gain: Gain,
|
||||
pub data_rate: DataRate,
|
||||
}
|
||||
|
||||
impl ModInput for ModInDataRate {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.gain
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
Some(self.data_rate)
|
||||
}
|
||||
}
|
||||
|
||||
/// buffer, gain
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInStatAdc {
|
||||
pub multiplexer: Multiplexer,
|
||||
pub status: Status,
|
||||
pub ad_control: AdControl,
|
||||
}
|
||||
|
||||
impl ModInput for ModInStatAdc {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.ad_control.gain()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
Some(self.status)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
Some(self.ad_control)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// buffer, data rate
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInStatDrate {
|
||||
pub multiplexer: Multiplexer,
|
||||
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
|
||||
/// will not *be* set unlike the other fields.
|
||||
pub gain: Gain,
|
||||
pub status: Status,
|
||||
pub data_rate: DataRate,
|
||||
}
|
||||
|
||||
impl ModInput for ModInStatDrate {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.gain
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
Some(self.status)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
Some(self.data_rate)
|
||||
}
|
||||
}
|
||||
|
||||
// gain, data rate
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInAdcDrate {
|
||||
pub multiplexer: Multiplexer,
|
||||
pub ad_control: AdControl,
|
||||
pub data_rate: DataRate,
|
||||
}
|
||||
|
||||
impl ModInput for ModInAdcDrate {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.ad_control.gain()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
Some(self.ad_control)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
Some(self.data_rate)
|
||||
}
|
||||
}
|
||||
|
||||
/// buffer, gain, data rate
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ModInAll {
|
||||
pub multiplexer: Multiplexer,
|
||||
pub status: Status,
|
||||
pub ad_control: AdControl,
|
||||
pub data_rate: DataRate,
|
||||
}
|
||||
|
||||
impl ModInput for ModInAll {
|
||||
#[inline(always)]
|
||||
fn multiplexer(self) -> Multiplexer {
|
||||
self.multiplexer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn gain(self) -> Gain {
|
||||
self.ad_control.gain()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn status(self) -> Option<Status> {
|
||||
Some(self.status)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ad_control(self) -> Option<AdControl> {
|
||||
Some(self.ad_control)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn data_rate(self) -> Option<DataRate> {
|
||||
Some(self.data_rate)
|
||||
}
|
||||
}
|
2
src/adc/mod.rs
Normal file
2
src/adc/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "stm32")]
|
||||
pub mod stm32;
|
13
src/adc/stm32.rs
Normal file
13
src/adc/stm32.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use uom::si::electric_potential::millivolt;
|
||||
use uom::si::quantities::ElectricPotential;
|
||||
|
||||
pub fn reading_to_voltage(
|
||||
reading: u16,
|
||||
reference_voltage: ElectricPotential<f32>,
|
||||
v_ref_int_scale: f32,
|
||||
) -> ElectricPotential<f32> {
|
||||
let reference_mv = reference_voltage.get::<millivolt>();
|
||||
let reading = f32::from(reading);
|
||||
|
||||
ElectricPotential::new::<millivolt>(reading * v_ref_int_scale / reference_mv)
|
||||
}
|
20
src/cell.rs
20
src/cell.rs
@ -1,20 +0,0 @@
|
||||
use core::cell::Cell;
|
||||
|
||||
/// Provides a view only reference to a [Cell].
|
||||
/// Useful alternative to `&Cell` when an API wants to control where a [Cell]s value can be set.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CellView<'a, T: Copy>(&'a Cell<T>);
|
||||
|
||||
impl<T: Copy> CellView<'_, T> {
|
||||
#[inline(always)]
|
||||
pub fn get(self) -> T {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Copy> From<&'a Cell<T>> for CellView<'a, T> {
|
||||
#[inline(always)]
|
||||
fn from(value: &'a Cell<T>) -> Self {
|
||||
CellView(value)
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
use crate::transducer::InvalidValue;
|
||||
/// Indicates the transducer value is statically known to be impossible.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct InvalidValue;
|
||||
|
||||
/// An error that it is likely impossible to recover from. This error should only be created in
|
||||
/// situations where attempts to recover have already been attempted and have failed. Error handling
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
pub mod transducer;
|
||||
pub mod control;
|
||||
pub mod cell;
|
||||
pub mod error;
|
||||
|
||||
pub mod adc;
|
||||
|
||||
pub use error::CriticalError;
|
||||
|
@ -1,6 +0,0 @@
|
||||
pub trait Poll {
|
||||
type Value: Copy;
|
||||
type Error: Copy;
|
||||
|
||||
async fn poll(&self) -> Result<Self::Value, Self::Error>;
|
||||
}
|
@ -1,62 +1,3 @@
|
||||
use core::cell::Cell;
|
||||
use crate::cell::CellView;
|
||||
|
||||
pub mod input;
|
||||
pub mod output;
|
||||
mod part;
|
||||
|
||||
pub use part::*;
|
||||
|
||||
// Initialisation will always be async and won't complete until a state is available for all
|
||||
// stateful transducers.
|
||||
pub trait Stateful {
|
||||
type Value: Copy;
|
||||
|
||||
fn state_cell(&self) -> CellView<Self::Value>;
|
||||
|
||||
fn state(&self) -> Self::Value;
|
||||
}
|
||||
|
||||
pub struct State<T: Copy> {
|
||||
state_cell: Cell<T>,
|
||||
}
|
||||
|
||||
impl<T: Copy> State<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(state_cell: Cell<T>) -> Self {
|
||||
Self { state_cell }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn update(&self, value: T) {
|
||||
self.state_cell.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Stateful for State<T> {
|
||||
type Value = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn state_cell(&self) -> CellView<Self::Value> {
|
||||
(&self.state_cell).into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn state(&self) -> Self::Value {
|
||||
self.state_cell.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<Cell<T>> for State<T> {
|
||||
#[inline(always)]
|
||||
fn from(state_cell: Cell<T>) -> Self {
|
||||
State::new(state_cell)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Error ------------------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
/// Indicates the transducer value is statically known to be impossible.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct InvalidValue;
|
||||
pub use part::*;
|
@ -1,8 +0,0 @@
|
||||
use crate::transducer::InvalidValue;
|
||||
|
||||
pub trait Output {
|
||||
type Value: Copy;
|
||||
|
||||
//TODO: Should this be maybe async?
|
||||
fn output(&mut self, setting: Self::Value) -> Result<(), InvalidValue>;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::transducer::InvalidValue;
|
||||
use crate::error::InvalidValue;
|
||||
use uom::si::electric_potential::volt;
|
||||
use uom::si::quantities::{ElectricPotential, ThermodynamicTemperature};
|
||||
use uom::si::thermodynamic_temperature::degree_celsius;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::transducer::InvalidValue;
|
||||
use crate::error::InvalidValue;
|
||||
use libm::powf;
|
||||
use uom::si::electric_potential::millivolt;
|
||||
use uom::si::quantities::{ElectricPotential, ThermodynamicTemperature};
|
||||
|
Reference in New Issue
Block a user