From 796247581f279319631f12fb5d3d8551989a2433 Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Wed, 8 Mar 2023 16:28:32 +0800 Subject: [PATCH 1/9] Added HAL for comparators --- src/comp.rs | 229 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/rcc/enable.rs | 2 + 3 files changed, 233 insertions(+) create mode 100644 src/comp.rs diff --git a/src/comp.rs b/src/comp.rs new file mode 100644 index 00000000..d66b200b --- /dev/null +++ b/src/comp.rs @@ -0,0 +1,229 @@ +//! Comparator +use crate::{pac, rcc::{APB2, Enable, Reset}}; + +// Config enums +/// Comparator power mode +pub enum PowerMode { + HighSpeed = 0x00000000, + MediumSpeed = 0x00000004, + LowSpeed = 0x0000000c, +} + +// TODO Io pins based on MCU +/// Comparator input plus (Non-inverting Input) +pub enum NonInvertingInput { + Io1 = 0x00000000, + Io2 = 0x00000080, +} + +// TODO Values are based on SCALEN (0x800000) and BRGEN (0x400000) check for other MCU. +/// Comparator input minus (Inverted Input) +pub enum InvertingInput { + /// 1/4 of Vref + OneQuarterVref = 0x00c00000, + /// 1/2 of Vref + OneHalfVref = 0x00c00010, + /// 3/4 of Vref + ThreeQuarterVref = 0x00c00020, + /// Vref + Vref = 0x00800030, + /// From DAC channel 1 + DacCh1 = 0x00000040, + /// From DAC channel 2 + DacCh2 = 0x00000050, + /// From the first GPIO pin connected to the comparator. + /// + /// The GPIO pin used depends on the MCU and comparator used. + Io1 = 0x00000060, + /// From the second GPIO pin connected to the comparator. + /// + /// The GPIO pin used depends on the MCU and comparator used. + Io2 = 0x00000070, +} + +/// Comparator hysterisis +pub enum Hysterisis { + NoHysterisis = 0x00000000, + LowHysteresis = 0x00010000, + MediumHysteresis = 0x00020000, + HighHysteresis = 0x00030000, +} + +/// Comparator output polarity +/// +/// When [OutputPolarity::NotInverted] is used. +/// The comparator output will be high (1) when [NonInvertingInput] has higher +/// voltage than [InvertingInput]. The comparator output will be low (0) when +/// [NonInvertingInput] has lower voltage than [InvertingInput]. +/// +/// When [OutputPolarity::Inverted] is used. +/// The comparator output will be high (1) when [NonInvertingInput] has lower +/// voltage than [InvertingInput]. The comparator output will be low (0) when +/// [NonInvertingInput] has higher voltage than [InvertingInput]. +pub enum OutputPolarity { + /// Comparator output will not be inverted. + NotInverted = 0x00000000, + /// Comparator output will be inverted. + Inverted = 0x00008000, +} + +/// Comparator blanking source +pub enum BlankingSource { + None = 0x00000000, + Timloc5, +} + +/// Comparator devices avaiable. +pub enum CompDevice { + /// Comparator number 1 (COMP1). + One, + /// Comparator number 2 (COMP2). + #[cfg(not(any(feature = "stm32l412", feature = "stm32l422")))] + Two, +} + +// Structs +/// Initial configuration data for the comparator peripheral. +pub struct CompConfig { + /// Comparator power mode. + pub pwrmode: PowerMode, + /// Comparator non-inverting input. + pub inpsel: NonInvertingInput, + /// Comparator inverting input. + pub inmsel: InvertingInput, + /// Comparator hysterisis. + pub hyst: Hysterisis, + /// Comparator output polarity. + pub polarity: OutputPolarity, + /// Comparator blanking source. + pub blanking: BlankingSource, +} + +/// Macro to write bits to the register +macro_rules! set_bit { + ($comp:ident, $value:expr) => { + unsafe { + let regs = &(*pac::COMP::ptr()).$comp; + regs.modify(|r, w| { + let current_bits = r.bits(); + let output_bits = current_bits | $value; + w.bits(output_bits) + }) + } + }; +} + +/// Macro to clear bits in the register +macro_rules! clear_bit { + ($comp:ident, $value:expr) => { + unsafe { + let regs = &(*pac::COMP::ptr()).$comp; + let current_bits = regs.read().bits(); + let output_bits = current_bits & !$value; + regs.write(|w| w.bits(output_bits)) + } + }; +} + +/// Macro to read bits in the register +macro_rules! read_bit { + ($comp:ident, $value:expr) => { + unsafe { + let regs = &(*pac::COMP::ptr()).$comp; + regs.read().bits() & $value + } + }; +} + +/// Macro to modify the register +macro_rules! modify_bit { + ($comp:ident, $value:expr) => { + unsafe { + let regs = &(*pac::COMP::ptr()).$comp; + regs.write(|w| w.bits($value)) + } + }; +} + +/// Represents an Analog Comparator peripheral. +pub struct Comp { + device: CompDevice, +} + +impl Comp { + /// Initialize the comparator peripheral. This will writes the configuration + /// according to `cfg`. + pub fn new(device: CompDevice, cfg: CompConfig, apb: &mut APB2) -> Self { + let result = Self { device }; + + let config = cfg.blanking as u32 + | cfg.hyst as u32 + | cfg.inmsel as u32 + | cfg.inpsel as u32 + | cfg.polarity as u32 + | cfg.pwrmode as u32; + + ::enable(apb); + ::reset(apb); + + match result.device { + CompDevice::One => modify_bit!(comp1_csr, config), + CompDevice::Two => modify_bit!(comp2_csr, config), + } + result + } + + /// Writes bit/bits to the regiter. + fn set_bit(&mut self, value: u32) { + match self.device { + CompDevice::One => set_bit!(comp1_csr, value), + CompDevice::Two => set_bit!(comp2_csr, value), + } + } + + /// Clears bit/bits in the register. + fn clear_bit(&mut self, value: u32) { + match self.device { + CompDevice::One => clear_bit!(comp1_csr, value), + CompDevice::Two => clear_bit!(comp2_csr, value), + } + } + + /// Read bit/bits in the register. + fn read_bit(&self, value: u32) -> u32 { + match self.device { + CompDevice::One => read_bit!(comp1_csr, value), + CompDevice::Two => read_bit!(comp2_csr, value), + } + } + + /// Gets the output level of the comparator + /// + /// The output level depends on the configuration of the comparator. + /// If the [polarity](CompConfig::polarity) is [NotInverted](OutputPolarity::NotInverted) + /// - It will output high (1) if the non-inverting input is higher than + /// the output of inverting input. + /// - It will output low (0) if the non-inverting input is lower than + /// the output of the inverting input. + /// + /// The oposite will be out inverted if [polarity](CompConfig::polarity) is + /// [Inverted](OutputPolarity::NotInverted). + pub fn get_output_level(&self) -> u32 { + self.read_bit(0b1 << 30) >> 30 + } + + /// Starts the comparator. + pub fn start(&mut self) { + self.set_bit(0b1); + } + + /// Stops the comparator. + pub fn stop(&mut self) { + self.clear_bit(0b1); + } + + /// Locks the comparator. + pub fn lock(&mut self) { + todo!() + } +} diff --git a/src/lib.rs b/src/lib.rs index e0788a66..d9fff178 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,3 +184,5 @@ mod sealed { pub trait Sealed {} } pub(crate) use sealed::Sealed; + +pub mod comp; diff --git a/src/rcc/enable.rs b/src/rcc/enable.rs index 67b9ac9f..7f5697d5 100644 --- a/src/rcc/enable.rs +++ b/src/rcc/enable.rs @@ -137,6 +137,8 @@ bus! { TIM15 => (APB2, tim15en, tim15smen, tim15rst), // 16 TIM16 => (APB2, tim16en, tim16smen, tim16rst), // 17 SAI1 => (APB2, sai1en, sai1smen, sai1rst), // 21 + + COMP => (APB2, syscfgen, syscfgsmen, syscfgrst), } // L4x1, L4x2, L4x3, L4x5 or L4x6 From f3e74a6b1de669d14727f1c0700737ff40c113ee Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Wed, 15 Mar 2023 23:01:18 +0800 Subject: [PATCH 2/9] Added comparator example --- examples/comparator.rs | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 examples/comparator.rs diff --git a/examples/comparator.rs b/examples/comparator.rs new file mode 100644 index 00000000..bba03548 --- /dev/null +++ b/examples/comparator.rs @@ -0,0 +1,73 @@ +//! Testing comparator. +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_halt; + +use cortex_m_rt::entry; +use rtt_target::rprintln; +use stm32l4xx_hal::{ + comp::{self, Comp, CompConfig, CompDevice}, + delay::Delay, + pac, + prelude::*, +}; + +#[entry] +fn main() -> ! { + // Set Up RTT + rtt_target::rtt_init_print!(); + + // Set up ARM Cortex-M peripherals. These are common to many MCUs, including all STM32 ones. + let cp = cortex_m::Peripherals::take().unwrap(); + // Set up peripherals specific to the microcontroller you're using. + let dp = pac::Peripherals::take().unwrap(); + + // Setting Up Clock + let mut rcc = dp.RCC.constrain(); + let mut flash = dp.FLASH.constrain(); + let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1); + + let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr); + + // Setting Up GPIO (Not really needed) + let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2); + gpiob.pb2.into_analog(&mut gpiob.moder, &mut gpiob.pupdr); + + // Setting Up Delay + let mut delay = Delay::new(cp.SYST, clocks); + + // Setting Up Comparator + // Comparator Configuration + let cfg = CompConfig { + // No blanking + blanking: comp::BlankingSource::None, + // No Hysterysis + hyst: comp::Hysterisis::NoHysterisis, + // Using internal Vref as negative input + // e.g. (1.22V) in STM32L47xx, STM32L48xx, STM32L49xx and STM32L4Axx. + // Consult Reference Manual for all negative input. + inmsel: comp::InvertingInput::Vref, + // Using Io2 as positive input + // e.g. (PB2) for COMP1 in STM32L47xx, STM32L48xx, STM32L49xx and STM32L4Axx. + // Consult Reference Manual for all positive input. + inpsel: comp::NonInvertingInput::Io2, + // Don't invert output high when inverting input < noninverting and etc. + polarity: comp::OutputPolarity::NotInverted, + // High Power Consumption (lowest propagation delay) + pwrmode: comp::PowerMode::HighSpeed, + }; + // Creating Comparator device using COMP1 + let mut comparator = Comp::new(CompDevice::One, cfg, &mut rcc.apb2); + // Starting Comparator + comparator.start(); + + loop { + // Reading and Printing Output + let output = comparator.get_output_level(); + rprintln!("{}", output); + delay.delay_ms(1000u32); + } +} From f92e9ff1d9fd803d39e4abcd28d93508b8984751 Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Wed, 15 Mar 2023 23:01:51 +0800 Subject: [PATCH 3/9] Reformatted Code --- src/comp.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/comp.rs b/src/comp.rs index d66b200b..c9131bf5 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -1,5 +1,8 @@ //! Comparator -use crate::{pac, rcc::{APB2, Enable, Reset}}; +use crate::{ + pac, + rcc::{Enable, Reset, APB2}, +}; // Config enums /// Comparator power mode From 98c6ff5925dccd7f4e6670fa3f203123d6cb644e Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Sat, 18 Mar 2023 17:55:05 +0800 Subject: [PATCH 4/9] Added PA1 support for L41xxx/2xxx/3xxx/4xxx/5xxx/6xxx --- src/comp.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/comp.rs b/src/comp.rs index c9131bf5..36786ed5 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -12,11 +12,24 @@ pub enum PowerMode { LowSpeed = 0x0000000c, } -// TODO Io pins based on MCU /// Comparator input plus (Non-inverting Input) pub enum NonInvertingInput { - Io1 = 0x00000000, - Io2 = 0x00000080, + PC5 = 0x00000000, + PB2 = 0x00000080, + // PA1 for STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx + #[cfg(any( + feature = "stm32l431", + feature = "stm32l451", + feature = "stm32l412", + feature = "stm32l422", + feature = "stm32l432", + feature = "stm32l442", + feature = "stm32l452", + feature = "stm32l462", + feature = "stm32l433", + feature = "stm32l443", + ))] + PA1 = 0x00000100, } // TODO Values are based on SCALEN (0x800000) and BRGEN (0x400000) check for other MCU. From b3fdc3f537796fa3d6a7c22113a3da7b7b16ef6d Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Sun, 19 Mar 2023 11:17:10 +0800 Subject: [PATCH 5/9] Added comparator lock functionality --- src/comp.rs | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/comp.rs b/src/comp.rs index 36786ed5..d5529851 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -164,13 +164,17 @@ macro_rules! modify_bit { /// Represents an Analog Comparator peripheral. pub struct Comp { device: CompDevice, + is_locked: bool, } impl Comp { /// Initialize the comparator peripheral. This will writes the configuration /// according to `cfg`. pub fn new(device: CompDevice, cfg: CompConfig, apb: &mut APB2) -> Self { - let result = Self { device }; + let result = Self { + device, + is_locked: false, + }; let config = cfg.blanking as u32 | cfg.hyst as u32 @@ -190,22 +194,38 @@ impl Comp { } /// Writes bit/bits to the regiter. - fn set_bit(&mut self, value: u32) { + fn set_bit(&mut self, value: u32) -> Result<(), ()> { + if self.is_locked { + return Err(()); + } + match self.device { CompDevice::One => set_bit!(comp1_csr, value), CompDevice::Two => set_bit!(comp2_csr, value), } + + Ok(()) } /// Clears bit/bits in the register. - fn clear_bit(&mut self, value: u32) { + /// + /// This function will return an Error when the comparator is locked. + fn clear_bit(&mut self, value: u32) -> Result<(), ()> { + if self.is_locked { + return Err(()); + } + match self.device { CompDevice::One => clear_bit!(comp1_csr, value), CompDevice::Two => clear_bit!(comp2_csr, value), } + + Ok(()) } /// Read bit/bits in the register. + /// + /// This function will return an Error when the comparator is locked. fn read_bit(&self, value: u32) -> u32 { match self.device { CompDevice::One => read_bit!(comp1_csr, value), @@ -229,17 +249,30 @@ impl Comp { } /// Starts the comparator. - pub fn start(&mut self) { - self.set_bit(0b1); + /// + /// This function will return an Error when the comparator is locked. + pub fn start(&mut self) -> Result<(), ()> { + self.set_bit(0b1) } /// Stops the comparator. - pub fn stop(&mut self) { - self.clear_bit(0b1); + /// + /// This function will return an Error when the comparator is locked. + pub fn stop(&mut self) -> Result<(), ()> { + self.clear_bit(0b1) } /// Locks the comparator. - pub fn lock(&mut self) { - todo!() + /// + /// This locks the comparator registers making it only read-only. + /// + /// *Note:* The lock also applies to the lock bit itself. Therefore, + /// the comparator register/configuration *cannot* be changed until + /// a hardware reset. + /// + /// This function will return an Error when the comparator is locked. + pub fn lock(&mut self) -> Result<(), ()> { + self.is_locked = true; + self.set_bit(0x80000000) } } From 3ee870612af445bee40613fea094fff98779a4bf Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Sun, 19 Mar 2023 11:42:01 +0800 Subject: [PATCH 6/9] Removed blanking source configuration --- src/comp.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/comp.rs b/src/comp.rs index d5529851..216c47ba 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -111,8 +111,8 @@ pub struct CompConfig { pub hyst: Hysterisis, /// Comparator output polarity. pub polarity: OutputPolarity, - /// Comparator blanking source. - pub blanking: BlankingSource, + // Comparator blanking source. + // pub blanking: BlankingSource, } /// Macro to write bits to the register @@ -176,8 +176,7 @@ impl Comp { is_locked: false, }; - let config = cfg.blanking as u32 - | cfg.hyst as u32 + let config = cfg.hyst as u32 | cfg.inmsel as u32 | cfg.inpsel as u32 | cfg.polarity as u32 From 051f437a5c002de8c5c700f1db43cbd21b3d2646 Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Sun, 19 Mar 2023 12:00:20 +0800 Subject: [PATCH 7/9] Updated documentation --- src/comp.rs | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/comp.rs b/src/comp.rs index 216c47ba..680c4504 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -1,4 +1,12 @@ //! Comparator +//! +//! TODO: +//! - Window Mode Configuration (COMP1 and COMP2 have different configs) +//! - Blanking Source Configuration (COMP1 and COMP2 have different configs) +//! - More Inputs For Inverting Input (STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx) +//! - Moving Peripheral into Struct (pac needs to change) +//! - Add Configuration Defaults +//! - Interrupts? use crate::{ pac, rcc::{Enable, Reset, APB2}, @@ -7,16 +15,25 @@ use crate::{ // Config enums /// Comparator power mode pub enum PowerMode { + /// High speed/full power (Lowest propagation delay). HighSpeed = 0x00000000, + /// Medium speed/medium power (Medium propagation delay). MediumSpeed = 0x00000004, + /// Low speed/ultra-low power (Highest propagation delay). LowSpeed = 0x0000000c, } /// Comparator input plus (Non-inverting Input) pub enum NonInvertingInput { - PC5 = 0x00000000, - PB2 = 0x00000080, - // PA1 for STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx + /// From the first GPIO pin connected to the comparator. + /// + /// The GPIO pin used depends on the MCU and comparator used. + Io1 = 0x00000000, + /// From the second GPIO pin connected to the comparator. + /// + /// The GPIO pin used depends on the MCU and comparator used. + Io2 = 0x00000080, + // PA1/PA3 for STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx #[cfg(any( feature = "stm32l431", feature = "stm32l451", @@ -29,7 +46,10 @@ pub enum NonInvertingInput { feature = "stm32l433", feature = "stm32l443", ))] - PA1 = 0x00000100, + /// From the third GPIO pin connected to the comparator. + /// + /// The GPIO pin used depends on the MCU and comparator used. + Io3 = 0x00000100, } // TODO Values are based on SCALEN (0x800000) and BRGEN (0x400000) check for other MCU. @@ -59,9 +79,13 @@ pub enum InvertingInput { /// Comparator hysterisis pub enum Hysterisis { + /// No Hysterisis. NoHysterisis = 0x00000000, + /// Low Hysterisis. LowHysteresis = 0x00010000, + /// Medium Hysterisis. MediumHysteresis = 0x00020000, + /// High Hysterisis. HighHysteresis = 0x00030000, } @@ -85,8 +109,10 @@ pub enum OutputPolarity { /// Comparator blanking source pub enum BlankingSource { + /// No Blanking. None = 0x00000000, - Timloc5, + /// TIM1 OC5 as the blanking source. + Timloc5 = 0x400000, } /// Comparator devices avaiable. @@ -163,7 +189,9 @@ macro_rules! modify_bit { /// Represents an Analog Comparator peripheral. pub struct Comp { + /// The comparator device. device: CompDevice, + /// The lock status of the comparator. is_locked: bool, } @@ -265,8 +293,8 @@ impl Comp { /// /// This locks the comparator registers making it only read-only. /// - /// *Note:* The lock also applies to the lock bit itself. Therefore, - /// the comparator register/configuration *cannot* be changed until + /// **Note:** The lock also applies to the lock bit itself. Therefore, + /// the comparator register/configuration **cannot** be changed until /// a hardware reset. /// /// This function will return an Error when the comparator is locked. From fc59a1c82647839aeb7f466d4b65e38cf2e83474 Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Mon, 20 Mar 2023 09:17:24 +0800 Subject: [PATCH 8/9] Updated example to fit new HAL --- Cargo.toml | 4 ++++ examples/comparator.rs | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 692c72bf..8d887b1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -203,3 +203,7 @@ required-features = ["rt"] [[example]] name = "adc_dma" required-features = ["rt"] + +[[example]] +name = "comparator" +required-features = ["rt"] diff --git a/examples/comparator.rs b/examples/comparator.rs index bba03548..d88e08f0 100644 --- a/examples/comparator.rs +++ b/examples/comparator.rs @@ -42,8 +42,6 @@ fn main() -> ! { // Setting Up Comparator // Comparator Configuration let cfg = CompConfig { - // No blanking - blanking: comp::BlankingSource::None, // No Hysterysis hyst: comp::Hysterisis::NoHysterisis, // Using internal Vref as negative input @@ -62,7 +60,7 @@ fn main() -> ! { // Creating Comparator device using COMP1 let mut comparator = Comp::new(CompDevice::One, cfg, &mut rcc.apb2); // Starting Comparator - comparator.start(); + comparator.start().unwrap(); loop { // Reading and Printing Output From c8bc58cd55ad1caf9f24b94d91bb0f791258e19f Mon Sep 17 00:00:00 2001 From: ecyht2 Date: Sat, 25 Mar 2023 17:36:34 +0800 Subject: [PATCH 9/9] clear_bit macro now use modify --- src/comp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/comp.rs b/src/comp.rs index 680c4504..bd603e89 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -162,7 +162,7 @@ macro_rules! clear_bit { let regs = &(*pac::COMP::ptr()).$comp; let current_bits = regs.read().bits(); let output_bits = current_bits & !$value; - regs.write(|w| w.bits(output_bits)) + regs.modify(|_, w| w.bits(output_bits)) } }; }