diff --git a/Cargo.lock b/Cargo.lock index 205bf6d..089e2b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ads126x" +version = "0.1.0" +dependencies = [ + "bitflags 2.6.0", + "cortex-m", + "defmt", + "defmt-test", + "embedded-hal 0.2.7", + "heapless 0.7.17", + "nb 1.1.0", + "panic-probe", + "stm32h7xx-hal", +] + [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -613,6 +628,7 @@ dependencies = [ name = "pressure" version = "0.1.0" dependencies = [ + "ads126x", "chrono", "common-arm", "cortex-m", @@ -946,6 +962,7 @@ dependencies = [ name = "strain" version = "0.1.0" dependencies = [ + "ads126x", "chrono", "common-arm", "cortex-m", @@ -991,6 +1008,7 @@ dependencies = [ name = "temperature" version = "0.1.0" dependencies = [ + "ads126x", "chrono", "common-arm", "cortex-m", diff --git a/boards/pressure/Cargo.toml b/boards/pressure/Cargo.toml index cabae2e..d157ffc 100644 --- a/boards/pressure/Cargo.toml +++ b/boards/pressure/Cargo.toml @@ -22,6 +22,7 @@ defmt-rtt = { workspace = true } panic-probe = { workspace = true } chrono = { workspace = true } messages = {workspace = true} +ads126x = {path = "../../crates/ads126x"} [dev-dependencies] defmt-test = { workspace = true } diff --git a/boards/pressure/src/adc_manager.rs b/boards/pressure/src/adc_manager.rs new file mode 100644 index 0000000..73e8d1f --- /dev/null +++ b/boards/pressure/src/adc_manager.rs @@ -0,0 +1,81 @@ +use ads126x::{ + register::{DataRate, Mode1Register, Mode2Register}, + ADCCommand, Ads126x, +}; + +use common_arm::spawn; +use stm32h7xx_hal::{ + gpio::{Output, Pin, PushPull}, + spi::Spi, +}; + +use crate::app::delay; + +// There is an option to use interrupts using the data ready pins, but for now we will poll. +pub struct AdcManager { + pub spi: Spi, + pub adc1: Ads126x>>, + pub adc2: Ads126x>>, + pub adc1_cs: Pin<'C', 10, Output>, + pub adc2_cs: Pin<'D', 2, Output>, +} + +impl AdcManager { + pub fn new( + spi: Spi, + adc1_rst: Pin<'C', 11, Output>, + adc2_rst: Pin<'E', 0, Output>, + adc1_cs: Pin<'C', 10, Output>, + adc2_cs: Pin<'D', 2, Output>, + ) -> Self { + Self { + spi, + adc1: Ads126x::new(adc1_rst), + adc2: Ads126x::new(adc2_rst), + adc1_cs, + adc2_cs, + } + } + + pub fn init_adc1(&mut self) -> Result<(), ads126x::error::ADS126xError> { + self.adc2_cs.set_high(); + self.adc1_cs.set_low(); + self.adc1.reset()?; + + spawn!(delay, 1000); + + let mut mode1_cfg = Mode1Register::default(); + mode1_cfg.set_filter(ads126x::register::DigitalFilter::Sinc1); + self.adc1.set_mode1(&mode1_cfg, &mut self.spi)?; + + let mut mode2_cfg = Mode2Register::default(); + mode2_cfg.set_dr(DataRate::SPS1200); + self.adc1.set_mode2(&mode2_cfg, &mut self.spi)?; + + self.adc1.send_command(ADCCommand::START1, &mut self.spi)?; + self.adc1.send_command(ADCCommand::START2, &mut self.spi)?; + + Ok(()) + } + + pub fn init_adc2(&mut self) -> Result<(), ads126x::error::ADS126xError> { + self.adc1_cs.set_high(); + self.adc2_cs.set_low(); + self.adc2.reset()?; + + spawn!(delay, 1000); + + let mut mode1_cfg = Mode1Register::default(); + mode1_cfg.set_filter(ads126x::register::DigitalFilter::Sinc1); + self.adc1.set_mode1(&mode1_cfg, &mut self.spi)?; + + let mut mode2_cfg = Mode2Register::default(); + mode2_cfg.set_dr(DataRate::SPS1200); + self.adc1.set_mode2(&mode2_cfg, &mut self.spi)?; + + self.adc1.send_command(ADCCommand::START1, &mut self.spi)?; + self.adc1.send_command(ADCCommand::START2, &mut self.spi)?; + + Ok(()) + } +} diff --git a/boards/pressure/src/data_manager.rs b/boards/pressure/src/data_manager.rs index 2729860..67b5834 100644 --- a/boards/pressure/src/data_manager.rs +++ b/boards/pressure/src/data_manager.rs @@ -114,7 +114,7 @@ impl DataManager { pub fn handle_data(&mut self, data: Message) { match data.data { messages::Data::Sensor(ref sensor) => match sensor.data { - messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data{ + messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data { messages::sensor::SbgData::EkfNavAcc(_) => { self.ekf_nav_acc = Some(data); } @@ -154,7 +154,7 @@ impl DataManager { messages::sensor::SbgData::GpsPos2(_) => { self.gps_pos_2 = Some(data); } - } + }, messages::sensor::SensorData::RecoverySensing(_) => { self.recovery_sensing = Some(data); } diff --git a/boards/pressure/src/main.rs b/boards/pressure/src/main.rs index d703bc0..b320b16 100644 --- a/boards/pressure/src/main.rs +++ b/boards/pressure/src/main.rs @@ -3,6 +3,7 @@ mod communication; mod data_manager; +mod adc_manager; mod types; use chrono::NaiveDate; @@ -322,14 +323,19 @@ mod app { ) } + #[task(priority = 3)] + async fn delay(_cx: delay::Context, delay: u32) { + Mono::delay(delay.millis()).await; + } + #[task(priority = 3, shared = [&em, rtc])] async fn generate_random_messages(mut cx: generate_random_messages::Context) { loop { cx.shared.em.run(|| { let message = Message::new( - cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), COM_ID, messages::state::State::new(messages::state::StateData::Initializing), ); @@ -363,10 +369,13 @@ mod app { stm32h7xx_hal::rcc::ResetReason::Unknown { rcc_rsr } => sensor::ResetReason::Unknown { rcc_rsr }, stm32h7xx_hal::rcc::ResetReason::WindowWatchdogReset => sensor::ResetReason::WindowWatchdogReset, }; - let message = - messages::Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, sensor::Sensor::new(x)); + let message = messages::Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + sensor::Sensor::new(x), + ); cx.shared.em.run(|| { spawn!(send_gs, message)?; @@ -385,10 +394,13 @@ mod app { .lock(|data_manager| data_manager.state.clone()); cx.shared.em.run(|| { if let Some(x) = state_data { - let message = - Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, messages::state::State::new(x)); + let message = Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + messages::state::State::new(x), + ); spawn!(send_gs, message)?; } // if there is none we still return since we simply don't have data yet. Ok(()) @@ -442,8 +454,7 @@ mod app { } #[task(priority = 3, shared = [rtc, &em])] - async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) - { + async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) { cx.shared.em.run(|| { cx.shared.rtc.lock(|rtc| { let message = messages::Message::new( @@ -572,4 +583,4 @@ mod app { sbg.set_low(); }); } -} \ No newline at end of file +} diff --git a/boards/strain/Cargo.toml b/boards/strain/Cargo.toml index f0735f3..f12215c 100644 --- a/boards/strain/Cargo.toml +++ b/boards/strain/Cargo.toml @@ -22,6 +22,7 @@ defmt-rtt = { workspace = true } panic-probe = { workspace = true } chrono = { workspace = true } messages = {workspace = true} +ads126x = {path = "../../crates/ads126x"} [dev-dependencies] defmt-test = { workspace = true } diff --git a/boards/strain/src/adc_manager.rs b/boards/strain/src/adc_manager.rs new file mode 100644 index 0000000..73e8d1f --- /dev/null +++ b/boards/strain/src/adc_manager.rs @@ -0,0 +1,81 @@ +use ads126x::{ + register::{DataRate, Mode1Register, Mode2Register}, + ADCCommand, Ads126x, +}; + +use common_arm::spawn; +use stm32h7xx_hal::{ + gpio::{Output, Pin, PushPull}, + spi::Spi, +}; + +use crate::app::delay; + +// There is an option to use interrupts using the data ready pins, but for now we will poll. +pub struct AdcManager { + pub spi: Spi, + pub adc1: Ads126x>>, + pub adc2: Ads126x>>, + pub adc1_cs: Pin<'C', 10, Output>, + pub adc2_cs: Pin<'D', 2, Output>, +} + +impl AdcManager { + pub fn new( + spi: Spi, + adc1_rst: Pin<'C', 11, Output>, + adc2_rst: Pin<'E', 0, Output>, + adc1_cs: Pin<'C', 10, Output>, + adc2_cs: Pin<'D', 2, Output>, + ) -> Self { + Self { + spi, + adc1: Ads126x::new(adc1_rst), + adc2: Ads126x::new(adc2_rst), + adc1_cs, + adc2_cs, + } + } + + pub fn init_adc1(&mut self) -> Result<(), ads126x::error::ADS126xError> { + self.adc2_cs.set_high(); + self.adc1_cs.set_low(); + self.adc1.reset()?; + + spawn!(delay, 1000); + + let mut mode1_cfg = Mode1Register::default(); + mode1_cfg.set_filter(ads126x::register::DigitalFilter::Sinc1); + self.adc1.set_mode1(&mode1_cfg, &mut self.spi)?; + + let mut mode2_cfg = Mode2Register::default(); + mode2_cfg.set_dr(DataRate::SPS1200); + self.adc1.set_mode2(&mode2_cfg, &mut self.spi)?; + + self.adc1.send_command(ADCCommand::START1, &mut self.spi)?; + self.adc1.send_command(ADCCommand::START2, &mut self.spi)?; + + Ok(()) + } + + pub fn init_adc2(&mut self) -> Result<(), ads126x::error::ADS126xError> { + self.adc1_cs.set_high(); + self.adc2_cs.set_low(); + self.adc2.reset()?; + + spawn!(delay, 1000); + + let mut mode1_cfg = Mode1Register::default(); + mode1_cfg.set_filter(ads126x::register::DigitalFilter::Sinc1); + self.adc1.set_mode1(&mode1_cfg, &mut self.spi)?; + + let mut mode2_cfg = Mode2Register::default(); + mode2_cfg.set_dr(DataRate::SPS1200); + self.adc1.set_mode2(&mode2_cfg, &mut self.spi)?; + + self.adc1.send_command(ADCCommand::START1, &mut self.spi)?; + self.adc1.send_command(ADCCommand::START2, &mut self.spi)?; + + Ok(()) + } +} diff --git a/boards/strain/src/data_manager.rs b/boards/strain/src/data_manager.rs index 2729860..67b5834 100644 --- a/boards/strain/src/data_manager.rs +++ b/boards/strain/src/data_manager.rs @@ -114,7 +114,7 @@ impl DataManager { pub fn handle_data(&mut self, data: Message) { match data.data { messages::Data::Sensor(ref sensor) => match sensor.data { - messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data{ + messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data { messages::sensor::SbgData::EkfNavAcc(_) => { self.ekf_nav_acc = Some(data); } @@ -154,7 +154,7 @@ impl DataManager { messages::sensor::SbgData::GpsPos2(_) => { self.gps_pos_2 = Some(data); } - } + }, messages::sensor::SensorData::RecoverySensing(_) => { self.recovery_sensing = Some(data); } diff --git a/boards/strain/src/main.rs b/boards/strain/src/main.rs index d703bc0..b320b16 100644 --- a/boards/strain/src/main.rs +++ b/boards/strain/src/main.rs @@ -3,6 +3,7 @@ mod communication; mod data_manager; +mod adc_manager; mod types; use chrono::NaiveDate; @@ -322,14 +323,19 @@ mod app { ) } + #[task(priority = 3)] + async fn delay(_cx: delay::Context, delay: u32) { + Mono::delay(delay.millis()).await; + } + #[task(priority = 3, shared = [&em, rtc])] async fn generate_random_messages(mut cx: generate_random_messages::Context) { loop { cx.shared.em.run(|| { let message = Message::new( - cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), COM_ID, messages::state::State::new(messages::state::StateData::Initializing), ); @@ -363,10 +369,13 @@ mod app { stm32h7xx_hal::rcc::ResetReason::Unknown { rcc_rsr } => sensor::ResetReason::Unknown { rcc_rsr }, stm32h7xx_hal::rcc::ResetReason::WindowWatchdogReset => sensor::ResetReason::WindowWatchdogReset, }; - let message = - messages::Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, sensor::Sensor::new(x)); + let message = messages::Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + sensor::Sensor::new(x), + ); cx.shared.em.run(|| { spawn!(send_gs, message)?; @@ -385,10 +394,13 @@ mod app { .lock(|data_manager| data_manager.state.clone()); cx.shared.em.run(|| { if let Some(x) = state_data { - let message = - Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, messages::state::State::new(x)); + let message = Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + messages::state::State::new(x), + ); spawn!(send_gs, message)?; } // if there is none we still return since we simply don't have data yet. Ok(()) @@ -442,8 +454,7 @@ mod app { } #[task(priority = 3, shared = [rtc, &em])] - async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) - { + async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) { cx.shared.em.run(|| { cx.shared.rtc.lock(|rtc| { let message = messages::Message::new( @@ -572,4 +583,4 @@ mod app { sbg.set_low(); }); } -} \ No newline at end of file +} diff --git a/boards/temperature/Cargo.toml b/boards/temperature/Cargo.toml index 81939a3..494b916 100644 --- a/boards/temperature/Cargo.toml +++ b/boards/temperature/Cargo.toml @@ -22,6 +22,7 @@ defmt-rtt = { workspace = true } panic-probe = { workspace = true } chrono = { workspace = true } messages = {workspace = true} +ads126x = {path = "../../crates/ads126x"} [dev-dependencies] defmt-test = { workspace = true } diff --git a/boards/temperature/src/adc_manager.rs b/boards/temperature/src/adc_manager.rs new file mode 100644 index 0000000..73e8d1f --- /dev/null +++ b/boards/temperature/src/adc_manager.rs @@ -0,0 +1,81 @@ +use ads126x::{ + register::{DataRate, Mode1Register, Mode2Register}, + ADCCommand, Ads126x, +}; + +use common_arm::spawn; +use stm32h7xx_hal::{ + gpio::{Output, Pin, PushPull}, + spi::Spi, +}; + +use crate::app::delay; + +// There is an option to use interrupts using the data ready pins, but for now we will poll. +pub struct AdcManager { + pub spi: Spi, + pub adc1: Ads126x>>, + pub adc2: Ads126x>>, + pub adc1_cs: Pin<'C', 10, Output>, + pub adc2_cs: Pin<'D', 2, Output>, +} + +impl AdcManager { + pub fn new( + spi: Spi, + adc1_rst: Pin<'C', 11, Output>, + adc2_rst: Pin<'E', 0, Output>, + adc1_cs: Pin<'C', 10, Output>, + adc2_cs: Pin<'D', 2, Output>, + ) -> Self { + Self { + spi, + adc1: Ads126x::new(adc1_rst), + adc2: Ads126x::new(adc2_rst), + adc1_cs, + adc2_cs, + } + } + + pub fn init_adc1(&mut self) -> Result<(), ads126x::error::ADS126xError> { + self.adc2_cs.set_high(); + self.adc1_cs.set_low(); + self.adc1.reset()?; + + spawn!(delay, 1000); + + let mut mode1_cfg = Mode1Register::default(); + mode1_cfg.set_filter(ads126x::register::DigitalFilter::Sinc1); + self.adc1.set_mode1(&mode1_cfg, &mut self.spi)?; + + let mut mode2_cfg = Mode2Register::default(); + mode2_cfg.set_dr(DataRate::SPS1200); + self.adc1.set_mode2(&mode2_cfg, &mut self.spi)?; + + self.adc1.send_command(ADCCommand::START1, &mut self.spi)?; + self.adc1.send_command(ADCCommand::START2, &mut self.spi)?; + + Ok(()) + } + + pub fn init_adc2(&mut self) -> Result<(), ads126x::error::ADS126xError> { + self.adc1_cs.set_high(); + self.adc2_cs.set_low(); + self.adc2.reset()?; + + spawn!(delay, 1000); + + let mut mode1_cfg = Mode1Register::default(); + mode1_cfg.set_filter(ads126x::register::DigitalFilter::Sinc1); + self.adc1.set_mode1(&mode1_cfg, &mut self.spi)?; + + let mut mode2_cfg = Mode2Register::default(); + mode2_cfg.set_dr(DataRate::SPS1200); + self.adc1.set_mode2(&mode2_cfg, &mut self.spi)?; + + self.adc1.send_command(ADCCommand::START1, &mut self.spi)?; + self.adc1.send_command(ADCCommand::START2, &mut self.spi)?; + + Ok(()) + } +} diff --git a/boards/temperature/src/data_manager.rs b/boards/temperature/src/data_manager.rs index 2729860..67b5834 100644 --- a/boards/temperature/src/data_manager.rs +++ b/boards/temperature/src/data_manager.rs @@ -114,7 +114,7 @@ impl DataManager { pub fn handle_data(&mut self, data: Message) { match data.data { messages::Data::Sensor(ref sensor) => match sensor.data { - messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data{ + messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data { messages::sensor::SbgData::EkfNavAcc(_) => { self.ekf_nav_acc = Some(data); } @@ -154,7 +154,7 @@ impl DataManager { messages::sensor::SbgData::GpsPos2(_) => { self.gps_pos_2 = Some(data); } - } + }, messages::sensor::SensorData::RecoverySensing(_) => { self.recovery_sensing = Some(data); } diff --git a/boards/temperature/src/main.rs b/boards/temperature/src/main.rs index d703bc0..74e2136 100644 --- a/boards/temperature/src/main.rs +++ b/boards/temperature/src/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +mod adc_manager; mod communication; mod data_manager; mod types; @@ -17,6 +18,7 @@ use fdcan::{ filter::{StandardFilter, StandardFilterSlot}, }; use messages::command::RadioRate; +use messages::Message; use messages::{sensor, Data}; use panic_probe as _; use rtic_monotonics::systick::prelude::*; @@ -27,8 +29,13 @@ use stm32h7xx_hal::gpio::Speed; use stm32h7xx_hal::gpio::{Output, PushPull}; use stm32h7xx_hal::prelude::*; use stm32h7xx_hal::rtc; +use stm32h7xx_hal::{ + gpio::{Alternate, Pin}, + hal::spi, +}; use stm32h7xx_hal::{rcc, rcc::rec}; use types::COM_ID; // global logger +use adc_manager::AdcManager; const DATA_CHANNEL_CAPACITY: usize = 10; @@ -43,10 +50,6 @@ fn panic() -> ! { #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, dispatchers = [EXTI0, EXTI1, EXTI2, SPI3, SPI2])] mod app { - - use messages::Message; - use stm32h7xx_hal::gpio::{Alternate, Pin}; - use super::*; #[shared] @@ -62,6 +65,7 @@ mod app { can_data_manager: CanDataManager, sbg_power: PB4>, rtc: rtc::Rtc, + adc_manager: AdcManager, } #[local] struct LocalResources { @@ -130,7 +134,9 @@ mod app { // GPIO let gpioa = ctx.device.GPIOA.split(ccdr.peripheral.GPIOA); let gpiod = ctx.device.GPIOD.split(ccdr.peripheral.GPIOD); + let gpioc = ctx.device.GPIOC.split(ccdr.peripheral.GPIOC); let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB); + let gpioe = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE); let pins = gpiob.pb14.into_alternate(); let mut c0 = ctx @@ -250,6 +256,31 @@ mod app { // let sd_manager = SdManager::new(spi_sd, cs_sd); + // ADC setup + let adc_spi: stm32h7xx_hal::spi::Spi< + stm32h7xx_hal::stm32::SPI4, + stm32h7xx_hal::spi::Enabled, + u8, + > = ctx.device.SPI4.spi( + ( + gpioe.pe2.into_alternate(), + gpioe.pe5.into_alternate(), + gpioe.pe6.into_alternate(), + ), + stm32h7xx_hal::spi::Config::new(spi::MODE_0), + 1.MHz(), + ccdr.peripheral.SPI4, + &ccdr.clocks, + ); + + let adc1_cs = gpioc.pc10.into_push_pull_output(); + let adc2_cs = gpiod.pd2.into_push_pull_output(); + + let adc1_rst = gpioc.pc11.into_push_pull_output(); + let adc2_rst = gpioe.pe0.into_push_pull_output(); + + let adc_manager = AdcManager::new(adc_spi, adc1_rst, adc2_rst, adc1_cs, adc2_cs); + // leds let led_red = gpioa.pa2.into_push_pull_output(); let led_green = gpioa.pa3.into_push_pull_output(); @@ -313,6 +344,7 @@ mod app { can_data_manager, sbg_power, rtc, + adc_manager, }, LocalResources { led_red, @@ -327,9 +359,9 @@ mod app { loop { cx.shared.em.run(|| { let message = Message::new( - cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), COM_ID, messages::state::State::new(messages::state::StateData::Initializing), ); @@ -363,10 +395,13 @@ mod app { stm32h7xx_hal::rcc::ResetReason::Unknown { rcc_rsr } => sensor::ResetReason::Unknown { rcc_rsr }, stm32h7xx_hal::rcc::ResetReason::WindowWatchdogReset => sensor::ResetReason::WindowWatchdogReset, }; - let message = - messages::Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, sensor::Sensor::new(x)); + let message = messages::Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + sensor::Sensor::new(x), + ); cx.shared.em.run(|| { spawn!(send_gs, message)?; @@ -377,6 +412,11 @@ mod app { } } + #[task(priority = 3)] + async fn delay(_cx: delay::Context, delay: u32) { + Mono::delay(delay.millis()).await; + } + #[task(shared = [data_manager, &em, rtc])] async fn state_send(mut cx: state_send::Context) { let state_data = cx @@ -385,10 +425,13 @@ mod app { .lock(|data_manager| data_manager.state.clone()); cx.shared.em.run(|| { if let Some(x) = state_data { - let message = - Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, messages::state::State::new(x)); + let message = Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + messages::state::State::new(x), + ); spawn!(send_gs, message)?; } // if there is none we still return since we simply don't have data yet. Ok(()) @@ -442,8 +485,7 @@ mod app { } #[task(priority = 3, shared = [rtc, &em])] - async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) - { + async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) { cx.shared.em.run(|| { cx.shared.rtc.lock(|rtc| { let message = messages::Message::new( @@ -572,4 +614,4 @@ mod app { sbg.set_low(); }); } -} \ No newline at end of file +} diff --git a/crates/ads126x/Cargo.toml b/crates/ads126x/Cargo.toml new file mode 100644 index 0000000..498c859 --- /dev/null +++ b/crates/ads126x/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "ads126x" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = {workspace = true} +embedded-hal = {workspace = true} +bitflags = { version = "2.3.1", features = ["serde"] } +nb = "1.1.0" +heapless = {workspace = true} +cortex-m = { workspace = true } + +[dev-dependencies] +defmt-test = { workspace = true } +panic-probe = { workspace = true } +stm32h7xx-hal = { workspace = true } + +[[test]] +name = "example" +harness = false + +[lib] +name = "ads126x" +harness = false \ No newline at end of file diff --git a/crates/ads126x/src/error.rs b/crates/ads126x/src/error.rs new file mode 100644 index 0000000..33223bc --- /dev/null +++ b/crates/ads126x/src/error.rs @@ -0,0 +1,4 @@ +pub enum ADS126xError { + IO, + InvalidInputData, +} diff --git a/crates/ads126x/src/lib.rs b/crates/ads126x/src/lib.rs new file mode 100644 index 0000000..f918978 --- /dev/null +++ b/crates/ads126x/src/lib.rs @@ -0,0 +1,573 @@ +#![no_std] +#![no_main] + +pub mod error; +pub mod register; + +use error::ADS126xError; +use register::{ + Adc2CfgRegister, Adc2MuxRegister, GpioConRegister, GpioDatRegister, GpioDirRegister, + IdRegister, IdacMagRegister, IdacMuxRegister, InpMuxRegister, InterfaceRegister, Mode0Register, + Mode1Register, Mode2Register, PowerRegister, RefMuxRegister, Register, TdacnRegister, + TdacpRegister, +}; + +use embedded_hal::digital::v2::OutputPin; +use embedded_hal::spi::FullDuplex; +use heapless::Vec; + +/// The [`Result`] type for ADS126x operations. +pub type Result = core::result::Result; + +pub struct Ads126x +where + GpioPin: OutputPin, +{ + reset_pin: GpioPin, +} + +pub enum ADCCommand { + NOP, + RESET, + START1, + STOP1, + START2, + STOP2, + RDATA1, + RDATA2, + SYOCAL1, + SYGCAL1, + SFOCAL1, + SYOCAL2, + SYGCAL2, + SFOCAL2, + RREG(Register, u8), // (register address, number of registers) + WREG(Register, u8), // (register address, number of registers) +} + +fn delay(delay: u32) { + for _ in 0..delay { + cortex_m::asm::nop(); + } +} + +impl Ads126x +where + GpioPin: OutputPin, +{ + pub fn new(reset_pin: GpioPin) -> Self { + Self { reset_pin } + } + + pub fn reset(&mut self) -> Result<()> { + self.reset_pin.set_high().map_err(|_| ADS126xError::IO)?; + delay(1000); + self.reset_pin.set_low().map_err(|_| ADS126xError::IO)?; + delay(1000); + self.reset_pin.set_high().map_err(|_| ADS126xError::IO)?; + delay(1000); + Ok(()) + } + + pub fn send_command(&mut self, command: ADCCommand, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + let (opcode1, opcode2) = match command { + ADCCommand::NOP => (0x00, None), + ADCCommand::RESET => (0x06, None), + ADCCommand::START1 => (0x08, None), + ADCCommand::STOP1 => (0x0A, None), + ADCCommand::START2 => (0x0C, None), + ADCCommand::STOP2 => (0x0E, None), + ADCCommand::RDATA1 => (0x12, None), + ADCCommand::RDATA2 => (0x14, None), + ADCCommand::SYOCAL1 => (0x16, None), + ADCCommand::SYGCAL1 => (0x17, None), + ADCCommand::SFOCAL1 => (0x19, None), + ADCCommand::SYOCAL2 => (0x1B, None), + ADCCommand::SYGCAL2 => (0x1C, None), + ADCCommand::SFOCAL2 => (0x1E, None), + ADCCommand::RREG(addr, num) => (0x20 | addr as u8, Some(num)), + ADCCommand::WREG(addr, num) => (0x40 | addr as u8, Some(num)), + }; + + spi.send(opcode1).map_err(|_| ADS126xError::IO)?; + if let Some(op2) = opcode2 { + spi.send(op2).map_err(|_| ADS126xError::IO)?; + } + Ok(()) + } + + /// Reads data from multiple registers starting at the provided register. + /// To read a single register, see [`ADS126x::read_register`]. + /// + /// Vector returns byte for each register read in order registers were read (increasing address). + pub fn read_multiple_registers( + &mut self, + reg: Register, + num: u8, + spi: &mut SPI, + ) -> Result> + where + SPI: FullDuplex, + { + if num > 27 { + return Err(ADS126xError::InvalidInputData); + } + self.send_command(ADCCommand::RREG(reg, num - 1), spi)?; + let mut buffer: Vec = Vec::new(); + for _ in 0..num { + buffer + .push(spi.read().map_err(|_| ADS126xError::IO)?) + .map_err(|_| ADS126xError::InvalidInputData)?; + } + Ok(buffer) + } + /// Reads data from only the single provided register. + /// To read multiple registers, see [`ADS126x::read_multiple_registers`]. + pub fn read_register(&mut self, reg: Register, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + // zero since number of registers read - 1, so 1-1=0. + self.send_command(ADCCommand::RREG(reg, 0), spi)?; + let data = spi.read().map_err(|_| ADS126xError::IO)?; + Ok(data) + } + + /// Writes data to multiple registers starting at the provided register. + /// To write data to a single register, see [`ADS126x::write_register`]. + /// + /// Data has byte for each register in order registers are written to (increasing address). + pub fn write_multiple_registers( + &mut self, + reg: Register, + data: &[u8], + spi: &mut SPI, + ) -> Result<()> + where + SPI: FullDuplex, + { + if data.len() > 27 { + return Err(ADS126xError::InvalidInputData); + } + self.send_command(ADCCommand::WREG(reg, data.len() as u8 - 1), spi)?; + for &byte in data { + spi.send(byte).map_err(|_| ADS126xError::IO)?; + } + Ok(()) + } + + /// Writes data to only the single provided register. + /// To write data to multiple registers, see [`ADS126x::write_multiple_registers`]. + pub fn write_register(&mut self, reg: Register, data: u8, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.send_command(ADCCommand::WREG(reg, 0), spi)?; + spi.send(data).map_err(|_| ADS126xError::IO) + } + + pub fn get_id(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::ID, spi)?; + let data = IdRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn get_power(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::POWER, spi)?; + let data = PowerRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_power(&mut self, reg: &PowerRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::POWER, reg.bits(), spi) + } + + pub fn get_interface(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::INTERFACE, spi)?; + let data = InterfaceRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_interface(&mut self, reg: &InterfaceRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::INTERFACE, reg.bits(), spi) + } + + pub fn get_mode0(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::MODE0, spi)?; + let data = Mode0Register::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_mode0(&mut self, reg: &Mode0Register, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::MODE0, reg.bits(), spi) + } + + pub fn get_mode1(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::MODE1, spi)?; + let data = Mode1Register::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_mode1(&mut self, reg: &Mode1Register, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::MODE1, reg.bits(), spi) + } + + pub fn get_mode2(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::MODE2, spi)?; + let data = Mode2Register::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_mode2(&mut self, reg: &Mode2Register, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::MODE2, reg.bits(), spi) + } + + pub fn get_inpmux(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::INPMUX, spi)?; + let data = InpMuxRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_inpmux(&mut self, reg: &InpMuxRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::INPMUX, reg.bits(), spi) + } + + pub fn get_ofcal(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bytes = self.read_multiple_registers(Register::OFCAL0, 3, spi)?; // [OFCAL0, OFCAL1, OFCAL2] + let res = (bytes[2] as u32) << 16 | (bytes[1] as u32) << 8 | (bytes[0] as u32); + Ok(res) + } + + pub fn set_ofcal(&mut self, ofcal: u32, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + // Will not panic as & 0xFF ensures values are u8 + let res: [u8; 3] = [ + u8::try_from(ofcal & 0xFF).unwrap(), + u8::try_from((ofcal >> 8) & 0xFF).unwrap(), + u8::try_from((ofcal >> 16) & 0xFF).unwrap(), + ]; + self.write_multiple_registers(Register::OFCAL0, &res, spi) + } + + pub fn get_fscal(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bytes = self.read_multiple_registers(Register::FSCAL0, 3, spi)?; // [FSCAL0, FSCAL1, FSCAL2] + let res = (bytes[2] as u32) << 16 | (bytes[1] as u32) << 8 | (bytes[0] as u32); + Ok(res) + } + + pub fn set_fscal(&mut self, fscal: u32, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + // Will not panic as & 0xFF ensures values are u8 + let res: [u8; 3] = [ + u8::try_from(fscal & 0xFF).unwrap(), + u8::try_from((fscal >> 8) & 0xFF).unwrap(), + u8::try_from((fscal >> 16) & 0xFF).unwrap(), + ]; + self.write_multiple_registers(Register::FSCAL0, &res, spi) + } + + pub fn get_idacmux(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::IDACMUX, spi)?; + let data = IdacMuxRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_idacmux(&mut self, reg: &IdacMuxRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::IDACMUX, reg.bits(), spi) + } + + pub fn get_idacmag(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::IDACMAG, spi)?; + let data = IdacMagRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_idacmag(&mut self, reg: &IdacMagRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::IDACMAG, reg.bits(), spi) + } + + pub fn get_refmux(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::REFMUX, spi)?; + let data = RefMuxRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_refmux(&mut self, reg: &RefMuxRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::REFMUX, reg.bits(), spi) + } + + pub fn get_tdacp(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::TDACP, spi)?; + let data = TdacpRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_tdacp(&mut self, reg: &TdacpRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::TDACP, reg.bits(), spi) + } + + pub fn get_tdacn(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::TDACN, spi)?; + let data = TdacnRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_tdacn(&mut self, reg: &TdacnRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::TDACN, reg.bits(), spi) + } + + pub fn get_gpiocon(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::GPIOCON, spi)?; + let data = GpioConRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_gpiocon(&mut self, reg: &GpioConRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::GPIOCON, reg.bits(), spi) + } + + pub fn get_gpiodir(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::GPIODIR, spi)?; + let data = GpioDirRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_gpiodir(&mut self, reg: &GpioDirRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::GPIODIR, reg.bits(), spi) + } + + pub fn get_gpiodat(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::GPIODAT, spi)?; + let data = GpioDatRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_gpiodat(&mut self, reg: &GpioDatRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::GPIODAT, reg.bits(), spi) + } + + pub fn get_adc2cfg(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::ADC2CFG, spi)?; + let data = Adc2CfgRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_adc2cfg(&mut self, reg: &Adc2CfgRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::ADC2CFG, reg.bits(), spi) + } + + pub fn get_adc2mux(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bits = self.read_register(Register::ADC2MUX, spi)?; + let data = Adc2MuxRegister::from_bits(bits); + match data { + Some(reg) => Ok(reg), + None => Err(ADS126xError::InvalidInputData), + } + } + + pub fn set_adc2mux(&mut self, reg: &Adc2MuxRegister, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + self.write_register(Register::ADC2MUX, reg.bits(), spi) + } + + pub fn get_adc2ofc(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bytes = self.read_multiple_registers(Register::ADC2OFC0, 2, spi)?; // [ADC2OFC0, ADC2OFC1] + let res = (bytes[1] as u16) << 8 | (bytes[0] as u16); + Ok(res) + } + + pub fn set_adc2ofc(&mut self, ofc2: u16, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + // Will not panic as & 0xFF ensures values are u8 + let res: [u8; 2] = [ + u8::try_from(ofc2 & 0xFF).unwrap(), + u8::try_from((ofc2 >> 8) & 0xFF).unwrap(), + ]; + self.write_multiple_registers(Register::ADC2OFC0, &res, spi) + } + + pub fn get_adc2fsc(&mut self, spi: &mut SPI) -> Result + where + SPI: FullDuplex, + { + let bytes = self.read_multiple_registers(Register::ADC2FSC0, 2, spi)?; // [ADC2FSC0, ADC2FSC1] + let res = (bytes[1] as u16) << 8 | (bytes[0] as u16); + Ok(res) + } + + pub fn set_adc2fsc(&mut self, fsc2: u32, spi: &mut SPI) -> Result<()> + where + SPI: FullDuplex, + { + // Will not panic as & 0xFF ensures values are u8 + let res: [u8; 2] = [ + u8::try_from(fsc2 & 0xFF).unwrap(), + u8::try_from((fsc2 >> 8) & 0xFF).unwrap(), + ]; + self.write_multiple_registers(Register::ADC2FSC0, &res, spi) + } +} diff --git a/crates/ads126x/src/register.rs b/crates/ads126x/src/register.rs new file mode 100644 index 0000000..56d5f35 --- /dev/null +++ b/crates/ads126x/src/register.rs @@ -0,0 +1,859 @@ +mod enums; + +use bitflags::bitflags; +pub use enums::*; + +#[repr(u8)] +pub enum Register { + ID = 0x00, + POWER = 0x01, + INTERFACE = 0x02, + MODE0 = 0x03, + MODE1 = 0x04, + MODE2 = 0x05, + INPMUX = 0x06, + OFCAL0 = 0x07, + OFCAL1 = 0x08, + OFCAL2 = 0x09, + FSCAL0 = 0x0A, + FSCAL1 = 0x0B, + FSCAL2 = 0x0C, + IDACMUX = 0x0D, + IDACMAG = 0x0E, + REFMUX = 0x0F, + TDACP = 0x10, + TDACN = 0x11, + GPIOCON = 0x12, + GPIODIR = 0x13, + GPIODAT = 0x14, + ADC2CFG = 0x15, + ADC2MUX = 0x16, + ADC2OFC0 = 0x17, + ADC2OFC1 = 0x18, + ADC2FSC0 = 0x19, + ADC2FSC1 = 0x1A, +} + +bitflags! { + pub struct StatusRegister: u8 { + const ADC2 = 0b1000_0000; + const ADC1 = 0b0100_0000; + const EXTCLK = 0b0010_0000; + const REF_ALM = 0b0001_0000; + const PGAL_ALM = 0b0000_1000; + const PGAH_ALM = 0b0000_0100; + const PGAD_ALM = 0b0000_0010; + const RESET = 0b0000_0001; + } +} + +bitflags! { + pub struct IdRegister: u8 { + const _ = !0; // Source may set any bits + } +} + +impl IdRegister { + pub fn get_rev_id(&self) -> u8 { + self.bits() & 0b0001_1111 + } + + pub fn get_dev_id(&self) -> DevId { + match (self.bits() & 0b1110_0000) >> 5 { + 0b000 => DevId::ADS1262, + 0b001 => DevId::ADS1263, + + _ => panic!("Device ID must be 0b000 or 0b001."), + } + } +} + +bitflags! { + pub struct PowerRegister: u8 { + const INTREF = 0b0000_0001; + const VBIAS = 0b0000_0010; + const RESET = 0b0001_0000; + } +} + +impl PowerRegister { + pub fn default() -> Self { + PowerRegister::from_bits_truncate(0b0001_0001) + } + + pub fn set_vbias(&mut self, vbias: bool) { + if vbias { + self.insert(PowerRegister::VBIAS); + } else { + self.remove(PowerRegister::VBIAS); + } + } + + pub fn set_intref(&mut self, intref: bool) { + if intref { + self.insert(PowerRegister::INTREF); + } else { + self.remove(PowerRegister::INTREF); + } + } + + pub fn get_reset(&self) -> bool { + self.contains(PowerRegister::RESET) + } + + // must be cleared if set to detect further resets. + pub fn clear_reset(&mut self) { + self.remove(PowerRegister::RESET); + } +} + +bitflags! { + /// WARNING: If CRC is 0b11 set by ADC, it will reflect as CRC enabled not reserved. + /// CRC only accounts for 0b00 disabled and 0b01 enabled. + pub struct InterfaceRegister: u8 { + const CRC = 0b0000_0001; + const STATUS = 0b0000_0100; + const TIMEOUT = 0b0000_1000; + } +} + +impl InterfaceRegister { + pub fn default() -> Self { + InterfaceRegister::from_bits_truncate(0b0000_0101) + } + + pub fn get_crc(&self) -> CrcMode { + match self.bits() & 0b0000_0001 { + 0b00 => CrcMode::Disabled, + 0b01 => CrcMode::Checksum, + 0b10 => CrcMode::CRC, + _ => unreachable!(), + } + } + + pub fn set_crc(&mut self, crc: CrcMode) { + let bits = crc as u8; + self.insert(InterfaceRegister::from_bits_retain(bits)); + } + + pub fn get_status(&self) -> bool { + self.contains(InterfaceRegister::STATUS) + } + + pub fn set_status(&mut self, status: bool) { + if status { + self.insert(InterfaceRegister::STATUS); + } else { + self.remove(InterfaceRegister::STATUS); + } + } + + pub fn get_timeout(&self) -> bool { + self.contains(InterfaceRegister::TIMEOUT) + } + + pub fn set_timeout(&mut self, timeout: bool) { + if timeout { + self.insert(InterfaceRegister::TIMEOUT); + } else { + self.remove(InterfaceRegister::TIMEOUT); + } + } +} + +bitflags! { + pub struct Mode0Register: u8 { + const RUNMODE = 0b0100_0000; + const REFREV = 0b1000_0000; + + const _ = !0; // Source may set any bits + } +} + +impl Mode0Register { + pub fn default() -> Self { + Mode0Register::from_bits_truncate(0b0000_0000) + } + + pub fn get_delay(&self) -> ConversionDelay { + match self.bits() & 0b0000_1111 { + 0b0000 => ConversionDelay::DNone, + 0b0001 => ConversionDelay::D8_7us, + 0b0010 => ConversionDelay::D17us, + 0b0011 => ConversionDelay::D35us, + 0b0100 => ConversionDelay::D69us, + 0b0101 => ConversionDelay::D139us, + 0b0110 => ConversionDelay::D278us, + 0b0111 => ConversionDelay::D555us, + 0b1000 => ConversionDelay::D1_1ms, + 0b1001 => ConversionDelay::D2_2ms, + 0b1010 => ConversionDelay::D4_4ms, + 0b1011 => ConversionDelay::D8_8ms, + + 0b1100..=0b1111 => panic!("Unknown conversion delay"), + _ => unreachable!(), + } + } + + pub fn set_delay(&mut self, delay: ConversionDelay) { + let bits = delay as u8; + self.insert(Mode0Register::from_bits_retain(bits)); + } + + pub fn get_chop(&self) -> ChopMode { + match (self.bits() & 0b0011_0000) >> 4 { + 0b00 => ChopMode::Disabled, + 0b01 => ChopMode::InChopEnabled, + 0b10 => ChopMode::IdacEnabled, + 0b11 => ChopMode::InChopAndIdacEnabled, + + _ => unreachable!(), + } + } + + pub fn set_chop(&mut self, chop: ChopMode) { + let bits = chop as u8; + self.insert(Mode0Register::from_bits_retain(bits << 4)); + } +} + +bitflags! { + pub struct Mode1Register: u8 { + const SBPOL = 0b0000_1000; + const SBADC = 0b0001_0000; + + const _ = !0; // Source may set any bits + } +} + +impl Mode1Register { + pub fn default() -> Self { + Mode1Register::from_bits_truncate(0b1000_0000) + } + + pub fn get_sbmag(&self) -> SensorBiasMagnitude { + match self.bits() & 0b0000_0111 { + 0b000 => SensorBiasMagnitude::BNone, + 0b001 => SensorBiasMagnitude::B0_5uA, + 0b010 => SensorBiasMagnitude::B2uA, + 0b011 => SensorBiasMagnitude::B10uA, + 0b100 => SensorBiasMagnitude::B50uA, + 0b101 => SensorBiasMagnitude::B200uA, + 0b110 => SensorBiasMagnitude::R10MOhm, + + 0b111 => panic!("Reserved SBMAG"), + _ => unreachable!(), + } + } + + pub fn set_sbmag(&mut self, sbmag: SensorBiasMagnitude) { + let bits = sbmag as u8; + self.insert(Mode1Register::from_bits_retain(bits)); + } + + pub fn get_filter(&self) -> DigitalFilter { + match (self.bits() & 0b1110_0000) >> 5 { + 0b000 => DigitalFilter::Sinc1, + 0b001 => DigitalFilter::Sinc2, + 0b010 => DigitalFilter::Sinc3, + 0b011 => DigitalFilter::Sinc4, + 0b100 => DigitalFilter::FIR, + + 0b101..=0b111 => panic!("Reserved filter"), + _ => unreachable!(), + } + } + + pub fn set_filter(&mut self, filter: DigitalFilter) { + let bits = filter as u8; + self.insert(Mode1Register::from_bits_retain(bits << 5)); + } +} + +bitflags! { + pub struct Mode2Register: u8 { + const BYPASS = 0b1000_0000; + + const _ = !0; // Source may set any bits + } +} + +impl Mode2Register { + pub fn default() -> Self { + Mode2Register::from_bits_truncate(0b0000_0100) + } + + pub fn get_dr(&self) -> DataRate { + match self.bits() & 0b0000_1111 { + 0b0000 => DataRate::SPS2_5, + 0b0001 => DataRate::SPS5, + 0b0010 => DataRate::SPS10, + 0b0011 => DataRate::SPS16_6, + 0b0100 => DataRate::SPS20, + 0b0101 => DataRate::SPS50, + 0b0110 => DataRate::SPS60, + 0b0111 => DataRate::SPS100, + 0b1000 => DataRate::SPS400, + 0b1001 => DataRate::SPS1200, + 0b1010 => DataRate::SPS2400, + 0b1011 => DataRate::SPS4800, + 0b1100 => DataRate::SPS7200, + 0b1101 => DataRate::SPS14400, + 0b1110 => DataRate::SPS19200, + 0b1111 => DataRate::SPS38400, + + _ => unreachable!(), + } + } + + pub fn set_dr(&mut self, rate: DataRate) { + let bits = rate as u8; + self.insert(Mode2Register::from_bits_retain(bits)); + } + + pub fn get_gain(&self) -> PGAGain { + match (self.bits() & 0b0111_0000) >> 4 { + 0b000 => PGAGain::VV1, + 0b001 => PGAGain::VV2, + 0b010 => PGAGain::VV4, + 0b011 => PGAGain::VV8, + 0b100 => PGAGain::VV16, + 0b101 => PGAGain::VV32, + + 0b110 | 0b111 => panic!("Reserved gain"), + _ => unreachable!(), + } + } + + pub fn set_gain(&mut self, gain: PGAGain) { + let bits = gain as u8; + self.insert(Mode2Register::from_bits_retain(bits << 4)); + } +} + +bitflags! { + pub struct InpMuxRegister: u8 { + const _ = !0; // Source may set any bits + } +} + +impl InpMuxRegister { + pub fn default() -> Self { + InpMuxRegister::from_bits_truncate(0b0000_0001) + } + + pub fn get_muxn(&self) -> NegativeInpMux { + match self.bits() & 0b0000_1111 { + 0b0000 => NegativeInpMux::AIN0, + 0b0001 => NegativeInpMux::AIN1, + 0b0010 => NegativeInpMux::AIN2, + 0b0011 => NegativeInpMux::AIN3, + 0b0100 => NegativeInpMux::AIN4, + 0b0101 => NegativeInpMux::AIN5, + 0b0110 => NegativeInpMux::AIN6, + 0b0111 => NegativeInpMux::AIN7, + 0b1000 => NegativeInpMux::AIN8, + 0b1001 => NegativeInpMux::AIN9, + 0b1010 => NegativeInpMux::AINCOM, + 0b1011 => NegativeInpMux::TempSensMonNeg, + 0b1100 => NegativeInpMux::AnlgPwrSupMonNeg, + 0b1101 => NegativeInpMux::DgtlPwrSubMonNeg, + 0b1110 => NegativeInpMux::TDACTestSignalNeg, + 0b1111 => NegativeInpMux::Float, + + _ => unreachable!(), + } + } + + pub fn set_muxn(&mut self, muxn: NegativeInpMux) { + let bits = muxn as u8; + self.insert(InpMuxRegister::from_bits_retain(bits)); + } + + pub fn get_muxp(&self) -> PositiveInpMux { + match (self.bits() & 0b1111_0000) >> 4 { + 0b0000 => PositiveInpMux::AIN0, + 0b0001 => PositiveInpMux::AIN1, + 0b0010 => PositiveInpMux::AIN2, + 0b0011 => PositiveInpMux::AIN3, + 0b0100 => PositiveInpMux::AIN4, + 0b0101 => PositiveInpMux::AIN5, + 0b0110 => PositiveInpMux::AIN6, + 0b0111 => PositiveInpMux::AIN7, + 0b1000 => PositiveInpMux::AIN8, + 0b1001 => PositiveInpMux::AIN9, + 0b1010 => PositiveInpMux::AINCOM, + 0b1011 => PositiveInpMux::TempSensMonPos, + 0b1100 => PositiveInpMux::AnlgPwrSupMonPos, + 0b1101 => PositiveInpMux::DgtlPwrSubMonPos, + 0b1110 => PositiveInpMux::TDACTestSignalPos, + 0b1111 => PositiveInpMux::Float, + + _ => unreachable!(), + } + } + + pub fn set_muxp(&mut self, muxp: PositiveInpMux) { + let bits = muxp as u8; + self.insert(InpMuxRegister::from_bits_retain(bits << 4)); + } +} + +bitflags! { + pub struct IdacMuxRegister: u8 { + const _ = !0; // Source may set any bits + } +} + +impl IdacMuxRegister { + pub fn default() -> Self { + IdacMuxRegister::from_bits_truncate(0b1011_1011) + } + + pub fn get_mux1(&self) -> IdacOutMux { + match self.bits() & 0b0000_1111 { + 0b0000 => IdacOutMux::AIN0, + 0b0001 => IdacOutMux::AIN1, + 0b0010 => IdacOutMux::AIN2, + 0b0011 => IdacOutMux::AIN3, + 0b0100 => IdacOutMux::AIN4, + 0b0101 => IdacOutMux::AIN5, + 0b0110 => IdacOutMux::AIN6, + 0b0111 => IdacOutMux::AIN7, + 0b1000 => IdacOutMux::AIN8, + 0b1001 => IdacOutMux::AIN9, + 0b1010 => IdacOutMux::AINCOM, + 0b1011 => IdacOutMux::NoConnection, + + 0b1100..=0b1111 => panic!("Reserved IDAC Output Multiplexer"), + _ => unreachable!(), + } + } + + pub fn set_mux1(&mut self, mux1: IdacOutMux) { + let bits = mux1 as u8; + self.insert(IdacMuxRegister::from_bits_retain(bits)); + } + + pub fn get_mux2(&self) -> IdacOutMux { + match (self.bits() & 0b1111_0000) >> 4 { + 0b0000 => IdacOutMux::AIN0, + 0b0001 => IdacOutMux::AIN1, + 0b0010 => IdacOutMux::AIN2, + 0b0011 => IdacOutMux::AIN3, + 0b0100 => IdacOutMux::AIN4, + 0b0101 => IdacOutMux::AIN5, + 0b0110 => IdacOutMux::AIN6, + 0b0111 => IdacOutMux::AIN7, + 0b1000 => IdacOutMux::AIN8, + 0b1001 => IdacOutMux::AIN9, + 0b1010 => IdacOutMux::AINCOM, + 0b1011 => IdacOutMux::NoConnection, + + 0b1100..=0b1111 => panic!("Reserved IDAC Output Multiplexer"), + _ => unreachable!(), + } + } + + pub fn set_mux2(&mut self, mux2: IdacOutMux) { + let bits = mux2 as u8; + self.insert(IdacMuxRegister::from_bits_retain(bits << 4)); + } +} + +bitflags! { + pub struct IdacMagRegister: u8 { + const _ = !0; // Source may set any bits + } +} + +impl IdacMagRegister { + pub fn default() -> Self { + IdacMagRegister::from_bits_truncate(0b0000_0000) + } + + pub fn get_mag1(&self) -> IdacCurMag { + match self.bits() & 0b0000_1111 { + 0b0000 => IdacCurMag::I50uA, + 0b0001 => IdacCurMag::I100uA, + 0b0010 => IdacCurMag::I250uA, + 0b0011 => IdacCurMag::I500uA, + 0b0100 => IdacCurMag::I750uA, + 0b0101 => IdacCurMag::I1000uA, + 0b0110 => IdacCurMag::I1500uA, + 0b0111 => IdacCurMag::I2000uA, + 0b1000 => IdacCurMag::I2500uA, + 0b1001 => IdacCurMag::I3000uA, + + 0b1010..=0b1111 => panic!("Reserved IDAC Magnitude Multiplexer"), + _ => unreachable!(), + } + } + + pub fn set_mag1(&mut self, mag1: IdacCurMag) { + let bits = mag1 as u8; + self.insert(IdacMagRegister::from_bits_retain(bits)); + } + + pub fn get_mag2(&self) -> IdacCurMag { + match (self.bits() & 0b1111_0000) >> 4 { + 0b0000 => IdacCurMag::I50uA, + 0b0001 => IdacCurMag::I100uA, + 0b0010 => IdacCurMag::I250uA, + 0b0011 => IdacCurMag::I500uA, + 0b0100 => IdacCurMag::I750uA, + 0b0101 => IdacCurMag::I1000uA, + 0b0110 => IdacCurMag::I1500uA, + 0b0111 => IdacCurMag::I2000uA, + 0b1000 => IdacCurMag::I2500uA, + 0b1001 => IdacCurMag::I3000uA, + + 0b1010..=0b1111 => panic!("Reserved IDAC Output Multiplexer"), + _ => unreachable!(), + } + } + + pub fn set_mag2(&mut self, mag2: IdacCurMag) { + let bits = mag2 as u8; + self.insert(IdacMagRegister::from_bits_retain(bits << 4)); + } +} + +bitflags! { + pub struct RefMuxRegister: u8 { + const _ = 0b0011_1111; + } +} + +impl RefMuxRegister { + pub fn default() -> Self { + RefMuxRegister::from_bits_truncate(0b0000_0000) + } + + pub fn get_rmuxn(&self) -> RefNegativeInp { + match self.bits() & 0b0000_0111 { + 0b000 => RefNegativeInp::Int2_5VRef, + 0b001 => RefNegativeInp::ExtAIN1, + 0b010 => RefNegativeInp::ExtAIN3, + 0b011 => RefNegativeInp::ExtAIN5, + 0b100 => RefNegativeInp::IntAnlgSup, + + 0b101..=0b111 => panic!("Reserved Reference Negative Input"), + _ => unreachable!(), + } + } + + pub fn set_rmuxn(&mut self, rmuxn: RefNegativeInp) { + let bits = rmuxn as u8; + self.insert(RefMuxRegister::from_bits_retain(bits)); + } + + pub fn get_rmuxp(&self) -> RefPositiveInp { + match (self.bits() & 0b0011_1000) >> 3 { + 0b000 => RefPositiveInp::Int2_5VRef, + 0b001 => RefPositiveInp::ExtAIN0, + 0b010 => RefPositiveInp::ExtAIN2, + 0b011 => RefPositiveInp::ExtAIN4, + 0b100 => RefPositiveInp::IntAnlgSup, + + 0b101..=0b111 => panic!("Reserved Reference Positive Input"), + _ => unreachable!(), + } + } + + pub fn set_rmuxp(&mut self, rmuxp: RefPositiveInp) { + let bits = rmuxp as u8; + self.insert(RefMuxRegister::from_bits_retain(bits << 3)); + } +} + +bitflags! { + pub struct TdacpRegister: u8 { + const OUTP = 0b1000_0000; + + const _ = 0b1001_1111; + } +} + +impl TdacpRegister { + // there is no defined default values in the datasheet so just zeros. + pub fn default() -> Self { + TdacpRegister::from_bits_truncate(0b0000_0000) + } + + pub fn get_magp(&self) -> TdacOutMag { + match self.bits() & 0b0001_1111 { + 0b01001 => TdacOutMag::V4_5, + 0b01000 => TdacOutMag::V3_5, + 0b00111 => TdacOutMag::V3, + 0b00110 => TdacOutMag::V2_75, + 0b00101 => TdacOutMag::V2_625, + 0b00100 => TdacOutMag::V2_5625, + 0b00011 => TdacOutMag::V2_53125, + 0b00010 => TdacOutMag::V2_515625, + 0b00001 => TdacOutMag::V2_5078125, + 0b00000 => TdacOutMag::V2_5, + 0b10001 => TdacOutMag::V2_4921875, + 0b10010 => TdacOutMag::V2_484375, + 0b10011 => TdacOutMag::V2_46875, + 0b10100 => TdacOutMag::V2_4375, + 0b10101 => TdacOutMag::V2_375, + 0b10110 => TdacOutMag::V2_25, + 0b10111 => TdacOutMag::V2, + 0b11000 => TdacOutMag::V1_5, + 0b11001 => TdacOutMag::V0_5, + + 0b01010..=0b10000 | 0b11010..=0b11111 => panic!("Reserved MAGP"), + _ => unreachable!(), + } + } + + pub fn set_magp(&mut self, magp: TdacOutMag) { + let bits = magp as u8; + self.insert(TdacpRegister::from_bits_retain(bits)); + } +} + +bitflags! { + pub struct TdacnRegister: u8 { + const OUTN = 0b1000_0000; + + const _ = 0b1001_1111; + } +} + +impl TdacnRegister { + // there is no defined default values in the datasheet so just zeros. + pub fn default() -> Self { + TdacnRegister::from_bits_truncate(0b0000_0000) + } + + pub fn get_magn(&self) -> TdacOutMag { + match self.bits() & 0b0001_1111 { + 0b01001 => TdacOutMag::V4_5, + 0b01000 => TdacOutMag::V3_5, + 0b00111 => TdacOutMag::V3, + 0b00110 => TdacOutMag::V2_75, + 0b00101 => TdacOutMag::V2_625, + 0b00100 => TdacOutMag::V2_5625, + 0b00011 => TdacOutMag::V2_53125, + 0b00010 => TdacOutMag::V2_515625, + 0b00001 => TdacOutMag::V2_5078125, + 0b00000 => TdacOutMag::V2_5, + 0b10001 => TdacOutMag::V2_4921875, + 0b10010 => TdacOutMag::V2_484375, + 0b10011 => TdacOutMag::V2_46875, + 0b10100 => TdacOutMag::V2_4375, + 0b10101 => TdacOutMag::V2_375, + 0b10110 => TdacOutMag::V2_25, + 0b10111 => TdacOutMag::V2, + 0b11000 => TdacOutMag::V1_5, + 0b11001 => TdacOutMag::V0_5, + + 0b01010..=0b10000 | 0b11010..=0b11111 => panic!("Reserved MAGN"), + _ => unreachable!(), + } + } + + pub fn set_magn(&mut self, magn: TdacOutMag) { + let bits = magn as u8; + self.insert(TdacnRegister::from_bits_retain(bits)); + } +} + +bitflags! { + pub struct GpioConRegister: u8 { + const CON0 = 0b0000_0001; // GPIO[0] -> AIN3 + const CON1 = 0b0000_0010; // GPIO[1] -> AIN4 + const CON2 = 0b0000_0100; // GPIO[2] -> AIN5 + const CON3 = 0b0000_1000; // GPIO[3] -> AIN6 + const CON4 = 0b0001_0000; // GPIO[4] -> AIN7 + const CON5 = 0b0010_0000; // GPIO[5] -> AIN8 + const CON6 = 0b0100_0000; // GPIO[6] -> AIN9 + const CON7 = 0b1000_0000; // GPIO[7] -> AINCOM + } +} + +impl GpioConRegister { + pub fn default() -> Self { + GpioConRegister::from_bits_truncate(0b0000_0000) + } +} + +bitflags! { + /// Setting `DIR` to: + /// - 0 = `GPIO` is output + /// - 1 = `GPIO` is input + pub struct GpioDirRegister: u8 { + const DIR0 = 0b0000_0001; + const DIR1 = 0b0000_0010; + const DIR2 = 0b0000_0100; + const DIR3 = 0b0000_1000; + const DIR4 = 0b0001_0000; + const DIR5 = 0b0010_0000; + const DIR6 = 0b0100_0000; + const DIR7 = 0b1000_0000; + } +} + +impl GpioDirRegister { + pub fn default() -> Self { + GpioDirRegister::from_bits_truncate(0b0000_0000) + } +} + +bitflags! { + /// If `GPIO` is output, read returns 0b. + /// If `GPIO` is input, write sets `GPIO` to high (if 1) or low (if 0). + pub struct GpioDatRegister: u8 { + const DAT0 = 0b0000_0001; + const DAT1 = 0b0000_0010; + const DAT2 = 0b0000_0100; + const DAT3 = 0b0000_1000; + const DAT4 = 0b0001_0000; + const DAT5 = 0b0010_0000; + const DAT6 = 0b0100_0000; + const DAT7 = 0b1000_0000; + } +} + +impl GpioDatRegister { + // there is no defined default values in the datasheet so just zeros. + pub fn default() -> Self { + GpioDatRegister::from_bits_truncate(0b0000_0000) + } +} + +bitflags! { + pub struct Adc2CfgRegister: u8 { + const _ = !0; // Source may set any bits + } +} + +impl Adc2CfgRegister { + pub fn default() -> Self { + Adc2CfgRegister::from_bits_truncate(0b0000_0000) + } + + pub fn get_gain2(&self) -> Adc2Gain { + match self.bits() & 0b0000_0111 { + 0b000 => Adc2Gain::VV1, + 0b001 => Adc2Gain::VV2, + 0b010 => Adc2Gain::VV4, + 0b011 => Adc2Gain::VV8, + 0b100 => Adc2Gain::VV16, + 0b101 => Adc2Gain::VV32, + 0b110 => Adc2Gain::VV64, + 0b111 => Adc2Gain::VV128, + + _ => unreachable!(), + } + } + + pub fn set_gain2(&mut self, gain2: Adc2Gain) { + let bits = gain2 as u8; + self.insert(Adc2CfgRegister::from_bits_retain(bits)); + } + + pub fn get_ref2(&self) -> Adc2RefInp { + match (self.bits() & 0b0011_1000) >> 3 { + 0b000 => Adc2RefInp::Int2_5VRef, + 0b001 => Adc2RefInp::ExtAIN0_1, + 0b010 => Adc2RefInp::ExtAIN2_3, + 0b011 => Adc2RefInp::ExtAIN4_5, + 0b100 => Adc2RefInp::IntAnlgSup, + + 0b101..=0b111 => panic!("Reserved ADC2 reference input"), + _ => unreachable!(), + } + } + + pub fn set_ref2(&mut self, ref2: Adc2RefInp) { + let bits = ref2 as u8; + self.insert(Adc2CfgRegister::from_bits_retain(bits << 3)); + } + + pub fn get_dr2(&self) -> Adc2DataRate { + match (self.bits() & 0b1100_0000) >> 6 { + 0b00 => Adc2DataRate::SPS10, + 0b01 => Adc2DataRate::SPS100, + 0b10 => Adc2DataRate::SPS400, + 0b11 => Adc2DataRate::SPS800, + + _ => unreachable!(), + } + } + + pub fn set_dr2(&mut self, dr2: Adc2DataRate) { + let bits = dr2 as u8; + self.insert(Adc2CfgRegister::from_bits_retain(bits << 6)); + } +} + +bitflags! { + pub struct Adc2MuxRegister: u8 { + const _ = !0; // Source may set any bits + } +} + +impl Adc2MuxRegister { + pub fn default() -> Self { + Adc2MuxRegister::from_bits_truncate(0b0000_0001) + } + + pub fn get_muxn2(&self) -> NegativeInpMux { + match self.bits() & 0b0000_1111 { + 0b0000 => NegativeInpMux::AIN0, + 0b0001 => NegativeInpMux::AIN1, + 0b0010 => NegativeInpMux::AIN2, + 0b0011 => NegativeInpMux::AIN3, + 0b0100 => NegativeInpMux::AIN4, + 0b0101 => NegativeInpMux::AIN5, + 0b0110 => NegativeInpMux::AIN6, + 0b0111 => NegativeInpMux::AIN7, + 0b1000 => NegativeInpMux::AIN8, + 0b1001 => NegativeInpMux::AIN9, + 0b1010 => NegativeInpMux::AINCOM, + 0b1011 => NegativeInpMux::TempSensMonNeg, + 0b1100 => NegativeInpMux::AnlgPwrSupMonNeg, + 0b1101 => NegativeInpMux::DgtlPwrSubMonNeg, + 0b1110 => NegativeInpMux::TDACTestSignalNeg, + 0b1111 => NegativeInpMux::Float, + + _ => unreachable!(), + } + } + + pub fn set_muxn2(&mut self, muxn2: NegativeInpMux) { + let bits = muxn2 as u8; + self.insert(Adc2MuxRegister::from_bits_retain(bits)); + } + + pub fn get_muxp2(&self) -> PositiveInpMux { + match (self.bits() & 0b1111_0000) >> 4 { + 0b0000 => PositiveInpMux::AIN0, + 0b0001 => PositiveInpMux::AIN1, + 0b0010 => PositiveInpMux::AIN2, + 0b0011 => PositiveInpMux::AIN3, + 0b0100 => PositiveInpMux::AIN4, + 0b0101 => PositiveInpMux::AIN5, + 0b0110 => PositiveInpMux::AIN6, + 0b0111 => PositiveInpMux::AIN7, + 0b1000 => PositiveInpMux::AIN8, + 0b1001 => PositiveInpMux::AIN9, + 0b1010 => PositiveInpMux::AINCOM, + 0b1011 => PositiveInpMux::TempSensMonPos, + 0b1100 => PositiveInpMux::AnlgPwrSupMonPos, + 0b1101 => PositiveInpMux::DgtlPwrSubMonPos, + 0b1110 => PositiveInpMux::TDACTestSignalPos, + 0b1111 => PositiveInpMux::Float, + + _ => unreachable!(), + } + } + + pub fn set_muxp2(&mut self, muxp2: PositiveInpMux) { + let bits = muxp2 as u8; + self.insert(Adc2MuxRegister::from_bits_retain(bits << 4)); + } +} diff --git a/crates/ads126x/src/register/enums.rs b/crates/ads126x/src/register/enums.rs new file mode 100644 index 0000000..38ff242 --- /dev/null +++ b/crates/ads126x/src/register/enums.rs @@ -0,0 +1,269 @@ +pub enum DevId { + ADS1262 = 0b000, + ADS1263 = 0b001, +} + +/// Conversion delays follow the pattern `D`. +/// - `len` is the length of time where _ is a substitute for a decimal point. +/// - `units` are the units of time where us is microseconds and ms is milliseconds. +/// +/// D8_7us = delay of 8.7 microseconds. D8_8ms = delay of 8.8 milliseconds. +#[repr(u8)] +pub enum ConversionDelay { + DNone = 0b0000, + D8_7us = 0b0001, + D17us = 0b0010, + D35us = 0b0011, + D69us = 0b0100, + D139us = 0b0101, + D278us = 0b0110, + D555us = 0b0111, + D1_1ms = 0b1000, + D2_2ms = 0b1001, + D4_4ms = 0b1010, + D8_8ms = 0b1011, +} + +impl ConversionDelay { + /// Returns the delay in nanoseconds. + pub fn delay(&self) -> u32 { + match self { + Self::DNone => 0, + Self::D8_7us => 8_700, + Self::D17us => 17_000, + Self::D35us => 35_000, + Self::D69us => 69_000, + Self::D139us => 139_000, + Self::D278us => 278_000, + Self::D555us => 555_000, + Self::D1_1ms => 1_100_000, + Self::D2_2ms => 2_200_000, + Self::D4_4ms => 4_400_000, + Self::D8_8ms => 8_800_000, + } + } +} + +#[repr(u8)] +pub enum ChopMode { + Disabled = 0b00, + InChopEnabled = 0b01, + IdacEnabled = 0b10, + InChopAndIdacEnabled = 0b11, +} + +/// SBMAGs follow the pattern `B` or `R`. +/// - `mag` is the magnitude of current or resistance where _ is a substitute for a decimal point. +/// - `units` are the units of current or resistance where uA is microamperes and MOhm is megaohms. +/// +/// B0_5uA = 0.5 microamps of current. R10MOhm = resistance of 10 megaohms. +#[repr(u8)] +pub enum SensorBiasMagnitude { + BNone = 0b000, + B0_5uA = 0b001, + B2uA = 0b010, + B10uA = 0b011, + B50uA = 0b100, + B200uA = 0b101, + R10MOhm = 0b110, +} + +#[repr(u8)] +pub enum CrcMode { + Disabled = 0b00, + Checksum = 0b01, + CRC = 0b10, +} + +#[repr(u8)] +pub enum DigitalFilter { + Sinc1 = 0b000, + Sinc2 = 0b001, + Sinc3 = 0b010, + Sinc4 = 0b011, + FIR = 0b100, +} + +/// Data rates follow the pattern `SPS`. +/// - `num` is the SPS rate where _ is a substitute for a decimal point. +/// +/// SPS2_5 = 2.5 SPS. +#[repr(u8)] +pub enum DataRate { + SPS2_5 = 0b0000, + SPS5 = 0b0001, + SPS10 = 0b0010, + SPS16_6 = 0b0011, // 16.6666... = 50/3 + SPS20 = 0b0100, + SPS50 = 0b0101, + SPS60 = 0b0110, + SPS100 = 0b0111, + SPS400 = 0b1000, + SPS1200 = 0b1001, + SPS2400 = 0b1010, + SPS4800 = 0b1011, + SPS7200 = 0b1100, + SPS14400 = 0b1101, + SPS19200 = 0b1110, + SPS38400 = 0b1111, +} + +#[repr(u8)] +pub enum PGAGain { + VV1 = 0b000, + VV2 = 0b001, + VV4 = 0b010, + VV8 = 0b011, + VV16 = 0b100, + VV32 = 0b101, +} + +#[repr(u8)] +pub enum NegativeInpMux { + AIN0 = 0b0000, + AIN1 = 0b0001, + AIN2 = 0b0010, + AIN3 = 0b0011, + AIN4 = 0b0100, + AIN5 = 0b0101, + AIN6 = 0b0110, + AIN7 = 0b0111, + AIN8 = 0b1000, + AIN9 = 0b1001, + AINCOM = 0b1010, + TempSensMonNeg = 0b1011, + AnlgPwrSupMonNeg = 0b1100, + DgtlPwrSubMonNeg = 0b1101, + TDACTestSignalNeg = 0b1110, + Float = 0b1111, +} + +#[repr(u8)] +pub enum PositiveInpMux { + AIN0 = 0b0000, + AIN1 = 0b0001, + AIN2 = 0b0010, + AIN3 = 0b0011, + AIN4 = 0b0100, + AIN5 = 0b0101, + AIN6 = 0b0110, + AIN7 = 0b0111, + AIN8 = 0b1000, + AIN9 = 0b1001, + AINCOM = 0b1010, + TempSensMonPos = 0b1011, + AnlgPwrSupMonPos = 0b1100, + DgtlPwrSubMonPos = 0b1101, + TDACTestSignalPos = 0b1110, + Float = 0b1111, +} + +#[repr(u8)] +pub enum IdacOutMux { + AIN0 = 0b0000, + AIN1 = 0b0001, + AIN2 = 0b0010, + AIN3 = 0b0011, + AIN4 = 0b0100, + AIN5 = 0b0101, + AIN6 = 0b0110, + AIN7 = 0b0111, + AIN8 = 0b1000, + AIN9 = 0b1001, + AINCOM = 0b1010, + NoConnection = 0b1011, +} + +/// Current magnitudes follow the pattern `I`. +/// - `mag` is the magnitude of current. +/// - `units` are uA meaning microamperes. +/// +/// I50uA = 50 microamps of current. +#[repr(u8)] +pub enum IdacCurMag { + I50uA = 0b0000, + I100uA = 0b0001, + I250uA = 0b0010, + I500uA = 0b0011, + I750uA = 0b0100, + I1000uA = 0b0101, + I1500uA = 0b0110, + I2000uA = 0b0111, + I2500uA = 0b1000, + I3000uA = 0b1001, +} + +#[repr(u8)] +pub enum RefNegativeInp { + Int2_5VRef = 0b000, + ExtAIN1 = 0b001, + ExtAIN3 = 0b010, + ExtAIN5 = 0b011, + IntAnlgSup = 0b100, +} + +#[repr(u8)] +pub enum RefPositiveInp { + Int2_5VRef = 0b000, + ExtAIN0 = 0b001, + ExtAIN2 = 0b010, + ExtAIN4 = 0b011, + IntAnlgSup = 0b100, +} + +/// Voltages are with respect to V_AVSS. +/// Output magnitudes follow the pattern `V`. +/// - `num` is the output magnitude in volts where _ is a substitute for a decimal point. +/// +/// V4_5 = 4.5 V. +#[repr(u8)] +pub enum TdacOutMag { + V4_5 = 0b01001, + V3_5 = 0b01000, + V3 = 0b00111, + V2_75 = 0b00110, + V2_625 = 0b00101, + V2_5625 = 0b00100, + V2_53125 = 0b00011, + V2_515625 = 0b00010, + V2_5078125 = 0b00001, + V2_5 = 0b00000, + V2_4921875 = 0b10001, + V2_484375 = 0b10010, + V2_46875 = 0b10011, + V2_4375 = 0b10100, + V2_375 = 0b10101, + V2_25 = 0b10110, + V2 = 0b10111, + V1_5 = 0b11000, + V0_5 = 0b11001, +} + +#[repr(u8)] +pub enum Adc2Gain { + VV1 = 0b000, + VV2 = 0b001, + VV4 = 0b010, + VV8 = 0b011, + VV16 = 0b100, + VV32 = 0b101, + VV64 = 0b110, + VV128 = 0b111, +} + +#[repr(u8)] +pub enum Adc2RefInp { + Int2_5VRef = 0b000, + ExtAIN0_1 = 0b001, + ExtAIN2_3 = 0b010, + ExtAIN4_5 = 0b011, + IntAnlgSup = 0b100, +} + +#[repr(u8)] +pub enum Adc2DataRate { + SPS10 = 0b00, + SPS100 = 0b01, + SPS400 = 0b10, + SPS800 = 0b11, +} diff --git a/crates/ads126x/tests/example.rs b/crates/ads126x/tests/example.rs new file mode 100644 index 0000000..8e70eb4 --- /dev/null +++ b/crates/ads126x/tests/example.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +use defmt::info; +use panic_probe as _; +use stm32h7xx_hal::pac; +use stm32h7xx_hal::prelude::*; + +#[defmt_test::tests] +mod tests { + use super::*; + + #[init] + fn init() { + let _cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + info!("Power enabled"); + // RCC + let mut rcc = dp.RCC.constrain(); + let reset = rcc.get_reset_reason(); + + info!("Reset reason: {:?}", reset); + + let _ccdr = rcc + .use_hse(48.MHz()) // check the clock hardware + .sys_ck(200.MHz()) + .freeze(pwrcfg, &dp.SYSCFG); + info!("RCC configured"); + } + + #[test] + fn example_test() { + assert!(true); + } +} diff --git a/crates/common-arm/src/lib.rs b/crates/common-arm/src/lib.rs index 4896d13..32d46e0 100644 --- a/crates/common-arm/src/lib.rs +++ b/crates/common-arm/src/lib.rs @@ -15,4 +15,4 @@ pub use crate::error::hydra_error::{ErrorContextTrait, HydraError, SpawnError}; pub use crate::logging::HydraLogging; pub use crate::sd_manager::SdManager; -use defmt_rtt as _; // global logger \ No newline at end of file +use defmt_rtt as _; // global logger diff --git a/examples/rtic-playground/src/data_manager.rs b/examples/rtic-playground/src/data_manager.rs index 2729860..67b5834 100644 --- a/examples/rtic-playground/src/data_manager.rs +++ b/examples/rtic-playground/src/data_manager.rs @@ -114,7 +114,7 @@ impl DataManager { pub fn handle_data(&mut self, data: Message) { match data.data { messages::Data::Sensor(ref sensor) => match sensor.data { - messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data{ + messages::sensor::SensorData::SbgData(ref sbg_data) => match sbg_data { messages::sensor::SbgData::EkfNavAcc(_) => { self.ekf_nav_acc = Some(data); } @@ -154,7 +154,7 @@ impl DataManager { messages::sensor::SbgData::GpsPos2(_) => { self.gps_pos_2 = Some(data); } - } + }, messages::sensor::SensorData::RecoverySensing(_) => { self.recovery_sensing = Some(data); } diff --git a/examples/rtic-playground/src/main.rs b/examples/rtic-playground/src/main.rs index d703bc0..2553651 100644 --- a/examples/rtic-playground/src/main.rs +++ b/examples/rtic-playground/src/main.rs @@ -327,9 +327,9 @@ mod app { loop { cx.shared.em.run(|| { let message = Message::new( - cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), COM_ID, messages::state::State::new(messages::state::StateData::Initializing), ); @@ -363,10 +363,13 @@ mod app { stm32h7xx_hal::rcc::ResetReason::Unknown { rcc_rsr } => sensor::ResetReason::Unknown { rcc_rsr }, stm32h7xx_hal::rcc::ResetReason::WindowWatchdogReset => sensor::ResetReason::WindowWatchdogReset, }; - let message = - messages::Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, sensor::Sensor::new(x)); + let message = messages::Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + sensor::Sensor::new(x), + ); cx.shared.em.run(|| { spawn!(send_gs, message)?; @@ -385,10 +388,13 @@ mod app { .lock(|data_manager| data_manager.state.clone()); cx.shared.em.run(|| { if let Some(x) = state_data { - let message = - Message::new(cx.shared.rtc.lock(|rtc| { - messages::FormattedNaiveDateTime(rtc.date_time().unwrap()) - }), COM_ID, messages::state::State::new(x)); + let message = Message::new( + cx.shared + .rtc + .lock(|rtc| messages::FormattedNaiveDateTime(rtc.date_time().unwrap())), + COM_ID, + messages::state::State::new(x), + ); spawn!(send_gs, message)?; } // if there is none we still return since we simply don't have data yet. Ok(()) @@ -442,8 +448,7 @@ mod app { } #[task(priority = 3, shared = [rtc, &em])] - async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) - { + async fn send_gs_intermediate(mut cx: send_gs_intermediate::Context, m: Data) { cx.shared.em.run(|| { cx.shared.rtc.lock(|rtc| { let message = messages::Message::new( @@ -572,4 +577,4 @@ mod app { sbg.set_low(); }); } -} \ No newline at end of file +}