Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HAL for Comparators #338

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,7 @@ required-features = ["rt"]
[[example]]
name = "adc_dma"
required-features = ["rt"]

[[example]]
name = "comparator"
required-features = ["rt"]
71 changes: 71 additions & 0 deletions examples/comparator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! 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 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().unwrap();

loop {
// Reading and Printing Output
let output = comparator.get_output_level();
rprintln!("{}", output);
delay.delay_ms(1000u32);
}
}
305 changes: 305 additions & 0 deletions src/comp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
//! 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},
};

// 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 {
/// 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",
feature = "stm32l412",
feature = "stm32l422",
feature = "stm32l432",
feature = "stm32l442",
feature = "stm32l452",
feature = "stm32l462",
feature = "stm32l433",
feature = "stm32l443",
))]
/// 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.
/// 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 {
/// No Hysterisis.
NoHysterisis = 0x00000000,
/// Low Hysterisis.
LowHysteresis = 0x00010000,
/// Medium Hysterisis.
MediumHysteresis = 0x00020000,
/// High Hysterisis.
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 {
/// No Blanking.
None = 0x00000000,
/// TIM1 OC5 as the blanking source.
Timloc5 = 0x400000,
}

/// 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.modify(|_, 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 {
/// The comparator device.
device: CompDevice,
/// The lock status of the comparator.
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,
is_locked: false,
};

let config = cfg.hyst as u32
| cfg.inmsel as u32
| cfg.inpsel as u32
| cfg.polarity as u32
| cfg.pwrmode as u32;

<pac::COMP>::enable(apb);
<pac::COMP>::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) -> 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.
///
/// 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),
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.
///
/// This function will return an Error when the comparator is locked.
pub fn start(&mut self) -> Result<(), ()> {
self.set_bit(0b1)
}

/// Stops the comparator.
///
/// This function will return an Error when the comparator is locked.
pub fn stop(&mut self) -> Result<(), ()> {
self.clear_bit(0b1)
}

/// Locks the comparator.
///
/// 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)
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,5 @@ mod sealed {
pub trait Sealed {}
}
pub(crate) use sealed::Sealed;

pub mod comp;
Loading