
committed by
GitHub

6 changed files with 478 additions and 1 deletions
@ -0,0 +1,56 @@ |
|||
#![no_main] |
|||
#![no_std] |
|||
|
|||
#[allow(unused_imports)] |
|||
use panic_halt; |
|||
|
|||
use stm32f0xx_hal as hal; |
|||
|
|||
use crate::hal::delay::Delay; |
|||
use crate::hal::prelude::*; |
|||
use crate::hal::stm32; |
|||
|
|||
use crate::hal::adc::Adc; |
|||
|
|||
use cortex_m::peripheral::Peripherals; |
|||
use cortex_m_rt::entry; |
|||
|
|||
#[entry] |
|||
fn main() -> ! { |
|||
if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) { |
|||
let gpioa = p.GPIOA.split(); |
|||
|
|||
/* (Re-)configure PA1 as output */ |
|||
let mut led = gpioa.pa1.into_push_pull_output(); |
|||
|
|||
/* (Re-)configure PA0 as analog in */ |
|||
let mut an_in = gpioa.pa0.into_analog(); |
|||
|
|||
/* Constrain clocking registers */ |
|||
let rcc = p.RCC.constrain(); |
|||
|
|||
/* Configure clock to 8 MHz (i.e. the default) and freeze it */ |
|||
let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(); |
|||
|
|||
/* Get delay provider */ |
|||
let mut delay = Delay::new(cp.SYST, clocks); |
|||
|
|||
let mut adc = Adc::new(p.ADC); |
|||
|
|||
loop { |
|||
led.toggle(); |
|||
|
|||
let val: u16 = adc.read(&mut an_in).unwrap(); |
|||
|
|||
/* shift the value right by 3, same as divide by 8, reduces
|
|||
the 0-4095 range into something approximating 1-512 */ |
|||
let time: u16 = (val >> 3) + 1; |
|||
|
|||
delay.delay_ms(time); |
|||
} |
|||
} |
|||
|
|||
loop { |
|||
continue; |
|||
} |
|||
} |
@ -0,0 +1,397 @@ |
|||
//! # API for the Analog to Digital converter
|
|||
//!
|
|||
//! Currently implements oneshot conversion with variable sampling times.
|
|||
//! Also references for the internal temperature sense, voltage
|
|||
//! reference and battery sense are provided.
|
|||
//!
|
|||
//! ## Example
|
|||
//! ``` no_run
|
|||
//! use stm32f0xx_hal as hal;
|
|||
//!
|
|||
//! use crate::hal::stm32;
|
|||
//! use crate::hal::prelude::*;
|
|||
//! use crate::hal::adc::Adc;
|
|||
//!
|
|||
//! let mut p = stm32::Peripherals::take().unwrap();
|
|||
//!
|
|||
//! let mut led = gpioa.pa1.into_push_pull_pull_output();
|
|||
//! let mut an_in = gpioa.pa0.into_analog();
|
|||
//!
|
|||
//! let rcc = p.RCC.constrain().cfgr.freeze();
|
|||
//! let mut delay = Delay::new(cp.SYST, clocks);
|
|||
//!
|
|||
//! let mut adc = Adc::new(p.ADC);
|
|||
//!
|
|||
//! loop {
|
|||
//! let val: u16 = adc.read(&mut an_in).unwrap();
|
|||
//! if val < ((1 << 8) - 1) {
|
|||
//! led.set_low();
|
|||
//! } else {
|
|||
//! led.set_high();
|
|||
//! }
|
|||
//! delay.delay_ms(50_u16);
|
|||
//! }
|
|||
//! ```
|
|||
|
|||
use embedded_hal::adc::{Channel, OneShot}; |
|||
|
|||
use crate::stm32; |
|||
|
|||
use crate::gpio::*; |
|||
|
|||
/// Analog to Digital converter interface
|
|||
pub struct Adc { |
|||
rb: stm32::ADC, |
|||
sample_time: AdcSampleTime, |
|||
align: AdcAlign, |
|||
precision: AdcPrecision, |
|||
} |
|||
|
|||
#[derive(Debug, PartialEq)] |
|||
/// ADC Sampling time
|
|||
///
|
|||
/// Options for the sampling time, each is T + 0.5 ADC clock cycles.
|
|||
pub enum AdcSampleTime { |
|||
/// 1.5 cycles sampling time
|
|||
T_1, |
|||
/// 7.5 cycles sampling time
|
|||
T_7, |
|||
/// 13.5 cycles sampling time
|
|||
T_13, |
|||
/// 28.5 cycles sampling time
|
|||
T_28, |
|||
/// 41.5 cycles sampling time
|
|||
T_41, |
|||
/// 55.5 cycles sampling time
|
|||
T_55, |
|||
/// 71.5 cycles sampling time
|
|||
T_71, |
|||
/// 239.5 cycles sampling time
|
|||
T_239, |
|||
} |
|||
|
|||
impl AdcSampleTime { |
|||
fn write_bits(&self, adc: &mut stm32::ADC) { |
|||
unsafe { |
|||
adc.smpr.write(|w| { |
|||
w.smpr().bits(match self { |
|||
AdcSampleTime::T_1 => 0b000_u8, |
|||
AdcSampleTime::T_7 => 0b001_u8, |
|||
AdcSampleTime::T_13 => 0b010_u8, |
|||
AdcSampleTime::T_28 => 0b011_u8, |
|||
AdcSampleTime::T_41 => 0b100_u8, |
|||
AdcSampleTime::T_55 => 0b101_u8, |
|||
AdcSampleTime::T_71 => 0b110_u8, |
|||
AdcSampleTime::T_239 => 0b111_u8, |
|||
}) |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/// Get the default sample time (currently 239.5 cycles)
|
|||
pub fn default() -> Self { |
|||
AdcSampleTime::T_239 |
|||
} |
|||
} |
|||
|
|||
#[derive(Debug, PartialEq)] |
|||
/// ADC Result Alignment
|
|||
pub enum AdcAlign { |
|||
/// Left aligned results (most significant bits)
|
|||
///
|
|||
/// Results in all precisions returning a value in the range 0-65535.
|
|||
/// Depending on the precision the result will step by larger or smaller
|
|||
/// amounts.
|
|||
Left, |
|||
/// Right aligned results (least significant bits)
|
|||
///
|
|||
/// Results in all precisions returning values from 0-(2^bits-1) in
|
|||
/// steps of 1.
|
|||
Right, |
|||
/// Left aligned results without correction of 6bit values.
|
|||
///
|
|||
/// Returns left aligned results exactly as shown in RM0091 Fig.37.
|
|||
/// Where the values are left aligned within the u16, with the exception
|
|||
/// of 6 bit mode where the value is left aligned within the first byte of
|
|||
/// the u16.
|
|||
LeftAsRM, |
|||
} |
|||
|
|||
impl AdcAlign { |
|||
fn write_bits(&self, adc: &mut stm32::ADC) { |
|||
adc.cfgr1.write(|w| { |
|||
w.align().bit(match self { |
|||
AdcAlign::Left => true, |
|||
AdcAlign::Right => false, |
|||
AdcAlign::LeftAsRM => true, |
|||
}) |
|||
}); |
|||
} |
|||
|
|||
/// Get the default alignment (currently right aligned)
|
|||
pub fn default() -> Self { |
|||
AdcAlign::Right |
|||
} |
|||
} |
|||
|
|||
#[derive(Debug, PartialEq)] |
|||
/// ADC Sampling Precision
|
|||
pub enum AdcPrecision { |
|||
/// 12 bit precision
|
|||
B_12, |
|||
/// 10 bit precision
|
|||
B_10, |
|||
/// 8 bit precision
|
|||
B_8, |
|||
/// 6 bit precision
|
|||
B_6, |
|||
} |
|||
|
|||
impl AdcPrecision { |
|||
fn write_bits(&self, adc: &mut stm32::ADC) { |
|||
unsafe { |
|||
adc.cfgr1.write(|w| { |
|||
w.res().bits(match self { |
|||
AdcPrecision::B_12 => 0b00_u8, |
|||
AdcPrecision::B_10 => 0b01_u8, |
|||
AdcPrecision::B_8 => 0b10_u8, |
|||
AdcPrecision::B_6 => 0b11_u8, |
|||
}) |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/// Get the default precision (currently 12 bit precision)
|
|||
pub fn default() -> Self { |
|||
AdcPrecision::B_12 |
|||
} |
|||
} |
|||
|
|||
macro_rules! adc_pins { |
|||
($($pin:ty => $chan:expr),+ $(,)*) => { |
|||
$( |
|||
impl Channel<Adc> for $pin { |
|||
type ID = u8; |
|||
|
|||
fn channel() -> u8 { $chan } |
|||
} |
|||
)+ |
|||
}; |
|||
} |
|||
|
|||
#[cfg(any(feature = "stm32f042", feature = "stm32f030", feature = "stm32f070",))] |
|||
adc_pins!( |
|||
gpioa::PA0<Analog> => 0_u8, |
|||
gpioa::PA1<Analog> => 1_u8, |
|||
gpioa::PA2<Analog> => 2_u8, |
|||
gpioa::PA3<Analog> => 3_u8, |
|||
gpioa::PA4<Analog> => 4_u8, |
|||
gpioa::PA5<Analog> => 5_u8, |
|||
gpioa::PA6<Analog> => 6_u8, |
|||
gpioa::PA7<Analog> => 7_u8, |
|||
gpiob::PB0<Analog> => 8_u8, |
|||
gpiob::PB1<Analog> => 9_u8, |
|||
); |
|||
|
|||
#[cfg(any(feature = "stm32f030", feature = "stm32f070",))] |
|||
adc_pins!( |
|||
gpioc::PC0<Analog> => 10_u8, |
|||
gpioc::PC1<Analog> => 11_u8, |
|||
gpioc::PC2<Analog> => 12_u8, |
|||
gpioc::PC3<Analog> => 13_u8, |
|||
gpioc::PC4<Analog> => 14_u8, |
|||
gpioc::PC5<Analog> => 15_u8, |
|||
); |
|||
|
|||
#[derive(Debug)] |
|||
/// Internal temperature sensor (ADC Channel 16)
|
|||
pub struct VTemp; |
|||
|
|||
#[derive(Debug)] |
|||
/// Internal voltage reference (ADC Channel 17)
|
|||
pub struct VRef; |
|||
|
|||
adc_pins!( |
|||
VTemp => 16_u8, |
|||
VRef => 17_u8, |
|||
); |
|||
|
|||
impl VTemp { |
|||
/// Init a new VTemp
|
|||
pub fn new() -> Self { |
|||
VTemp {} |
|||
} |
|||
|
|||
/// Enable the internal temperature sense, this has a wake up time
|
|||
/// t<sub>START</sub> which can be found in your micro's datasheet, you
|
|||
/// must wait at least that long after enabling before taking a reading.
|
|||
/// Remember to disable when not in use.
|
|||
pub fn enable(&mut self, adc: &mut Adc) { |
|||
adc.rb.ccr.modify(|_, w| w.tsen().set_bit()); |
|||
} |
|||
|
|||
/// Disable the internal temperature sense.
|
|||
pub fn disable(&mut self, adc: &mut Adc) { |
|||
adc.rb.ccr.modify(|_, w| w.tsen().clear_bit()); |
|||
} |
|||
} |
|||
|
|||
impl VRef { |
|||
/// Init a new VRef
|
|||
pub fn new() -> Self { |
|||
VRef {} |
|||
} |
|||
|
|||
/// Enable the internal voltage reference, remember to disable when not in use.
|
|||
pub fn enable(&mut self, adc: &mut Adc) { |
|||
adc.rb.ccr.modify(|_, w| w.vrefen().set_bit()); |
|||
} |
|||
|
|||
/// Disable the internal reference voltage.
|
|||
pub fn disable(&mut self, adc: &mut Adc) { |
|||
adc.rb.ccr.modify(|_, w| w.vrefen().clear_bit()); |
|||
} |
|||
} |
|||
|
|||
#[cfg(any(feature = "stm32f042",))] |
|||
#[derive(Debug)] |
|||
/// Battery reference voltage (ADC Channel 18)
|
|||
pub struct VBat; |
|||
|
|||
#[cfg(any(feature = "stm32f042",))] |
|||
adc_pins!( |
|||
VBat => 18_u8, |
|||
); |
|||
|
|||
#[cfg(any(feature = "stm32f042",))] |
|||
impl VBat { |
|||
/// Init a new VBat
|
|||
pub fn new() -> Self { |
|||
VBat {} |
|||
} |
|||
|
|||
/// Enable the internal VBat sense, remember to disable when not in use
|
|||
/// as otherwise it will sap current from the VBat source.
|
|||
pub fn enable(&mut self, adc: &mut Adc) { |
|||
adc.rb.ccr.modify(|_, w| w.vbaten().set_bit()); |
|||
} |
|||
|
|||
/// Disable the internal VBat sense.
|
|||
pub fn disable(&mut self, adc: &mut Adc) { |
|||
adc.rb.ccr.modify(|_, w| w.vbaten().clear_bit()); |
|||
} |
|||
} |
|||
|
|||
impl Adc { |
|||
/// Init a new Adc
|
|||
///
|
|||
/// Sets all configurable parameters to defaults, enables the HSI14 clock
|
|||
/// for the ADC if it is not already enabled and performs a boot time
|
|||
/// calibration. As such this method may take an appreciable time to run.
|
|||
pub fn new(adc: stm32::ADC) -> Self { |
|||
let mut s = Self { |
|||
rb: adc, |
|||
sample_time: AdcSampleTime::default(), |
|||
align: AdcAlign::default(), |
|||
precision: AdcPrecision::default(), |
|||
}; |
|||
s.select_clock(); |
|||
s.calibrate(); |
|||
s |
|||
} |
|||
|
|||
/// Set the Adc sampling time
|
|||
///
|
|||
/// Options can be found in [AdcSampleTime](crate::adc::AdcSampleTime).
|
|||
pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { |
|||
self.sample_time = t_samp; |
|||
} |
|||
|
|||
/// Set the Adc result alignment
|
|||
///
|
|||
/// Options can be found in [AdcAlign](crate::adc::AdcAlign).
|
|||
pub fn set_align(&mut self, align: AdcAlign) { |
|||
self.align = align; |
|||
} |
|||
|
|||
/// Set the Adc precision
|
|||
///
|
|||
/// Options can be found in [AdcPrecision](crate::adc::AdcPrecision).
|
|||
pub fn set_precision(&mut self, precision: AdcPrecision) { |
|||
self.precision = precision; |
|||
} |
|||
|
|||
fn calibrate(&mut self) { |
|||
/* Ensure that ADEN = 0 */ |
|||
if self.rb.cr.read().aden().bit_is_set() { |
|||
/* Clear ADEN by setting ADDIS */ |
|||
self.rb.cr.modify(|_, w| w.addis().set_bit()); |
|||
} |
|||
while self.rb.cr.read().aden().bit_is_set() {} |
|||
|
|||
/* Clear DMAEN */ |
|||
self.rb.cfgr1.modify(|_, w| w.dmaen().clear_bit()); |
|||
|
|||
/* Start calibration by setting ADCAL */ |
|||
self.rb.cr.modify(|_, w| w.adcal().set_bit()); |
|||
|
|||
/* Wait until calibration is finished and ADCAL = 0 */ |
|||
while self.rb.cr.read().adcal().bit_is_set() {} |
|||
} |
|||
|
|||
fn select_clock(&mut self) { |
|||
let rcc = unsafe { &*stm32::RCC::ptr() }; |
|||
|
|||
rcc.apb2enr.modify(|_, w| w.adcen().set_bit()); |
|||
rcc.cr2.write(|w| w.hsi14on().set_bit()); |
|||
while rcc.cr2.read().hsi14rdy().bit_is_clear() {} |
|||
} |
|||
|
|||
fn power_up(&mut self) { |
|||
if self.rb.isr.read().adrdy().bit_is_set() { |
|||
self.rb.isr.modify(|_, w| w.adrdy().clear_bit()); |
|||
} |
|||
self.rb.cr.modify(|_, w| w.aden().set_bit()); |
|||
while self.rb.isr.read().adrdy().bit_is_clear() {} |
|||
} |
|||
|
|||
fn power_down(&mut self) { |
|||
self.rb.cr.modify(|_, w| w.adstp().set_bit()); |
|||
while self.rb.cr.read().adstp().bit_is_set() {} |
|||
self.rb.cr.modify(|_, w| w.addis().set_bit()); |
|||
while self.rb.cr.read().aden().bit_is_set() {} |
|||
} |
|||
|
|||
fn convert(&mut self, chan: u8) -> u16 { |
|||
self.rb.chselr.write(|w| unsafe { w.bits(1_u32 << chan) }); |
|||
|
|||
self.sample_time.write_bits(&mut self.rb); |
|||
self.align.write_bits(&mut self.rb); |
|||
self.precision.write_bits(&mut self.rb); |
|||
|
|||
self.rb.cr.modify(|_, w| w.adstart().set_bit()); |
|||
while self.rb.isr.read().eoc().bit_is_clear() {} |
|||
|
|||
let res = self.rb.dr.read().bits() as u16; |
|||
if self.align == AdcAlign::Left && self.precision == AdcPrecision::B_6 { |
|||
res << 8 |
|||
} else { |
|||
res |
|||
} |
|||
} |
|||
} |
|||
|
|||
impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc |
|||
where |
|||
WORD: From<u16>, |
|||
PIN: Channel<Adc, ID = u8>, |
|||
{ |
|||
type Error = (); |
|||
|
|||
fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> { |
|||
self.power_up(); |
|||
let res = self.convert(PIN::channel()); |
|||
self.power_down(); |
|||
Ok(res.into()) |
|||
} |
|||
} |
Loading…
Reference in new issue