Compare commits

..

7 Commits

Author SHA1 Message Date
Zachary Levy d0b792be0e Update rustfmt.toml 2026-04-10 13:52:51 -07:00
Zachary Levy d6300afe50 formatting rules 2026-04-10 13:49:22 -07:00
Zachary Levy 349e8efdb6 derive neg for quantity 2026-04-10 11:37:00 -07:00
Zachary Levy 52c2169e1c Formatting and derive copy for counter config 2026-04-09 20:53:14 -07:00
Zachary Levy b3906b08e4 Added stm32 counter abstraction 2026-04-08 19:15:00 -07:00
Zachary Levy fef05b937d USB type alias 2026-04-02 14:24:12 -07:00
Zachary Levy 18f7e19726 Update dependencies and do necessary migrations 2026-04-02 12:02:35 -07:00
20 changed files with 328 additions and 112 deletions
+28 -17
View File
@@ -16,8 +16,8 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.4.6" version = "0.5.0"
edition = "2021" edition = "2024"
repository = "https://git.bfpower.io/BFPOWER/physical" repository = "https://git.bfpower.io/BFPOWER/physical"
readme = "README.md" readme = "README.md"
license = "MIT" license = "MIT"
@@ -30,14 +30,14 @@ default-features = false
[workspace.dependencies.libm] [workspace.dependencies.libm]
version = "0.2.*" version = "0.2.*"
[workspace.dependencies.float-cmp] [workspace.dependencies.float-cmp]
version = "0.9.*" version = "0.10.*"
# Logging # Logging
[workspace.dependencies.tracing] [workspace.dependencies.tracing]
version = "0.1.*" version = "0.1.*"
[workspace.dependencies.defmt] [workspace.dependencies.defmt]
version = "0.3.*" version = "1.0.*"
[workspace.dependencies.defmt-rtt] [workspace.dependencies.defmt-rtt]
version = "0.4.*" version = "1.1.*"
# Embedded-HAL # Embedded-HAL
[workspace.dependencies.embedded-hal] [workspace.dependencies.embedded-hal]
version = "1.0.*" version = "1.0.*"
@@ -46,7 +46,7 @@ version = "1.0.*"
# Memory # Memory
[workspace.dependencies.static_cell] [workspace.dependencies.static_cell]
version = "2.1.*" version = "2.1.*"
# Serioalization # Serialization
[workspace.dependencies.serde] [workspace.dependencies.serde]
version = "1.0.*" version = "1.0.*"
default-features = false default-features = false
@@ -57,34 +57,45 @@ version = "0.7.*"
[workspace.dependencies.cortex-m-rt] [workspace.dependencies.cortex-m-rt]
version = "0.7.*" version = "0.7.*"
[workspace.dependencies.panic-probe] [workspace.dependencies.panic-probe]
version = "0.3.*" version = "1.0.*"
features = ["print-defmt"] features = ["print-defmt"]
# Embassy # Embassy
[workspace.dependencies.embassy-futures] [workspace.dependencies.embassy-futures]
version = "0.1.*" version = "0.1.*"
[workspace.dependencies.embassy-time] [workspace.dependencies.embassy-time]
version = "0.3.*" version = "0.5.*"
features = ["defmt", "defmt-timestamp-uptime"] features = ["defmt", "defmt-timestamp-uptime"]
[workspace.dependencies.embassy-sync] [workspace.dependencies.embassy-sync]
version = "0.6.*" version = "0.8.*"
features = ["defmt"] features = ["defmt"]
[workspace.dependencies.embassy-embedded-hal] [workspace.dependencies.embassy-embedded-hal]
version = "0.1.*" version = "0.6.*"
[workspace.dependencies.embassy-executor] [workspace.dependencies.embassy-executor]
version = "0.5.*" version = "0.10.*"
features = ["defmt", "arch-cortex-m", "integrated-timers", "executor-interrupt", "executor-thread"] features = ["defmt", "platform-cortex-m", "executor-interrupt", "executor-thread"]
[workspace.dependencies.embassy-usb] [workspace.dependencies.embassy-usb]
version = "0.2.*" version = "0.6.*"
features = ["defmt"] features = ["defmt"]
[workspace.dependencies.embassy-stm32] [workspace.dependencies.embassy-stm32]
version = "0.1.*" version = "0.6.*"
features = ["defmt", "unstable-pac"] features = ["defmt", "unstable-pac", "gpio-init-analog"]
[workspace.dependencies.embassy-nrf] [workspace.dependencies.embassy-nrf]
version = "0.1.*" version = "0.10.*"
features = ["defmt"] features = ["defmt"]
# Meta # Meta
[workspace.dependencies.cfg-if]
version = "1.0.*"
[workspace.dependencies.derive_more] [workspace.dependencies.derive_more]
version = "0.99.*" version = "2.1.*"
default-features = false
features = ["add", "add_assign", "not", "display"]
[workspace.dependencies.thiserror]
version = "2.0.*"
default-features = false
[workspace.dependencies.bitfields]
version = "1.0.*"
[workspace.dependencies.enumflags2]
version = "0.7.*"
[workspace.dependencies.syn] [workspace.dependencies.syn]
version = "2.0.*" version = "2.0.*"
features = ["extra-traits", "parsing"] features = ["extra-traits", "parsing"]
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::{ use crate::{
drate, mux, opcodes, status, AdControl, Ads1256, BlockingDelay, CalibrationCommand, Conversion, drate, mux, opcodes, status, AdControl, Ads1256, BlockingDelay, CalibrationCommand, Conversion,
DataRate, Gain, Multiplexer, Status, DataRate, Multiplexer, Status,
}; };
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal::spi; use embedded_hal::spi;
+3
View File
@@ -6,10 +6,13 @@ mod io;
#[cfg(feature = "embassy-sync")] #[cfg(feature = "embassy-sync")]
mod mutex; mod mutex;
#[allow(unused_imports)]
pub use crate::adc::*; pub use crate::adc::*;
pub use crate::delay::*; pub use crate::delay::*;
#[allow(unused_imports)]
pub use crate::io::*; pub use crate::io::*;
#[cfg(feature = "embassy-sync")] #[cfg(feature = "embassy-sync")]
#[allow(unused_imports)]
pub use crate::mutex::*; pub use crate::mutex::*;
pub use ads1256_types::adcon::{ClockOut, Gain, Sdcs}; pub use ads1256_types::adcon::{ClockOut, Gain, Sdcs};
pub use ads1256_types::drate::DataRate; pub use ads1256_types::drate::DataRate;
+1 -1
View File
@@ -157,7 +157,7 @@ impl Status {
#[inline(always)] #[inline(always)]
pub const fn data_ready(self) -> bool { pub const fn data_ready(self) -> bool {
const MASK: u8 = 0b1; const MASK: u8 = 0b1;
unsafe { mem::transmute::<u8, bool>(self.0 & MASK) } unsafe { !mem::transmute::<u8, bool>(self.0 & MASK) }
} }
#[inline(always)] #[inline(always)]
+1 -1
View File
@@ -1,5 +1,5 @@
pub mod input { pub mod input {
use crate::{Buffer, Config, DataRate, Gain, Multiplexer, MuxInput}; use crate::{Multiplexer, MuxInput};
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]
+1 -1
View File
@@ -34,4 +34,4 @@ features = ["tick-hz-16_000_000"]
[dependencies.panic-probe] [dependencies.panic-probe]
workspace = true workspace = true
[dependencies] [dependencies]
log = "0.4.20" log = "0.4.*"
+45 -33
View File
@@ -1,27 +1,25 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use cortex_m::prelude::{_embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
use {embassy_executor as executor, embassy_stm32 as stm32}; use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::{ use ads1256::{
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DState, DataRate, DigitalIo, AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config,
DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, MuxInput, OutputPin, Sdcs, DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, Gain, Multiplexer,
SpiBus, Status, Wait, BlockingDelay MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait,
}; };
use embassy_time::{Delay, Timer}; use embassy_time::Delay;
use executor::Spawner; use executor::Spawner;
use physical::quantity::Quantity; use physical::quantity::Quantity;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput; use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed}; use stm32::gpio::{Level, Output, Pull, Speed};
use stm32::spi::Spi; use stm32::spi::Spi;
use stm32::time::Hertz; use stm32::time::Hertz;
use stm32::{pac, spi}; use stm32::{pac, spi};
use defmt::{debug, error, info, trace, unwrap}; use defmt::info;
const AUTOCAL_CONF: Config = Config { const AUTOCAL_CONF: Config = Config {
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
@@ -33,15 +31,17 @@ const AUTOCAL_CONF: Config = Config {
const ADS1256_DELAY: ads1256::DefaultDelay<Delay> = ads1256::DefaultDelay::new(Delay); const ADS1256_DELAY: ads1256::DefaultDelay<Delay> = ads1256::DefaultDelay::new(Delay);
stm32::bind_interrupts!(struct Irqs {
EXTI3 => stm32::exti::InterruptHandler<stm32::interrupt::typelevel::EXTI3>;
});
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(_spawner: Spawner) {
unsafe { pac::FLASH.acr().modify(|v| {
pac::FLASH.acr().modify(|v| { v.set_prften(true);
v.set_prften(true); v.set_icen(true);
v.set_icen(true); v.set_dcen(true);
v.set_dcen(true); });
});
}
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
@@ -50,18 +50,10 @@ async fn main(spawner: Spawner) {
spi_conf.bit_order = spi::BitOrder::MsbFirst; spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ); spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3); let ads1256_data_ready = ExtiInput::new(p.PA3, p.EXTI3, Pull::Up, Irqs);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh); let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let mut spi = Spi::new( let mut spi = Spi::new_blocking(p.SPI1, p.PA5, p.PA7, p.PA6, spi_conf);
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
);
let mut ads_1256 = Ads1256::new(ADS1256_DELAY, select_ads1256, ads1256_data_ready); let mut ads_1256 = Ads1256::new(ADS1256_DELAY, select_ads1256, ads1256_data_ready);
// single_conversion(&mut spi, &mut ads_1256).await; // single_conversion(&mut spi, &mut ads_1256).await;
@@ -70,6 +62,7 @@ async fn main(spawner: Spawner) {
cycle_multiplexer(&mut spi, &mut ads_1256).await; cycle_multiplexer(&mut spi, &mut ads_1256).await;
} }
#[allow(dead_code)]
async fn single_conversion<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( async fn single_conversion<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
@@ -78,9 +71,14 @@ async fn single_conversion<DelayerT: ads1256::BlockingDelay, SST: OutputPin, Drd
ads_1256.delayer.t11_1_delay(); ads_1256.delayer.t11_1_delay();
ads_1256.conversion_init(spi).unwrap(); ads_1256.conversion_init(spi).unwrap();
let data = ads_1256.cmd_read_data(spi).await.unwrap(); let data = ads_1256.cmd_read_data(spi).await.unwrap();
info!("data: {}, volts: {}", data, data.to_voltage(AUTOCAL_CONF.ad_control.gain()).fmt(Some(5))); info!(
"data: {}, volts: {}",
data,
data.to_voltage(AUTOCAL_CONF.ad_control.gain()).fmt(Some(5))
);
} }
#[allow(dead_code)]
async fn read_continuous<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( async fn read_continuous<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
@@ -90,7 +88,11 @@ async fn read_continuous<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT
ads_1256.start_rdatac(spi).await.unwrap(); ads_1256.start_rdatac(spi).await.unwrap();
loop { loop {
let data = ads_1256.read_data(spi).await.unwrap(); let data = ads_1256.read_data(spi).await.unwrap();
info!("data: {}, volts: {}", data, data.to_voltage(AUTOCAL_CONF.ad_control.gain()).fmt(Some(5))); info!(
"data: {}, volts: {}",
data,
data.to_voltage(AUTOCAL_CONF.ad_control.gain()).fmt(Some(5))
);
} }
} }
@@ -103,7 +105,6 @@ async fn cycle_multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, Drd
const INPUT_3: Multiplexer = Multiplexer::setting(MuxInput::AIn3, MuxInput::Common); const INPUT_3: Multiplexer = Multiplexer::setting(MuxInput::AIn3, MuxInput::Common);
let ad_control = AUTOCAL_CONF.ad_control; let ad_control = AUTOCAL_CONF.ad_control;
let status = AUTOCAL_CONF.status;
ads_1256.write_config(spi, AUTOCAL_CONF).unwrap(); ads_1256.write_config(spi, AUTOCAL_CONF).unwrap();
ads_1256.delayer.t11_1_delay(); ads_1256.delayer.t11_1_delay();
@@ -113,19 +114,30 @@ async fn cycle_multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, Drd
.autocal_convert(spi, INPUT_1, None, Some(ad_control.with_gain(Gain::X4)), None, false) .autocal_convert(spi, INPUT_1, None, Some(ad_control.with_gain(Gain::X4)), None, false)
.await .await
.unwrap(); .unwrap();
info!("Input 1: data: {}, volts: {}", data, data.to_voltage(ad_control.gain()).fmt(Some(5))); info!(
"Input 1: data: {}, volts: {}",
data,
data.to_voltage(ad_control.gain()).fmt(Some(5))
);
let ad_control = ad_control.with_gain(Gain::X8); let ad_control = ad_control.with_gain(Gain::X8);
let data = ads_1256 let data = ads_1256
.autocal_convert(spi, INPUT_2, None, Some(ad_control.with_gain(Gain::X8)), None, false) .autocal_convert(spi, INPUT_2, None, Some(ad_control.with_gain(Gain::X8)), None, false)
.await .await
.unwrap(); .unwrap();
info!("Input 2: data: {}, volts: {}", data, data.to_voltage(ad_control.gain()).fmt(Some(5))); info!(
"Input 2: data: {}, volts: {}",
data,
data.to_voltage(ad_control.gain()).fmt(Some(5))
);
let ad_control = ad_control.with_gain(Gain::X16); let ad_control = ad_control.with_gain(Gain::X16);
let data = ads_1256 let data = ads_1256
.autocal_convert(spi, INPUT_3, None, Some(ad_control.with_gain(Gain::X16)), None, false) .autocal_convert(spi, INPUT_3, None, Some(ad_control.with_gain(Gain::X16)), None, false)
.await .await
.unwrap(); .unwrap();
info!("Input 3: data: {}, volts: {}", data, data.to_voltage(ad_control.gain()).fmt(Some(5))); info!(
"Input 3: data: {}, volts: {}",
data,
data.to_voltage(ad_control.gain()).fmt(Some(5))
);
} }
} }
+22 -23
View File
@@ -1,9 +1,9 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
use embassy_stm32::bind_interrupts;
use {embassy_executor as executor, embassy_stm32 as stm32}; use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::{ use ads1256::{
@@ -13,26 +13,28 @@ use ads1256::{
}; };
use embassy_time::Delay; use embassy_time::Delay;
use executor::Spawner; use executor::Spawner;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput; use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed}; use stm32::gpio::{Level, Output, Pull, Speed};
use stm32::spi::Spi; use stm32::spi::Spi;
use stm32::time::Hertz; use stm32::time::Hertz;
use stm32::{pac, spi}; use stm32::{pac, spi};
use defmt::{debug, error, info, trace, unwrap}; use defmt::info;
const ADS1256_DELAY: ads1256::DefaultDelay<Delay> = ads1256::DefaultDelay::new(Delay); const ADS1256_DELAY: ads1256::DefaultDelay<Delay> = ads1256::DefaultDelay::new(Delay);
bind_interrupts!(struct Irqs {
EXTI3 => stm32::exti::InterruptHandler<stm32::interrupt::typelevel::EXTI3>;
});
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(_spawner: Spawner) {
unsafe { pac::FLASH.acr().modify(|v| {
pac::FLASH.acr().modify(|v| { v.set_prften(true);
v.set_prften(true); v.set_icen(true);
v.set_icen(true); v.set_dcen(true);
v.set_dcen(true); });
});
}
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
@@ -41,18 +43,10 @@ async fn main(spawner: Spawner) {
spi_conf.bit_order = spi::BitOrder::MsbFirst; spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ); spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3); let ads1256_data_ready = ExtiInput::new(p.PA3, p.EXTI3, Pull::Up, Irqs);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh); let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let mut spi = Spi::new( let mut spi = Spi::new_blocking(p.SPI1, p.PA5, p.PA7, p.PA6, spi_conf);
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
);
let mut ads_1256 = Ads1256::new(ADS1256_DELAY, select_ads1256, ads1256_data_ready); let mut ads_1256 = Ads1256::new(ADS1256_DELAY, select_ads1256, ads1256_data_ready);
// status(&mut spi, &mut ads_1256); // status(&mut spi, &mut ads_1256);
@@ -60,9 +54,10 @@ async fn main(spawner: Spawner) {
// ad_control(&mut spi, &mut ads_1256); // ad_control(&mut spi, &mut ads_1256);
// data_rate(&mut spi, &mut ads_1256); // data_rate(&mut spi, &mut ads_1256);
// gpio(&mut spi, &mut ads_1256); // gpio(&mut spi, &mut ads_1256);
config(&mut spi, &mut ads_1256); config(&mut spi, &mut ads_1256);
} }
#[allow(dead_code)]
fn status<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( fn status<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
@@ -79,6 +74,7 @@ fn status<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
info!("ADS1256 new status: {}", status); info!("ADS1256 new status: {}", status);
} }
#[allow(dead_code)]
fn multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( fn multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
@@ -98,6 +94,7 @@ fn multiplexer<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
info!("ADS1256 new ad_control: {}", multiplexer); info!("ADS1256 new ad_control: {}", multiplexer);
} }
#[allow(dead_code)]
fn ad_control<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( fn ad_control<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
@@ -115,6 +112,7 @@ fn ad_control<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
info!("ADS1256 new ad_control: {}", ad_control); info!("ADS1256 new ad_control: {}", ad_control);
} }
#[allow(dead_code)]
fn data_rate<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( fn data_rate<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
@@ -129,6 +127,7 @@ fn data_rate<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
info!("ADS1256 new data rate: {}", data_rate); info!("ADS1256 new data rate: {}", data_rate);
} }
#[allow(dead_code)]
fn gpio<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>( fn gpio<DelayerT: ads1256::BlockingDelay, SST: OutputPin, DrdyT: Wait>(
spi: &mut impl SpiBus, spi: &mut impl SpiBus,
ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>, ads_1256: &mut Ads1256<DelayerT, SST, DrdyT>,
+3 -2
View File
@@ -4,7 +4,7 @@ use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote; use quote::quote;
use std::ops::Deref; use std::ops::Deref;
use syn::parse::{Parse, ParseStream}; use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, Ident, LitStr, Token}; use syn::{Ident, LitStr, Token, parse_macro_input};
const NUMBER_TYPES: &[&str] = &[ const NUMBER_TYPES: &[&str] = &[
"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "u128", "i128", "usize", "isize", "f32", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "u128", "i128", "usize", "isize", "f32",
@@ -108,9 +108,10 @@ pub fn quantity_type(input: TokenStream) -> TokenStream {
derive_more::AddAssign, derive_more::AddAssign,
derive_more::Sub, derive_more::Sub,
derive_more::SubAssign, derive_more::SubAssign,
derive_more::Neg,
derive_more::Display derive_more::Display
)] )]
#[display(fmt = "{} {}", _0, "Self::symbol()")] #[display("{_0} {}", Self::symbol())]
#[repr(transparent)] #[repr(transparent)]
pub struct #struct_name<V: Value>(pub V); pub struct #struct_name<V: Value>(pub V);
+3
View File
@@ -11,10 +11,13 @@ license.workspace = true
comms = [] comms = []
single-packet-msgs = [] single-packet-msgs = []
usb = ["embassy-usb"] usb = ["embassy-usb"]
counter = []
stm32 = ["embassy-stm32", "physical/stm32"] stm32 = ["embassy-stm32", "physical/stm32"]
[dependencies.physical] [dependencies.physical]
path = ".." path = ".."
[dependencies.cfg-if]
workspace = true
[dependencies.embedded-hal] [dependencies.embedded-hal]
workspace = true workspace = true
[dependencies.embedded-hal-async] [dependencies.embedded-hal-async]
+2
View File
@@ -1,7 +1,9 @@
#[allow(async_fn_in_trait)]
pub trait Sender { pub trait Sender {
async fn send(&mut self, msg: &[u8]) -> Result<(), Reset>; async fn send(&mut self, msg: &[u8]) -> Result<(), Reset>;
} }
#[allow(async_fn_in_trait)]
pub trait Receiver { pub trait Receiver {
async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Reset>; async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Reset>;
} }
+181
View File
@@ -0,0 +1,181 @@
//! Hardware pulse counter using a timer in External Clock Mode.
//!
//! Counts edges on an external signal entirely in hardware. Supports three
//! pin sources per timer: the dedicated ETR pin, or any CH1/CH2 pin.
use embassy_stm32::Peri;
use embassy_stm32::gpio::{AfType, Flex, Pull};
use embassy_stm32::pac::timer::vals::{Etp, Etps};
use embassy_stm32::timer::low_level::{
FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource,
};
use embassy_stm32::timer::{
Ch1, Ch2, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin,
};
/// Which edge increments the counter.
#[derive(Clone, Copy, Default, defmt::Format)]
pub enum CountEdge {
#[default]
Rising,
Falling,
/// Count on both rising and falling edges.
/// Only supported with channel pins (CH1/CH2), not ETR.
Both,
}
/// Pulse counter configuration.
#[derive(Copy, Clone)]
pub struct PulseCounterConfig {
pub edge: CountEdge,
pub filter: FilterValue,
pub pull: Pull,
}
impl Default for PulseCounterConfig {
fn default() -> Self {
Self {
edge: CountEdge::Rising,
filter: FilterValue::NO_FILTER,
pull: Pull::None,
}
}
}
/// Hardware pulse counter driven by an external signal.
///
/// Takes ownership of the timer and pin, preventing reuse elsewhere.
pub struct PulseCounter<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,
_pin: Flex<'d>,
}
// ---- Constructors ----
impl<'d, T: GeneralInstance4Channel> PulseCounter<'d, T> {
/// Count pulses on the timer's ETR (External Trigger) pin.
///
/// # Panics
/// Panics if `config.edge` is [`CountEdge::Both`] — ETR only supports single-edge detection.
pub fn new_etr(
tim: Peri<'d, T>,
pin: Peri<'d, impl ExternalTriggerPin<T>>,
config: PulseCounterConfig,
) -> Self {
assert!(
!matches!(config.edge, CountEdge::Both),
"ETR does not support both-edge detection"
);
let af_num = pin.af_num();
let mut flex_pin = Flex::new(pin);
flex_pin.set_as_af_unchecked(af_num, AfType::input(config.pull));
let inner = Timer::new(tim);
inner.set_slave_mode(SlaveMode::EXT_CLOCK_MODE);
inner.set_trigger_source(TriggerSource::ETRF);
inner.set_external_trigger_filter(config.filter);
inner.set_external_trigger_prescaler(Etps::DIV1);
inner.set_external_trigger_polarity(match config.edge {
CountEdge::Rising => Etp::from(0),
CountEdge::Falling => Etp::from(1),
CountEdge::Both => unreachable!(),
});
inner.start();
Self {
inner,
_pin: flex_pin,
}
}
/// Count pulses on a CH1 pin.
pub fn new_ch1(
tim: Peri<'d, T>,
pin: Peri<'d, impl TimerPin<T, Ch1>>,
config: PulseCounterConfig,
) -> Self {
let af_num = pin.af_num();
let mut flex_pin = Flex::new(pin);
flex_pin.set_as_af_unchecked(af_num, AfType::input(config.pull));
let inner = Timer::new(tim);
Self::configure_channel(&inner, Channel::Ch1, TriggerSource::TI1FP1, &config);
inner.start();
Self {
inner,
_pin: flex_pin,
}
}
/// Count pulses on a CH2 pin.
pub fn new_ch2(
tim: Peri<'d, T>,
pin: Peri<'d, impl TimerPin<T, Ch2>>,
config: PulseCounterConfig,
) -> Self {
let af_num = pin.af_num();
let mut flex_pin = Flex::new(pin);
flex_pin.set_as_af_unchecked(af_num, AfType::input(config.pull));
let inner = Timer::new(tim);
Self::configure_channel(&inner, Channel::Ch2, TriggerSource::TI2FP2, &config);
inner.start();
Self {
inner,
_pin: flex_pin,
}
}
/// Common channel setup: put the channel in input mode, configure its
/// filter and edge polarity, then wire it as the slave-mode trigger.
fn configure_channel(
inner: &Timer<'d, T>,
channel: Channel,
trigger: TriggerSource,
config: &PulseCounterConfig,
) {
// Channel must be in input mode for the ICxF filter bits to exist.
// Normal = direct mapping (TI1→IC1, TI2→IC2).
inner.set_input_ti_selection(channel, InputTISelection::Normal);
inner.set_input_capture_filter(channel, config.filter);
inner.set_input_capture_mode(
channel,
match config.edge {
CountEdge::Rising => InputCaptureMode::Rising,
CountEdge::Falling => InputCaptureMode::Falling,
CountEdge::Both => InputCaptureMode::BothEdges,
},
);
inner.set_trigger_source(trigger);
inner.set_slave_mode(SlaveMode::EXT_CLOCK_MODE);
}
}
// ---- Reading ----
impl<'d, T: GeneralInstance4Channel> PulseCounter<'d, T> {
/// Read the current count (lower 16 bits).
#[inline]
pub fn count(&self) -> u16 {
self.inner.regs_gp16().cnt().read().cnt()
}
/// Reset the counter to zero.
#[inline]
pub fn reset(&self) {
self.inner.reset();
}
}
impl<'d, T: embassy_stm32::timer::GeneralInstance32bit4Channel> PulseCounter<'d, T> {
/// Read the current count as a full 32-bit value.
#[inline]
pub fn count_32(&self) -> u32 {
self.inner.regs_gp32().cnt().read()
}
}
+2
View File
@@ -1,2 +1,4 @@
#[cfg(feature = "usb")] #[cfg(feature = "usb")]
pub mod usb; pub mod usb;
#[cfg(feature = "counter")]
pub mod counter;
+9 -7
View File
@@ -3,13 +3,15 @@
use crate::comms; use crate::comms;
use embassy_stm32::peripherals::USB_OTG_FS; use embassy_stm32::peripherals::USB_OTG_FS;
use embassy_stm32::usb_otg::{Driver, Endpoint, In, Out}; use embassy_stm32::usb::Driver as StmUsbDriver;
use embassy_usb::driver::{EndpointIn, EndpointOut}; use embassy_usb::driver::{Driver as UsbDriverTrait, EndpointIn, EndpointOut};
use embassy_usb::UsbDevice; use embassy_usb::UsbDevice;
pub type TypedUSB = UsbDevice<'static, Driver<'static, USB_OTG_FS>>; type UsbDriver = StmUsbDriver<'static, USB_OTG_FS>;
pub type TypedInterIn = Endpoint<'static, USB_OTG_FS, In>; pub type TypedInterIn = <UsbDriver as UsbDriverTrait<'static>>::EndpointIn;
pub type TypedInterOut = Endpoint<'static, USB_OTG_FS, Out>; pub type TypedInterOut = <UsbDriver as UsbDriverTrait<'static>>::EndpointOut;
pub type TypedUSB = UsbDevice<'static, UsbDriver>;
pub struct UsbIO { pub struct UsbIO {
/// Send to master /// Send to master
@@ -18,13 +20,13 @@ pub struct UsbIO {
pub interrupt_out: TypedInterOut, pub interrupt_out: TypedInterOut,
} }
impl comms::Sender for TypedInterIn { impl<T: EndpointIn> comms::Sender for T {
async fn send(&mut self, msg: &[u8]) -> Result<(), comms::Reset> { async fn send(&mut self, msg: &[u8]) -> Result<(), comms::Reset> {
self.write(msg).await.map_err(|_| comms::Reset) self.write(msg).await.map_err(|_| comms::Reset)
} }
} }
impl comms::Receiver for TypedInterOut { impl<T: EndpointOut> comms::Receiver for T {
#[cfg(feature = "single-packet-msgs")] #[cfg(feature = "single-packet-msgs")]
async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), comms::Reset> { async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), comms::Reset> {
// This is OK when all our messages are smaller than a single packet. // This is OK when all our messages are smaller than a single packet.
+2 -2
View File
@@ -1,8 +1,8 @@
# Before upgrading check that everything is available on all tier1 targets here: # Before upgrading check that everything is available on all tier1 targets here:
# https://rust-lang.github.io/rustup-components-history # https://rust-lang.github.io/rustup-components-history
[toolchain] [toolchain]
channel = "1.82" channel = "stable"
components = [ "rust-src", "rustfmt", "llvm-tools" ] components = [ "rust-src", "rustfmt" ]
targets = [ targets = [
"thumbv7em-none-eabi", "thumbv7em-none-eabi",
"thumbv7m-none-eabi", "thumbv7m-none-eabi",
+5 -3
View File
@@ -2,7 +2,9 @@ imports_granularity = "Module"
format_strings = true format_strings = true
wrap_comments = true wrap_comments = true
match_block_trailing_comma = true match_block_trailing_comma = true
enum_discrim_align_threshold = 25 enum_discrim_align_threshold = 50
fn_call_width = 100 max_width = 110
chain_width = 110
fn_call_width = 110
single_line_if_else_max_width = 110
comment_width = 120 comment_width = 120
single_line_if_else_max_width = 100
+10 -13
View File
@@ -6,9 +6,6 @@ mod volume;
mod volume_rate; mod volume_rate;
mod pressure; mod pressure;
#[cfg(feature = "defmt")]
pub use defmt_impl::*;
pub use irradiance::*; pub use irradiance::*;
pub use resistance::*; pub use resistance::*;
pub use temperature::*; pub use temperature::*;
@@ -22,16 +19,16 @@ use core::marker::PhantomData;
use core::ops::{Add, Sub}; use core::ops::{Add, Sub};
use num_traits::{FromPrimitive, Num, NumCast}; use num_traits::{FromPrimitive, Num, NumCast};
const DECA: u8 = 10; pub const DECA: u8 = 10;
const DECI: u8 = 10; pub const DECI: u8 = 10;
const HECTO: u8 = 100; pub const HECTO: u8 = 100;
const CENTI: u8 = 100; pub const CENTI: u8 = 100;
const KILO: u16 = 1_000; pub const KILO: u16 = 1_000;
const MILLI: u16 = 1_000; pub const MILLI: u16 = 1_000;
const MEGA: u32 = 1_000_000; pub const MEGA: u32 = 1_000_000;
const MICRO: u32 = 1_000_000; pub const MICRO: u32 = 1_000_000;
const GIGA: u32 = 1_000_000_000; pub const GIGA: u32 = 1_000_000_000;
const NANO: u32 = 1_000_000_000; pub const NANO: u32 = 1_000_000_000;
pub trait Quantity<V: Value>: Copy + PartialEq + PartialOrd + Add + Sub { pub trait Quantity<V: Value>: Copy + PartialEq + PartialOrd + Add + Sub {
fn value(self) -> V; fn value(self) -> V;
+2 -2
View File
@@ -19,9 +19,9 @@ quantity_type! {MilliLiters, "mL"}
impl<V: Value> MilliLiters<V> { impl<V: Value> MilliLiters<V> {
#[inline] #[inline]
pub fn to_liters(self) -> MilliLiters<V> { pub fn to_liters(self) -> Liters<V> {
let divisor = V::from_u16(MILLI).unwrap(); let divisor = V::from_u16(MILLI).unwrap();
MilliLiters(self.0 / divisor) Liters(self.0 / divisor)
} }
} }
+1
View File
@@ -1,3 +1,4 @@
mod part; mod part;
#[allow(unused_imports)]
pub use part::*; pub use part::*;
+4 -4
View File
@@ -1,6 +1,6 @@
//! Note - Thermocouple conversion uses [f64] arithmetic internally. //! Note - Thermocouple conversion uses [f64] arithmetic internally.
use libm::pow; use libm::exp;
use crate::error::InvalidValue; use crate::error::InvalidValue;
use crate::quantity::{Celsius, MilliVolts, Quantity}; use crate::quantity::{Celsius, MilliVolts, Quantity};
@@ -45,7 +45,7 @@ fn _convert(
Ok(Celsius(celsius as f32)) Ok(Celsius(celsius as f32))
} else if mv >= 20.644 && mv <= 54.886 { } else if mv >= 20.644 && mv <= 54.886 {
let celsius = 1.318058e2 let celsius = -1.318058e2
+ 4.830222E+1 * mv + 4.830222E+1 * mv
+ -1.646031 * mv_pow2 + -1.646031 * mv_pow2
+ 5.464731E-2 * mv_pow3 + 5.464731E-2 * mv_pow3
@@ -136,8 +136,8 @@ pub fn temp_to_voltage_poly(
Ok(MilliVolts(mv as f32)) Ok(MilliVolts(mv as f32))
} else if celsius >= 0.0 && celsius <= 1372.0 { } else if celsius >= 0.0 && celsius <= 1372.0 {
let base = celsius - 0.126968600000E+03; let base = celsius - 0.126968600000E+03;
let exp = -0.118343200000E-03 * (base * base); let exponent = -0.118343200000E-03 * (base * base);
let addition = pow(0.1185976, exp); let addition = 0.1185976 * exp(exponent);
let mv = -0.176004136860E-01 let mv = -0.176004136860E-01
+ 0.389212049750E-01 * celsius + 0.389212049750E-01 * celsius