From e01c533c8da8031ba0e83211cdc19e45904919d1 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:10:13 -0400 Subject: [PATCH 01/31] added DAC with basic features --- src/dac.rs | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 160 insertions(+) create mode 100644 src/dac.rs diff --git a/src/dac.rs b/src/dac.rs new file mode 100644 index 000000000..6c7a8ca19 --- /dev/null +++ b/src/dac.rs @@ -0,0 +1,158 @@ +//! Configure the internal DAC on the stm32f3xx. +//! Incomplete, but includes basic operation. +//! +//! You should have the approprite dac pin set up as an analog input, to prevent +//! parasitic power consumption. For example: +//! ```rust +//! let _dac1_pin = gpioa.pa4.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); +//! let _dac2_pin = gpioa.pa5.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); +//! ``` +//! + + +// todo: DAC trait that will go in a PR to embeddd hal. +// [PR](https://github.com/rust-embedded/embedded-hal/pull/247) + +//! Digital-analog conversion traits +//! +//! A trait used to identify a digital-to-analog converter, and its +//! most fundamental features. + +use crate::rcc::APB1; + +pub trait DacTrait { + /// Enable the DAC. + fn enable(&mut self, p: &mut APB1); // todo: generalize periph clock + + /// Disable the DAC. + fn disable(&mut self); + + /// Output a constant signal, given a bit word. + fn set_value(&mut self, value: u32); + + /// Set a constant signal, given a voltage. + fn set_voltage(&mut self, volts: f32); +} + +use crate::stm32; + +#[derive(Clone, Copy, Debug)] +pub enum DacId { + One, + Two +} + +#[derive(Clone, Copy, Debug)] +pub enum DacBits { + EightR, + TwelveL, + TwelveR, +} + +pub struct Dac { + regs: stm32::DAC, + id: DacId, + bits: DacBits, + vref: f32, +} + +impl Dac { + /// Create a new DAC instances + pub fn new(regs: stm32::DAC, id: DacId, bits: DacBits, vref: f32) -> Self { + Self { regs, id, bits, vref } + } + + //// Independent trigger with single LFSR generation + // pub fn trigger_lfsr(&mut self) { + // // todo: incomplete + // match self.id { + // DacId::One => { + // self.regs.cr.modify(|_, w| w.ten1().set_bit()); + // self.regs.cr.modify(|_, w| unsafe { w.tsel1().bits(0)}); + // self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01)}); + // self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0)}); + // } + // DacId::Two => { + // self.regs.cr.modify(|_, w| w.ten2().set_bit()); + // self.regs.cr.modify(|_, w| unsafe { w.tsel2().bits(0)}); + // self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01)}); + // self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0)}); + // } + // } + // + // } + // + // /// Independent trigger with single triangle generation + // pub fn trigger_triangle(&mut self) { + // // todo: incomplete + // match self.id { + // DacId::One => { + // self.regs.cr.modify(|_, w| w.ten1().set_bit()); + // self.regs.cr.modify(|_, w| unsafe { w.tsel1().bits(0)}); + // // self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b1x)}); + // self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0)}); + // } + // DacId::Two => { + // self.regs.cr.modify(|_, w| w.ten2().set_bit()); + // self.regs.cr.modify(|_, w| unsafe { w.tsel2().bits(0)}); + // // self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b1x)}); + // self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0)}); + // } + // } + // } +} + +impl DacTrait for Dac { + /// Enable the DAC. + fn enable(&mut self, apb1: &mut APB1) { + match self.id { + DacId::One => { + apb1.enr().modify(|_, w| w.dac1en().set_bit()); + self.regs.cr.modify(|_, w| w.en1().set_bit()); + + }, + DacId::Two => { + apb1.enr().modify(|_, w| w.dac2en().set_bit()); + self.regs.cr.modify(|_, w| w.en2().set_bit()); + } + } + } + + fn disable(&mut self) { + match self.id { + DacId::One => self.regs.cr.modify(|_, w| w.en1().clear_bit()), + DacId::Two => self.regs.cr.modify(|_, w| w.en2().clear_bit()), + } + } + + /// Set the DAC value as an integer. + fn set_value(&mut self, val: u32) { + match self.id { + DacId::One => { + match self.bits { + DacBits::EightR => self.regs.dhr8r1.modify(|_, w| unsafe { w.bits(val)}), + DacBits::TwelveL => self.regs.dhr12l1.modify(|_, w| unsafe { w.bits(val)}), + DacBits::TwelveR => self.regs.dhr12r1.modify(|_, w| unsafe { w.bits(val)}), + } + } + DacId::Two => { + match self.bits { + DacBits::EightR => self.regs.dhr8r2.modify(|_, w| unsafe { w.bits(val)}), + DacBits::TwelveL => self.regs.dhr12l2.modify(|_, w| unsafe { w.bits(val)}), + DacBits::TwelveR => self.regs.dhr12r2.modify(|_, w| unsafe { w.bits(val)}), + } + } + } + } + + /// Set the DAC voltage. `v` is in Volts. + fn set_voltage(&mut self, volts: f32) { + let val = match self.bits { + DacBits::EightR => ((volts / self.vref) * 255.) as u32, + DacBits::TwelveL => ((volts / self.vref) * 4_095.) as u32, + DacBits::TwelveR => ((volts / self.vref) * 4_095.) as u32, + }; + + self.set_value(val); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d2bcd452d..eaf74fa22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,6 +115,8 @@ pub use crate::pac::interrupt; #[cfg(feature = "stm32f303")] pub mod adc; #[cfg(feature = "device-selected")] +pub mod dac; +#[cfg(feature = "device-selected")] pub mod delay; #[cfg(feature = "stm32f303")] pub mod dma; From 6bd5df1038bf213b157679c5a503bfbefb7a769b Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:16:54 -0400 Subject: [PATCH 02/31] added dac example --- examples/dac.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/dac.rs diff --git a/examples/dac.rs b/examples/dac.rs new file mode 100644 index 000000000..22fb8a877 --- /dev/null +++ b/examples/dac.rs @@ -0,0 +1,55 @@ +#![no_main] +#![no_std] + +use cortex_m; +use cortex_m_rt::entry; +use embedded_hal::blocking::delay::DelayMs; +use stm32f3xx_hal as hal; +use hal::{ + delay::Delay, + prelude::*, + pac, + dac::{Dac, DacBits, DacId, DacTrait} +}; + +// Handle panics and println. +use rtt_target::{rprintln, rtt_init_print}; + +#[entry] +fn main() -> ! { + // Enable RTT debug output (printing) + rtt_init_print!(); + // Set up CPU peripherals + let mut cp = cortex_m::Peripherals::take().unwrap(); + + // Set up microcontroller peripherals + let dp = pac::Peripherals::take().unwrap(); + + // Set up clocks + let mut flash = dp.FLASH.constrain(); + let mut rcc = dp.RCC.constrain(); + let clocks = rcc.cfgr.freeze(&mut flash.acr); + + let mut delay = Delay::new(cp.SYST, clocks); + + // Set up gpio pins if required + let mut gpioa = dp.GPIOA.split(&mut rcc.ahb); + + // Set up the DAC + let mut _dac_pin = gpioa.pa4.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); + + let mut dac = Dac::new(dp.DAC, DacId::One, DacBits::EightR, 3.3); + dac.enable(&mut rcc.apb1); + + dac.set_voltage(1.5); + // dac.set_value(128); // or equivalently + + loop { + delay.delay_ms(1_000_u16); + } +} + +#[panic_handler] +fn my_panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} From d7a63884a7473343a42567aa0593548ccb7ec02f Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:17:29 -0400 Subject: [PATCH 03/31] moved rprintln --- examples/dac.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/dac.rs b/examples/dac.rs index 22fb8a877..fc848203a 100644 --- a/examples/dac.rs +++ b/examples/dac.rs @@ -12,13 +12,8 @@ use hal::{ dac::{Dac, DacBits, DacId, DacTrait} }; -// Handle panics and println. -use rtt_target::{rprintln, rtt_init_print}; - #[entry] fn main() -> ! { - // Enable RTT debug output (printing) - rtt_init_print!(); // Set up CPU peripherals let mut cp = cortex_m::Peripherals::take().unwrap(); From 71a74720c69ea49b61eeb2bca14c37d6933153cd Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:28:06 -0400 Subject: [PATCH 04/31] fixed some bugs --- src/dac.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/dac.rs b/src/dac.rs index 6c7a8ca19..902d8525f 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -18,7 +18,7 @@ //! A trait used to identify a digital-to-analog converter, and its //! most fundamental features. -use crate::rcc::APB1; +use crate::{pac, rcc::APB1}; pub trait DacTrait { /// Enable the DAC. @@ -34,8 +34,6 @@ pub trait DacTrait { fn set_voltage(&mut self, volts: f32); } -use crate::stm32; - #[derive(Clone, Copy, Debug)] pub enum DacId { One, @@ -50,7 +48,7 @@ pub enum DacBits { } pub struct Dac { - regs: stm32::DAC, + regs: pac::DAC, id: DacId, bits: DacBits, vref: f32, @@ -58,7 +56,7 @@ pub struct Dac { impl Dac { /// Create a new DAC instances - pub fn new(regs: stm32::DAC, id: DacId, bits: DacBits, vref: f32) -> Self { + pub fn new(regs: pac::DAC, id: DacId, bits: DacBits, vref: f32) -> Self { Self { regs, id, bits, vref } } From 0b62bf206930c14e22bd793329feca9edf8ee960 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:31:41 -0400 Subject: [PATCH 05/31] Disable clocks on disable --- src/dac.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/dac.rs b/src/dac.rs index 902d8525f..b5b230b09 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -20,12 +20,12 @@ use crate::{pac, rcc::APB1}; -pub trait DacTrait { +pub trait DacTrait

{ /// Enable the DAC. fn enable(&mut self, p: &mut APB1); // todo: generalize periph clock /// Disable the DAC. - fn disable(&mut self); + fn disable(&mut self, p: &mut APB1); /// Output a constant signal, given a bit word. fn set_value(&mut self, value: u32); @@ -108,7 +108,7 @@ impl DacTrait for Dac { apb1.enr().modify(|_, w| w.dac1en().set_bit()); self.regs.cr.modify(|_, w| w.en1().set_bit()); - }, + } DacId::Two => { apb1.enr().modify(|_, w| w.dac2en().set_bit()); self.regs.cr.modify(|_, w| w.en2().set_bit()); @@ -116,10 +116,16 @@ impl DacTrait for Dac { } } - fn disable(&mut self) { + fn disable(&mut self, apb1: &mut APB1) { match self.id { - DacId::One => self.regs.cr.modify(|_, w| w.en1().clear_bit()), - DacId::Two => self.regs.cr.modify(|_, w| w.en2().clear_bit()), + DacId::One => { + self.regs.cr.modify(|_, w| w.en1().clear_bit()); + apb1.enr().modify(|_, w| w.dac1en().clear_bit()); + } + DacId::Two => { + self.regs.cr.modify(|_, w| w.en2().clear_bit()); + apb1.enr().modify(|_, w| w.dac2en().clear_bit()); + } } } From a0a536bfc99204607a9dceb04c2ffeb0b06580cf Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:35:40 -0400 Subject: [PATCH 06/31] Fixed typo --- src/dac.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dac.rs b/src/dac.rs index b5b230b09..8166d448b 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -20,7 +20,7 @@ use crate::{pac, rcc::APB1}; -pub trait DacTrait

{ +pub trait DacTrait { /// Enable the DAC. fn enable(&mut self, p: &mut APB1); // todo: generalize periph clock From dc1f6b41f30030e06aa084d1e222b9da08788475 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:37:43 -0400 Subject: [PATCH 07/31] fmt --- examples/dac.rs | 6 +++--- src/dac.rs | 39 +++++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/examples/dac.rs b/examples/dac.rs index fc848203a..ec691f3ac 100644 --- a/examples/dac.rs +++ b/examples/dac.rs @@ -4,13 +4,13 @@ use cortex_m; use cortex_m_rt::entry; use embedded_hal::blocking::delay::DelayMs; -use stm32f3xx_hal as hal; use hal::{ + dac::{Dac, DacBits, DacId, DacTrait}, delay::Delay, - prelude::*, pac, - dac::{Dac, DacBits, DacId, DacTrait} + prelude::*, }; +use stm32f3xx_hal as hal; #[entry] fn main() -> ! { diff --git a/src/dac.rs b/src/dac.rs index 8166d448b..f4d287320 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -9,7 +9,6 @@ //! ``` //! - // todo: DAC trait that will go in a PR to embeddd hal. // [PR](https://github.com/rust-embedded/embedded-hal/pull/247) @@ -22,7 +21,7 @@ use crate::{pac, rcc::APB1}; pub trait DacTrait { /// Enable the DAC. - fn enable(&mut self, p: &mut APB1); // todo: generalize periph clock + fn enable(&mut self, p: &mut APB1); // todo: generalize periph clock /// Disable the DAC. fn disable(&mut self, p: &mut APB1); @@ -37,7 +36,7 @@ pub trait DacTrait { #[derive(Clone, Copy, Debug)] pub enum DacId { One, - Two + Two, } #[derive(Clone, Copy, Debug)] @@ -57,7 +56,12 @@ pub struct Dac { impl Dac { /// Create a new DAC instances pub fn new(regs: pac::DAC, id: DacId, bits: DacBits, vref: f32) -> Self { - Self { regs, id, bits, vref } + Self { + regs, + id, + bits, + vref, + } } //// Independent trigger with single LFSR generation @@ -107,7 +111,6 @@ impl DacTrait for Dac { DacId::One => { apb1.enr().modify(|_, w| w.dac1en().set_bit()); self.regs.cr.modify(|_, w| w.en1().set_bit()); - } DacId::Two => { apb1.enr().modify(|_, w| w.dac2en().set_bit()); @@ -132,20 +135,16 @@ impl DacTrait for Dac { /// Set the DAC value as an integer. fn set_value(&mut self, val: u32) { match self.id { - DacId::One => { - match self.bits { - DacBits::EightR => self.regs.dhr8r1.modify(|_, w| unsafe { w.bits(val)}), - DacBits::TwelveL => self.regs.dhr12l1.modify(|_, w| unsafe { w.bits(val)}), - DacBits::TwelveR => self.regs.dhr12r1.modify(|_, w| unsafe { w.bits(val)}), - } - } - DacId::Two => { - match self.bits { - DacBits::EightR => self.regs.dhr8r2.modify(|_, w| unsafe { w.bits(val)}), - DacBits::TwelveL => self.regs.dhr12l2.modify(|_, w| unsafe { w.bits(val)}), - DacBits::TwelveR => self.regs.dhr12r2.modify(|_, w| unsafe { w.bits(val)}), - } - } + DacId::One => match self.bits { + DacBits::EightR => self.regs.dhr8r1.modify(|_, w| unsafe { w.bits(val) }), + DacBits::TwelveL => self.regs.dhr12l1.modify(|_, w| unsafe { w.bits(val) }), + DacBits::TwelveR => self.regs.dhr12r1.modify(|_, w| unsafe { w.bits(val) }), + }, + DacId::Two => match self.bits { + DacBits::EightR => self.regs.dhr8r2.modify(|_, w| unsafe { w.bits(val) }), + DacBits::TwelveL => self.regs.dhr12l2.modify(|_, w| unsafe { w.bits(val) }), + DacBits::TwelveR => self.regs.dhr12r2.modify(|_, w| unsafe { w.bits(val) }), + }, } } @@ -159,4 +158,4 @@ impl DacTrait for Dac { self.set_value(val); } -} \ No newline at end of file +} From 1da6b831a82bd00ab072ce74165aa617afd8d789 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:40:47 -0400 Subject: [PATCH 08/31] Only support dac on f303 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index eaf74fa22..3ffce1ef2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ pub use crate::pac::interrupt; #[cfg(feature = "stm32f303")] pub mod adc; -#[cfg(feature = "device-selected")] +#[cfg(feature = "stm32f303")] pub mod dac; #[cfg(feature = "device-selected")] pub mod delay; From 4bc115296dc1cf7766ffed103e4d069092f77d54 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Mon, 31 Aug 2020 23:43:50 -0400 Subject: [PATCH 09/31] Tweaked example --- examples/dac.rs | 7 +------ src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/dac.rs b/examples/dac.rs index ec691f3ac..ae54ffa9c 100644 --- a/examples/dac.rs +++ b/examples/dac.rs @@ -42,9 +42,4 @@ fn main() -> ! { loop { delay.delay_ms(1_000_u16); } -} - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3ffce1ef2..eaf74fa22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ pub use crate::pac::interrupt; #[cfg(feature = "stm32f303")] pub mod adc; -#[cfg(feature = "stm32f303")] +#[cfg(feature = "device-selected")] pub mod dac; #[cfg(feature = "device-selected")] pub mod delay; From d070c5e6f98f41e07ed639fac3c94bd49bf71c6f Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Tue, 1 Sep 2020 20:14:42 -0400 Subject: [PATCH 10/31] Updated trait --- Cargo.toml | 1 + src/dac.rs | 35 +++++++++++++++++++++++++---------- src/lib.rs | 2 ++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 902a9954c..9c4b5b818 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ cortex-m-rt = "0.6" embedded-hal = "0.2" nb = "0.1" stm32f3 = "0.11" +rtcc = "0.2.0" [dependencies.bare-metal] version = "0.2" diff --git a/src/dac.rs b/src/dac.rs index f4d287320..be25ab109 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -19,18 +19,21 @@ use crate::{pac, rcc::APB1}; -pub trait DacTrait { +pub trait SingleChannelDac { + /// Error type returned by DAC methods + type Error; + /// Enable the DAC. - fn enable(&mut self, p: &mut APB1); // todo: generalize periph clock + fn enable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; // todo: generalize periph clock /// Disable the DAC. - fn disable(&mut self, p: &mut APB1); + fn disable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; /// Output a constant signal, given a bit word. - fn set_value(&mut self, value: u32); + fn set_value(&mut self, value: u32) -> Result<(), Self::Error>; /// Set a constant signal, given a voltage. - fn set_voltage(&mut self, volts: f32); + fn set_voltage(&mut self, volts: f32) -> Result<(), Self::Error>; } #[derive(Clone, Copy, Debug)] @@ -104,9 +107,13 @@ impl Dac { // } } -impl DacTrait for Dac { +pub struct DacError {} + +impl SingleChannelDac for Dac { + type Error = DacError; + /// Enable the DAC. - fn enable(&mut self, apb1: &mut APB1) { + fn enable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { match self.id { DacId::One => { apb1.enr().modify(|_, w| w.dac1en().set_bit()); @@ -117,9 +124,11 @@ impl DacTrait for Dac { self.regs.cr.modify(|_, w| w.en2().set_bit()); } } + + Ok(()) } - fn disable(&mut self, apb1: &mut APB1) { + fn disable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { match self.id { DacId::One => { self.regs.cr.modify(|_, w| w.en1().clear_bit()); @@ -130,10 +139,12 @@ impl DacTrait for Dac { apb1.enr().modify(|_, w| w.dac2en().clear_bit()); } } + + Ok(()) } /// Set the DAC value as an integer. - fn set_value(&mut self, val: u32) { + fn set_value(&mut self, val: u32) -> Result<(), DacError> { match self.id { DacId::One => match self.bits { DacBits::EightR => self.regs.dhr8r1.modify(|_, w| unsafe { w.bits(val) }), @@ -146,10 +157,12 @@ impl DacTrait for Dac { DacBits::TwelveR => self.regs.dhr12r2.modify(|_, w| unsafe { w.bits(val) }), }, } + + Ok(()) } /// Set the DAC voltage. `v` is in Volts. - fn set_voltage(&mut self, volts: f32) { + fn set_voltage(&mut self, volts: f32) -> Result<(), DacError> { let val = match self.bits { DacBits::EightR => ((volts / self.vref) * 255.) as u32, DacBits::TwelveL => ((volts / self.vref) * 4_095.) as u32, @@ -157,5 +170,7 @@ impl DacTrait for Dac { }; self.set_value(val); + + Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index eaf74fa22..ccc79813f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,6 +133,8 @@ pub mod pwm; #[cfg(feature = "device-selected")] pub mod rcc; #[cfg(feature = "device-selected")] +pub mod rtc; +#[cfg(feature = "device-selected")] pub mod serial; #[cfg(feature = "device-selected")] pub mod spi; From ee997bebd84725cabe1e0f962eccf16b213ed9f9 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Tue, 1 Sep 2020 21:57:04 -0400 Subject: [PATCH 11/31] Added trigger support --- src/dac.rs | 113 ++++++--- src/rcc.rs | 673 ----------------------------------------------------- 2 files changed, 75 insertions(+), 711 deletions(-) delete mode 100644 src/rcc.rs diff --git a/src/dac.rs b/src/dac.rs index be25ab109..4859d9e73 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -49,6 +49,33 @@ pub enum DacBits { TwelveR, } +#[derive(Clone, Copy, Debug)] +pub enum Trigger { + Tim6, + Tim3_8, + Tim7, + Tim15, + Tim2, + Tim4, // Not available on DAC 2. + Exti9, + Swtrig, +} + +impl Trigger { + pub fn bits(&self) -> u8 { + match self { + Self::Tim6 => 0b000, + Self::Tim3_8 => 0b001, + Self::Tim7 => 0b010, + Self::Tim15 => 0b011, + Self::Tim2 => 0b100, + Self::Tim4 => 0b101, + Self::Exti9 => 0b110, + Self::Swtrig => 0b111, + } + } +} + pub struct Dac { regs: pac::DAC, id: DacId, @@ -67,44 +94,54 @@ impl Dac { } } - //// Independent trigger with single LFSR generation - // pub fn trigger_lfsr(&mut self) { - // // todo: incomplete - // match self.id { - // DacId::One => { - // self.regs.cr.modify(|_, w| w.ten1().set_bit()); - // self.regs.cr.modify(|_, w| unsafe { w.tsel1().bits(0)}); - // self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01)}); - // self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0)}); - // } - // DacId::Two => { - // self.regs.cr.modify(|_, w| w.ten2().set_bit()); - // self.regs.cr.modify(|_, w| unsafe { w.tsel2().bits(0)}); - // self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01)}); - // self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0)}); - // } - // } - // - // } - // - // /// Independent trigger with single triangle generation - // pub fn trigger_triangle(&mut self) { - // // todo: incomplete - // match self.id { - // DacId::One => { - // self.regs.cr.modify(|_, w| w.ten1().set_bit()); - // self.regs.cr.modify(|_, w| unsafe { w.tsel1().bits(0)}); - // // self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b1x)}); - // self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0)}); - // } - // DacId::Two => { - // self.regs.cr.modify(|_, w| w.ten2().set_bit()); - // self.regs.cr.modify(|_, w| unsafe { w.tsel2().bits(0)}); - // // self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b1x)}); - // self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0)}); - // } - // } - // } + // Select and activate a trigger. See f303 Reference manual, section 16.5.4. + pub fn set_trigger(&mut self, trigger: Trigger) { + match self.id { + DacId::One => { + self.regs.cr.modify(|_, w| w.ten1().set_bit()); + self.regs.cr.modify(|_, w| unsafe { w.tsel1().bits(trigger.bits())}); + } + DacId::Two => { + self.regs.cr.modify(|_, w| w.ten2().set_bit()); + self.regs.cr.modify(|_, w| unsafe { w.tsel2().bits(trigger.bits())}); + } + } + } + + /// Independent trigger with single LFSR generation + /// See f303 Reference Manual section 16.5.2 + pub fn trigger_lfsr(&mut self, trigger: Trigger, data: u32) { + // todo: This may not be correct. + match self.id { + DacId::One => { + self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01)}); + self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b01)}); + } + DacId::Two => { + self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01)}); + self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b01)}); + } + } + self.set_trigger(trigger); + self.set_value(data); + } + + /// Independent trigger with single triangle generation + pub fn trigger_triangle(&mut self, trigger: Trigger, data: u32) { + // todo: This may not be correct. + match self.id { + DacId::One => { + self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b10)}); + self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b10)}); + } + DacId::Two => { + self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b10)}); + self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b10)}); + } + } + self.set_trigger(trigger); + self.set_value(data); + } } pub struct DacError {} diff --git a/src/rcc.rs b/src/rcc.rs deleted file mode 100644 index 3c8adeb5c..000000000 --- a/src/rcc.rs +++ /dev/null @@ -1,673 +0,0 @@ -//! Reset and Clock Control - -use crate::pac::{ - rcc::{self, cfgr, cfgr2}, - RCC, -}; - -use crate::flash::ACR; -use crate::time::Hertz; - -/// Extension trait that constrains the `RCC` peripheral -pub trait RccExt { - /// Constrains the `RCC` peripheral so it plays nicely with the other abstractions - fn constrain(self) -> Rcc; -} - -impl RccExt for RCC { - fn constrain(self) -> Rcc { - Rcc { - ahb: AHB { _0: () }, - apb1: APB1 { _0: () }, - apb2: APB2 { _0: () }, - cfgr: CFGR { - hse: None, - hclk: None, - pclk1: None, - pclk2: None, - sysclk: None, - }, - } - } -} - -/// Constrained RCC peripheral -/// -/// An instance of this struct is aquired by calling the -/// [constrain](trait.RccExt.html#tymethod.constrain) function on the -/// [pac::RCC](../pac/struct.RCC.html) struct. -/// -/// ``` -/// let dp = pac::Peripherals::take().unwrap(); -/// let rcc = dp.RCC.constrain(); -/// ``` -pub struct Rcc { - /// AMBA High-performance Bus (AHB) registers - pub ahb: AHB, - /// Advanced Peripheral Bus 1 (APB1) registers - pub apb1: APB1, - /// Advanced Peripheral Bus 2 (APB2) registers - pub apb2: APB2, - /// Clock configuration - pub cfgr: CFGR, -} - -/// AMBA High-performance Bus (AHB) registers -/// -/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. -/// -/// ``` -/// let dp = pac::Peripherals::take().unwrap(); -/// let rcc = dp.RCC.constrain(); -/// use_ahb(&mut rcc.ahb) -/// ``` -pub struct AHB { - _0: (), -} - -impl AHB { - pub(crate) fn enr(&mut self) -> &rcc::AHBENR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).ahbenr } - } - - pub(crate) fn rstr(&mut self) -> &rcc::AHBRSTR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).ahbrstr } - } -} - -/// Advanced Peripheral Bus 1 (APB1) registers -/// -/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. -/// -/// ``` -/// let dp = pac::Peripherals::take().unwrap(); -/// let rcc = dp.RCC.constrain(); -/// use_ahb(&mut rcc.apb1) -/// ``` -pub struct APB1 { - _0: (), -} - -impl APB1 { - pub(crate) fn enr(&mut self) -> &rcc::APB1ENR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).apb1enr } - } - - pub(crate) fn rstr(&mut self) -> &rcc::APB1RSTR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).apb1rstr } - } -} - -/// Advanced Peripheral Bus 2 (APB2) registers -/// -/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. -/// -/// ``` -/// let dp = pac::Peripherals::take().unwrap(); -/// let rcc = dp.RCC.constrain(); -/// use_ahb(&mut rcc.apb2) -/// ``` -pub struct APB2 { - _0: (), -} - -impl APB2 { - pub(crate) fn enr(&mut self) -> &rcc::APB2ENR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).apb2enr } - } - - pub(crate) fn rstr(&mut self) -> &rcc::APB2RSTR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).apb2rstr } - } -} - -const HSI: u32 = 8_000_000; // Hz - -// some microcontrollers do not have USB -#[cfg(any(feature = "stm32f301", feature = "stm32f318", feature = "stm32f334",))] -mod usb_clocking { - use crate::rcc::PllConfig; - - pub(crate) fn is_valid( - _sysclk: u32, - _hse: Option, - _pclk1: u32, - _pll_config: &Option, - ) -> (bool, bool) { - (false, false) - } - - pub(crate) fn set_usbpre(w: &mut W, _: bool) -> &mut W { - w - } -} - -#[cfg(any( - feature = "stm32f302", - feature = "stm32f303", - feature = "stm32f373", - feature = "stm32f378", - feature = "stm32f328", - feature = "stm32f358", - feature = "stm32f398", -))] -mod usb_clocking { - use crate::pac::rcc::cfgr; - use crate::rcc::PllConfig; - - /// Check for all clock options to be - pub(crate) fn is_valid( - sysclk: u32, - hse: Option, - pclk1: u32, - pll_config: &Option, - ) -> (cfgr::USBPRE_A, bool) { - // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the - // PLL output frequency is a supported one. - // usbpre == false: divide clock by 1.5, otherwise no division - let usb_ok = hse.is_some() && pll_config.is_some(); - // The APB1 clock must have a minimum frequency of 10 MHz to avoid data overrun/underrun - // problems. [RM0316 32.5.2] - if pclk1 >= 10_000_000 { - match (usb_ok, sysclk) { - (true, 72_000_000) => (cfgr::USBPRE_A::DIV1_5, true), - (true, 48_000_000) => (cfgr::USBPRE_A::DIV1, true), - _ => (cfgr::USBPRE_A::DIV1, false), - } - } else { - (cfgr::USBPRE_A::DIV1, false) - } - } - - pub(crate) fn set_usbpre(w: &mut cfgr::W, usb_prescale: cfgr::USBPRE_A) -> &mut cfgr::W { - w.usbpre().variant(usb_prescale) - } -} - -/// Clock configuration -/// -/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. -/// -/// ``` -/// let dp = pac::Peripherals::take().unwrap(); -/// let rcc = dp.RCC.constrain(); -/// use_ahb(&mut rcc.cfgr) -/// ``` -pub struct CFGR { - hse: Option, - hclk: Option, - pclk1: Option, - pclk2: Option, - sysclk: Option, -} - -pub(crate) struct PllConfig { - src: cfgr::PLLSRC_A, - mul: cfgr::PLLMUL_A, - div: Option, -} - -/// Determine the [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) -/// -/// This function is based on the [Euclidean algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm). -fn gcd(mut a: u32, mut b: u32) -> u32 { - while b != 0 { - let r = a % b; - a = b; - b = r; - } - a -} - -/// Convert pll multiplier into equivalent register field type -fn into_pll_mul(mul: u8) -> cfgr::PLLMUL_A { - match mul { - 2 => cfgr::PLLMUL_A::MUL2, - 3 => cfgr::PLLMUL_A::MUL3, - 4 => cfgr::PLLMUL_A::MUL4, - 5 => cfgr::PLLMUL_A::MUL5, - 6 => cfgr::PLLMUL_A::MUL6, - 7 => cfgr::PLLMUL_A::MUL7, - 8 => cfgr::PLLMUL_A::MUL8, - 9 => cfgr::PLLMUL_A::MUL9, - 10 => cfgr::PLLMUL_A::MUL10, - 11 => cfgr::PLLMUL_A::MUL11, - 12 => cfgr::PLLMUL_A::MUL12, - 13 => cfgr::PLLMUL_A::MUL13, - 14 => cfgr::PLLMUL_A::MUL14, - 15 => cfgr::PLLMUL_A::MUL15, - 16 => cfgr::PLLMUL_A::MUL16, - _ => unreachable!(), - } -} - -/// Convert pll divisor into equivalent register field type -fn into_pre_div(div: u8) -> cfgr2::PREDIV_A { - match div { - 1 => cfgr2::PREDIV_A::DIV1, - 2 => cfgr2::PREDIV_A::DIV2, - 3 => cfgr2::PREDIV_A::DIV3, - 4 => cfgr2::PREDIV_A::DIV4, - 5 => cfgr2::PREDIV_A::DIV5, - 6 => cfgr2::PREDIV_A::DIV6, - 7 => cfgr2::PREDIV_A::DIV7, - 8 => cfgr2::PREDIV_A::DIV8, - 9 => cfgr2::PREDIV_A::DIV9, - 10 => cfgr2::PREDIV_A::DIV10, - 11 => cfgr2::PREDIV_A::DIV11, - 12 => cfgr2::PREDIV_A::DIV12, - 13 => cfgr2::PREDIV_A::DIV13, - 14 => cfgr2::PREDIV_A::DIV14, - 15 => cfgr2::PREDIV_A::DIV15, - 16 => cfgr2::PREDIV_A::DIV16, - _ => unreachable!(), - } -} - -impl CFGR { - /// Uses HSE (external oscillator) instead of HSI (internal RC oscillator) as the clock source. - /// Will result in a hang if an external oscillator is not connected or it fails to start. - pub fn use_hse(mut self, freq: F) -> Self - where - F: Into, - { - self.hse = Some(freq.into().0); - self - } - - /// Sets a frequency for the AHB bus - pub fn hclk(mut self, freq: F) -> Self - where - F: Into, - { - self.hclk = Some(freq.into().0); - self - } - - /// Sets a frequency for the APB1 bus - pub fn pclk1(mut self, freq: F) -> Self - where - F: Into, - { - self.pclk1 = Some(freq.into().0); - self - } - - /// Sets a frequency for the APB2 bus - pub fn pclk2(mut self, freq: F) -> Self - where - F: Into, - { - self.pclk2 = Some(freq.into().0); - self - } - - /// Sets the system (core) frequency - pub fn sysclk(mut self, freq: F) -> Self - where - F: Into, - { - self.sysclk = Some(freq.into().0); - self - } - - /// Calculate the values for the pll multiplier (PLLMUL) and the pll divisior (PLLDIV). - /// - /// These values are chosen depending on the chosen system clock (SYSCLK) and the frequency of the - /// oscillator clock (HSE / HSI). - /// - /// For these devices, PLL_SRC can selected between the internal oscillator (HSI) and - /// the external oscillator (HSE). - /// - /// HSI is divided by 2 before its transferred to PLL_SRC. - /// HSE can be divided between 2..16, before it is transferred to PLL_SRC. - /// After this system clock frequency (SYSCLK) can be changed via multiplier. - /// The value can be multiplied with 2..16. - /// - /// To determine the optimal values, if HSE is chosen as PLL_SRC, the greatest common divisor - /// is calculated and the limitations of the possible values are taken into consideration. - /// - /// HSI is simpler to calculate, but the possible system clocks are less than HSE, because the - /// division is not configurable. - #[cfg(not(any( - feature = "stm32f302xd", - feature = "stm32f302xe", - feature = "stm32f303xd", - feature = "stm32f303xe", - feature = "stm32f398" - )))] - fn calc_pll(&self, sysclk: u32) -> (u32, PllConfig) { - let pllsrcclk = self.hse.unwrap_or(HSI / 2); - // Get the optimal value for the pll divisor (PLL_DIV) and multiplier (PLL_MUL) - // Only for HSE PLL_DIV can be changed - let (pll_mul, pll_div): (u32, Option) = if self.hse.is_some() { - // Get the optimal value for the pll divisor (PLL_DIV) and multiplier (PLL_MUL) - // with the greatest common divisor calculation. - let common_divisor = gcd(sysclk, pllsrcclk); - let mut multiplier = sysclk / common_divisor; - let mut divisor = pllsrcclk / common_divisor; - - // Check if the multiplier can be represented by PLL_MUL - // or if the divisor can be represented by PRE_DIV - if multiplier == 1 || divisor == 1 { - // PLL_MUL minimal value is 2 - multiplier *= 2; - // PRE_DIV minimal value is 2 - divisor *= 2; - } - - // PLL_MUL maximal value is 16 - assert!(divisor <= 16); - // PRE_DIV maximal value is 16 - assert!(multiplier <= 16); - - (multiplier, Some(divisor)) - } - // HSI division is always divided by 2 and has no adjustable division - else { - let pll_mul = sysclk / pllsrcclk; - assert!(pll_mul <= 16); - (pll_mul, None) - }; - - let sysclk = (pllsrcclk / pll_div.unwrap_or(1)) * pll_mul; - assert!(sysclk <= 72_000_000); - - let pll_src = if self.hse.is_some() { - cfgr::PLLSRC_A::HSE_DIV_PREDIV - } else { - cfgr::PLLSRC_A::HSI_DIV2 - }; - - // Convert into register bit field types - let pll_mul_bits = into_pll_mul(pll_mul as u8); - let pll_div_bits = pll_div.map(|pll_div| into_pre_div(pll_div as u8)); - - ( - sysclk, - PllConfig { - src: pll_src, - mul: pll_mul_bits, - div: pll_div_bits, - }, - ) - } - - /// Calculate the values for the pll multiplier (PLLMUL) and the pll divisor (PLLDIV). - /// - /// These values are chosen depending on the chosen system clock (SYSCLK) and the frequency of the oscillator - /// clk (HSI / HSE). - /// - /// For these devices, PLL_SRC can be set to choose between the internal oscillator (HSI) and - /// the external oscillator (HSE). - /// After this the system clock frequency (SYSCLK) can be changed via a division and a - /// multiplication block. - /// It can be divided from with values 1..16 and multiplied from 2..16. - /// - /// To determine the optimal values, the greatest common divisor is calculated and the - /// limitations of the possible values are taken into considiration. - #[cfg(any( - feature = "stm32f302xd", - feature = "stm32f302xe", - feature = "stm32f303xd", - feature = "stm32f303xe", - feature = "stm32f398", - ))] - fn calc_pll(&self, sysclk: u32) -> (u32, PllConfig) { - let pllsrcclk = self.hse.unwrap_or(HSI); - - let (pll_mul, pll_div) = { - // Get the optimal value for the pll divisor (PLL_DIV) and multiplcator (PLL_MUL) - // with the greatest common divisor calculation. - let common_divisor = gcd(sysclk, pllsrcclk); - let mut multiplier = sysclk / common_divisor; - let mut divisor = pllsrcclk / common_divisor; - - // Check if the multiplier can be represented by PLL_MUL - if multiplier == 1 { - // PLL_MUL minimal value is 2 - multiplier *= 2; - divisor *= 2; - } - - // PLL_MUL maximal value is 16 - assert!(multiplier <= 16); - - // PRE_DIV maximal value is 16 - assert!(divisor <= 16); - - (multiplier, divisor) - }; - - let sysclk = (pllsrcclk / pll_div) * pll_mul; - assert!(sysclk <= 72_000_000); - - // Select hardware clock source of the PLL - // TODO Check whether HSI_DIV2 could be useful - let pll_src = if self.hse.is_some() { - cfgr::PLLSRC_A::HSE_DIV_PREDIV - } else { - cfgr::PLLSRC_A::HSI_DIV_PREDIV - }; - - // Convert into register bit field types - let pll_mul_bits = into_pll_mul(pll_mul as u8); - let pll_div_bits = into_pre_div(pll_div as u8); - - ( - sysclk, - PllConfig { - src: pll_src, - mul: pll_mul_bits, - div: Some(pll_div_bits), - }, - ) - } - - /// Get the system clock, the system clock source and the pll_options, if needed. - /// - /// The system clock source is determined by the chosen system clock and the provided hardware - /// clock. - /// This function does only chose the PLL if needed, otherwise it will use the oscillator clock as system clock. - fn get_sysclk(&self) -> (u32, cfgr::SW_A, Option) { - // If a sysclk is given, check if the PLL has to be used, - // else select the system clock source, which is either HSI or HSE. - match (self.sysclk, self.hse) { - // No need to use the PLL - // PLL is needed for USB, but we can make this assumption, to not use PLL here, - // because the two valid USB clocks, 72 Mhz and 48 Mhz, can't be generated - // directly from neither the internal rc (8 Mhz) nor the external - // Oscillator (max 32 Mhz), without using the PLL. - (Some(sysclk), Some(hse)) if sysclk == hse => (hse, cfgr::SW_A::HSE, None), - // No need to use the PLL - (Some(sysclk), None) if sysclk == HSI => (HSI, cfgr::SW_A::HSI, None), - (Some(sysclk), _) => { - let (sysclk, pll_config) = self.calc_pll(sysclk); - (sysclk, cfgr::SW_A::PLL, Some(pll_config)) - } - // Use HSE as system clock - (None, Some(hse)) => (hse, cfgr::SW_A::HSE, None), - // Use HSI as system clock - (None, None) => (HSI, cfgr::SW_A::HSI, None), - } - } - - /// Freezes the clock configuration, making it effective - pub fn freeze(self, acr: &mut ACR) -> Clocks { - let (sysclk, sysclk_source, pll_config) = self.get_sysclk(); - - let (hpre_bits, hpre) = self - .hclk - .map(|hclk| match sysclk / hclk { - 0 => unreachable!(), - 1 => (cfgr::HPRE_A::DIV1, 1), - 2 => (cfgr::HPRE_A::DIV2, 2), - 3..=5 => (cfgr::HPRE_A::DIV4, 4), - 6..=11 => (cfgr::HPRE_A::DIV8, 8), - 12..=39 => (cfgr::HPRE_A::DIV16, 16), - 40..=95 => (cfgr::HPRE_A::DIV64, 64), - 96..=191 => (cfgr::HPRE_A::DIV128, 128), - 192..=383 => (cfgr::HPRE_A::DIV256, 256), - _ => (cfgr::HPRE_A::DIV512, 512), - }) - .unwrap_or((cfgr::HPRE_A::DIV1, 1)); - - let hclk: u32 = sysclk / hpre; - - assert!(hclk <= 72_000_000); - - let (ppre1_bits, ppre1) = self - .pclk1 - .map(|pclk1| match hclk / pclk1 { - 0 => unreachable!(), - 1 => (cfgr::PPRE1_A::DIV1, 1), - 2 => (cfgr::PPRE1_A::DIV2, 2), - 3..=5 => (cfgr::PPRE1_A::DIV4, 4), - 6..=11 => (cfgr::PPRE1_A::DIV8, 8), - _ => (cfgr::PPRE1_A::DIV16, 16), - }) - .unwrap_or((cfgr::PPRE1_A::DIV1, 1)); - - let pclk1 = hclk / u32::from(ppre1); - - assert!(pclk1 <= 36_000_000); - - let (ppre2_bits, ppre2) = self - .pclk2 - .map(|pclk2| match hclk / pclk2 { - 0 => unreachable!(), - 1 => (cfgr::PPRE2_A::DIV1, 1), - 2 => (cfgr::PPRE2_A::DIV2, 2), - 3..=5 => (cfgr::PPRE2_A::DIV4, 4), - 6..=11 => (cfgr::PPRE2_A::DIV8, 8), - _ => (cfgr::PPRE2_A::DIV16, 16), - }) - .unwrap_or((cfgr::PPRE2_A::DIV1, 1)); - - let pclk2 = hclk / u32::from(ppre2); - - assert!(pclk2 <= 72_000_000); - - // Adjust flash wait states according to the - // HCLK frequency (cpu core clock) - acr.acr().modify(|_, w| { - if hclk <= 24_000_000 { - w.latency().ws0() - } else if hclk <= 48_000_000 { - w.latency().ws1() - } else { - w.latency().ws2() - } - }); - - let (usbpre, usbclk_valid) = usb_clocking::is_valid(sysclk, self.hse, pclk1, &pll_config); - - let rcc = unsafe { &*RCC::ptr() }; - - if self.hse.is_some() { - // enable HSE and wait for it to be ready - rcc.cr.modify(|_, w| w.hseon().on()); - - while rcc.cr.read().hserdy().is_not_ready() {} - } - - // enable PLL and wait for it to be ready - if let Some(pll_config) = pll_config { - rcc.cfgr.modify(|_, w| { - w.pllmul() - .variant(pll_config.mul) - .pllsrc() - .variant(pll_config.src) - }); - - if let Some(pll_div) = pll_config.div { - rcc.cfgr2.modify(|_, w| w.prediv().variant(pll_div)); - }; - - rcc.cr.modify(|_, w| w.pllon().on()); - - while rcc.cr.read().pllrdy().is_not_ready() {} - }; - - // set prescalers and clock source - rcc.cfgr.modify(|_, w| { - usb_clocking::set_usbpre(w, usbpre); - - w.ppre2() - .variant(ppre2_bits) - .ppre1() - .variant(ppre1_bits) - .hpre() - .variant(hpre_bits) - .sw() - .variant(sysclk_source) - }); - - Clocks { - hclk: Hertz(hclk), - pclk1: Hertz(pclk1), - pclk2: Hertz(pclk2), - ppre1, - ppre2, - sysclk: Hertz(sysclk), - usbclk_valid, - } - } -} - -/// Frozen clock frequencies -/// -/// The existence of this value indicates that the clock configuration can no longer be changed -#[derive(Clone, Copy)] -pub struct Clocks { - hclk: Hertz, - pclk1: Hertz, - pclk2: Hertz, - ppre1: u8, - ppre2: u8, - sysclk: Hertz, - usbclk_valid: bool, -} - -impl Clocks { - /// Returns the frequency of the AHB - pub fn hclk(&self) -> Hertz { - self.hclk - } - - /// Returns the frequency of the APB1 - pub fn pclk1(&self) -> Hertz { - self.pclk1 - } - - /// Returns the frequency of the APB2 - pub fn pclk2(&self) -> Hertz { - self.pclk2 - } - - pub(crate) fn ppre1(&self) -> u8 { - self.ppre1 - } - - // TODO remove `allow` - #[allow(dead_code)] - pub(crate) fn ppre2(&self) -> u8 { - self.ppre2 - } - - /// Returns the system (core) frequency - pub fn sysclk(&self) -> Hertz { - self.sysclk - } - - /// Returns whether the USBCLK clock frequency is valid for the USB peripheral - pub fn usbclk_valid(&self) -> bool { - self.usbclk_valid - } -} From 6a6130aa0de1c537630383ac1c968fcc47abc85b Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 20:37:48 -0400 Subject: [PATCH 12/31] Cleaned up RTC module a bit --- Cargo.toml | 2 +- examples/dac.rs | 6 +- src/dac.rs | 63 +++-- src/rcc.rs | 647 ++++++++++++++++++++++++++++++++++++++++++++++++ src/rtc.rs | 432 ++++++++++++++++++++++++++++++++ 5 files changed, 1120 insertions(+), 30 deletions(-) create mode 100644 src/rcc.rs create mode 100644 src/rtc.rs diff --git a/Cargo.toml b/Cargo.toml index 9c4b5b818..5ebcf4749 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ cortex-m-rt = "0.6" embedded-hal = "0.2" nb = "0.1" stm32f3 = "0.11" -rtcc = "0.2.0" +rtcc = "0.2" [dependencies.bare-metal] version = "0.2" diff --git a/examples/dac.rs b/examples/dac.rs index ae54ffa9c..a9d4117c9 100644 --- a/examples/dac.rs +++ b/examples/dac.rs @@ -34,12 +34,12 @@ fn main() -> ! { let mut _dac_pin = gpioa.pa4.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); let mut dac = Dac::new(dp.DAC, DacId::One, DacBits::EightR, 3.3); - dac.enable(&mut rcc.apb1); + dac.try_enable(&mut rcc.apb1).ok(); - dac.set_voltage(1.5); + dac.try_set_voltage(1.5).ok(); // dac.set_value(128); // or equivalently loop { delay.delay_ms(1_000_u16); } -} \ No newline at end of file +} diff --git a/src/dac.rs b/src/dac.rs index 4859d9e73..2fe0e18a2 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -22,18 +22,19 @@ use crate::{pac, rcc::APB1}; pub trait SingleChannelDac { /// Error type returned by DAC methods type Error; + type Word; /// Enable the DAC. - fn enable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; // todo: generalize periph clock + fn try_enable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; // todo: generalize periph clock /// Disable the DAC. - fn disable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; + fn try_disable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; /// Output a constant signal, given a bit word. - fn set_value(&mut self, value: u32) -> Result<(), Self::Error>; + fn try_set_value(&mut self, value: Self::Word) -> Result<(), Self::Error>; - /// Set a constant signal, given a voltage. - fn set_voltage(&mut self, volts: f32) -> Result<(), Self::Error>; + /// Output a constant signal, given a voltage + fn try_set_voltage(&mut self, volts: f32) -> Result<(), Self::Error>; } #[derive(Clone, Copy, Debug)] @@ -56,7 +57,7 @@ pub enum Trigger { Tim7, Tim15, Tim2, - Tim4, // Not available on DAC 2. + Tim4, // Not available on DAC 2. Exti9, Swtrig, } @@ -99,48 +100,57 @@ impl Dac { match self.id { DacId::One => { self.regs.cr.modify(|_, w| w.ten1().set_bit()); - self.regs.cr.modify(|_, w| unsafe { w.tsel1().bits(trigger.bits())}); + self.regs + .cr + .modify(|_, w| unsafe { w.tsel1().bits(trigger.bits()) }); } DacId::Two => { self.regs.cr.modify(|_, w| w.ten2().set_bit()); - self.regs.cr.modify(|_, w| unsafe { w.tsel2().bits(trigger.bits())}); + self.regs + .cr + .modify(|_, w| unsafe { w.tsel2().bits(trigger.bits()) }); } } } /// Independent trigger with single LFSR generation /// See f303 Reference Manual section 16.5.2 - pub fn trigger_lfsr(&mut self, trigger: Trigger, data: u32) { + pub fn trigger_lfsr(&mut self, trigger: Trigger, data: u32) -> Result<(), DacError> { // todo: This may not be correct. match self.id { DacId::One => { - self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01)}); - self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b01)}); + self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01) }); + self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b01) }); } DacId::Two => { - self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01)}); - self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b01)}); + self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01) }); + self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b01) }); } } self.set_trigger(trigger); - self.set_value(data); + self.try_set_value(data)?; + + Ok(()) } - + /// Independent trigger with single triangle generation - pub fn trigger_triangle(&mut self, trigger: Trigger, data: u32) { + /// See f303 Reference Manual section 16.5.2 + pub fn trigger_triangle(&mut self, trigger: Trigger, data: u32) -> Result<(), DacError> { // todo: This may not be correct. match self.id { DacId::One => { - self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b10)}); - self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b10)}); + self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b10) }); + self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b10) }); } DacId::Two => { - self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b10)}); - self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b10)}); + self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b10) }); + self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b10) }); } } self.set_trigger(trigger); - self.set_value(data); + self.try_set_value(data)?; + + Ok(()) } } @@ -148,9 +158,10 @@ pub struct DacError {} impl SingleChannelDac for Dac { type Error = DacError; + type Word = u32; /// Enable the DAC. - fn enable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { + fn try_enable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { match self.id { DacId::One => { apb1.enr().modify(|_, w| w.dac1en().set_bit()); @@ -165,7 +176,7 @@ impl SingleChannelDac for Dac { Ok(()) } - fn disable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { + fn try_disable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { match self.id { DacId::One => { self.regs.cr.modify(|_, w| w.en1().clear_bit()); @@ -181,7 +192,7 @@ impl SingleChannelDac for Dac { } /// Set the DAC value as an integer. - fn set_value(&mut self, val: u32) -> Result<(), DacError> { + fn try_set_value(&mut self, val: u32) -> Result<(), DacError> { match self.id { DacId::One => match self.bits { DacBits::EightR => self.regs.dhr8r1.modify(|_, w| unsafe { w.bits(val) }), @@ -199,14 +210,14 @@ impl SingleChannelDac for Dac { } /// Set the DAC voltage. `v` is in Volts. - fn set_voltage(&mut self, volts: f32) -> Result<(), DacError> { + fn try_set_voltage(&mut self, volts: f32) -> Result<(), DacError> { let val = match self.bits { DacBits::EightR => ((volts / self.vref) * 255.) as u32, DacBits::TwelveL => ((volts / self.vref) * 4_095.) as u32, DacBits::TwelveR => ((volts / self.vref) * 4_095.) as u32, }; - self.set_value(val); + self.try_set_value(val)?; Ok(()) } diff --git a/src/rcc.rs b/src/rcc.rs new file mode 100644 index 000000000..8d3c1780f --- /dev/null +++ b/src/rcc.rs @@ -0,0 +1,647 @@ +//! Reset and Clock Control + +use crate::pac::{ + rcc::{self, cfgr, cfgr2}, + RCC, +}; + +use crate::flash::ACR; +use crate::time::Hertz; + +/// Extension trait that constrains the `RCC` peripheral +pub trait RccExt { + /// Constrains the `RCC` peripheral so it plays nicely with the other abstractions + fn constrain(self) -> Rcc; +} + +impl RccExt for RCC { + fn constrain(self) -> Rcc { + Rcc { + ahb: AHB { _0: () }, + apb1: APB1 { _0: () }, + apb2: APB2 { _0: () }, + bdcr: BDCR { _0: () }, + cfgr: CFGR { + hse: None, + hclk: None, + pclk1: None, + pclk2: None, + sysclk: None, + }, + } + } +} + +/// Constrained RCC peripheral +pub struct Rcc { + /// AMBA High-performance Bus (AHB) registers + pub ahb: AHB, + /// Advanced Peripheral Bus 1 (APB1) registers + pub apb1: APB1, + /// Advanced Peripheral Bus 2 (APB2) registers + pub apb2: APB2, + /// RCC Backup Domain + pub bdcr: BDCR, + /// Clock configuration + pub cfgr: CFGR, +} + +/// AMBA High-performance Bus (AHB) registers +pub struct AHB { + _0: (), +} + +impl AHB { + pub(crate) fn enr(&mut self) -> &rcc::AHBENR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).ahbenr } + } + + pub(crate) fn rstr(&mut self) -> &rcc::AHBRSTR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).ahbrstr } + } +} + +/// Advanced Peripheral Bus 1 (APB1) registers +pub struct APB1 { + _0: (), +} + +impl APB1 { + pub(crate) fn enr(&mut self) -> &rcc::APB1ENR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).apb1enr } + } + + pub(crate) fn rstr(&mut self) -> &rcc::APB1RSTR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).apb1rstr } + } +} + +/// Advanced Peripheral Bus 2 (APB2) registers +pub struct APB2 { + _0: (), +} + +impl APB2 { + pub(crate) fn enr(&mut self) -> &rcc::APB2ENR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).apb2enr } + } + + pub(crate) fn rstr(&mut self) -> &rcc::APB2RSTR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).apb2rstr } + } +} + +const HSI: u32 = 8_000_000; // Hz + +// some microcontrollers do not have USB +#[cfg(any(feature = "stm32f301", feature = "stm32f318", feature = "stm32f334",))] +mod usb_clocking { + use crate::rcc::PllConfig; + + pub(crate) fn is_valid( + _sysclk: u32, + _hse: Option, + _pclk1: u32, + _pll_config: &Option, + ) -> (bool, bool) { + (false, false) + } + + pub(crate) fn set_usbpre(w: &mut W, _: bool) -> &mut W { + w + } +} + +#[cfg(any( + feature = "stm32f302", + feature = "stm32f303", + feature = "stm32f373", + feature = "stm32f378", + feature = "stm32f328", + feature = "stm32f358", + feature = "stm32f398", +))] +mod usb_clocking { + use crate::pac::rcc::cfgr; + use crate::rcc::PllConfig; + + /// Check for all clock options to be + pub(crate) fn is_valid( + sysclk: u32, + hse: Option, + pclk1: u32, + pll_config: &Option, + ) -> (cfgr::USBPRE_A, bool) { + // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the + // PLL output frequency is a supported one. + // usbpre == false: divide clock by 1.5, otherwise no division + let usb_ok = hse.is_some() && pll_config.is_some(); + // The APB1 clock must have a minimum frequency of 10 MHz to avoid data overrun/underrun + // problems. [RM0316 32.5.2] + if pclk1 >= 10_000_000 { + match (usb_ok, sysclk) { + (true, 72_000_000) => (cfgr::USBPRE_A::DIV1_5, true), + (true, 48_000_000) => (cfgr::USBPRE_A::DIV1, true), + _ => (cfgr::USBPRE_A::DIV1, false), + } + } else { + (cfgr::USBPRE_A::DIV1, false) + } + } + + pub(crate) fn set_usbpre(w: &mut cfgr::W, usb_prescale: cfgr::USBPRE_A) -> &mut cfgr::W { + w.usbpre().variant(usb_prescale) + } +} + +/// Backup Domain Control register (RCC_BDCR) +pub struct BDCR { + _0: (), +} + +impl BDCR { + pub(crate) fn bdcr(&mut self) -> &rcc::BDCR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).bdcr } + } +} + +/// Clock configuration +pub struct CFGR { + hse: Option, + hclk: Option, + pclk1: Option, + pclk2: Option, + sysclk: Option, +} + +pub(crate) struct PllConfig { + src: cfgr::PLLSRC_A, + mul: cfgr::PLLMUL_A, + div: Option, +} + +/// Determine the [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) +/// +/// This function is based on the [Euclidean algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm). +fn gcd(mut a: u32, mut b: u32) -> u32 { + while b != 0 { + let r = a % b; + a = b; + b = r; + } + a +} + +/// Convert pll multiplier into equivalent register field type +fn into_pll_mul(mul: u8) -> cfgr::PLLMUL_A { + match mul { + 2 => cfgr::PLLMUL_A::MUL2, + 3 => cfgr::PLLMUL_A::MUL3, + 4 => cfgr::PLLMUL_A::MUL4, + 5 => cfgr::PLLMUL_A::MUL5, + 6 => cfgr::PLLMUL_A::MUL6, + 7 => cfgr::PLLMUL_A::MUL7, + 8 => cfgr::PLLMUL_A::MUL8, + 9 => cfgr::PLLMUL_A::MUL9, + 10 => cfgr::PLLMUL_A::MUL10, + 11 => cfgr::PLLMUL_A::MUL11, + 12 => cfgr::PLLMUL_A::MUL12, + 13 => cfgr::PLLMUL_A::MUL13, + 14 => cfgr::PLLMUL_A::MUL14, + 15 => cfgr::PLLMUL_A::MUL15, + 16 => cfgr::PLLMUL_A::MUL16, + _ => unreachable!(), + } +} + +/// Convert pll divisor into equivalent register field type +fn into_pre_div(div: u8) -> cfgr2::PREDIV_A { + match div { + 1 => cfgr2::PREDIV_A::DIV1, + 2 => cfgr2::PREDIV_A::DIV2, + 3 => cfgr2::PREDIV_A::DIV3, + 4 => cfgr2::PREDIV_A::DIV4, + 5 => cfgr2::PREDIV_A::DIV5, + 6 => cfgr2::PREDIV_A::DIV6, + 7 => cfgr2::PREDIV_A::DIV7, + 8 => cfgr2::PREDIV_A::DIV8, + 9 => cfgr2::PREDIV_A::DIV9, + 10 => cfgr2::PREDIV_A::DIV10, + 11 => cfgr2::PREDIV_A::DIV11, + 12 => cfgr2::PREDIV_A::DIV12, + 13 => cfgr2::PREDIV_A::DIV13, + 14 => cfgr2::PREDIV_A::DIV14, + 15 => cfgr2::PREDIV_A::DIV15, + 16 => cfgr2::PREDIV_A::DIV16, + _ => unreachable!(), + } +} + +impl CFGR { + /// Uses HSE (external oscillator) instead of HSI (internal RC oscillator) as the clock source. + /// Will result in a hang if an external oscillator is not connected or it fails to start. + pub fn use_hse(mut self, freq: F) -> Self + where + F: Into, + { + self.hse = Some(freq.into().0); + self + } + + /// Sets a frequency for the AHB bus + pub fn hclk(mut self, freq: F) -> Self + where + F: Into, + { + self.hclk = Some(freq.into().0); + self + } + + /// Sets a frequency for the APB1 bus + pub fn pclk1(mut self, freq: F) -> Self + where + F: Into, + { + self.pclk1 = Some(freq.into().0); + self + } + + /// Sets a frequency for the APB2 bus + pub fn pclk2(mut self, freq: F) -> Self + where + F: Into, + { + self.pclk2 = Some(freq.into().0); + self + } + + /// Sets the system (core) frequency + pub fn sysclk(mut self, freq: F) -> Self + where + F: Into, + { + self.sysclk = Some(freq.into().0); + self + } + + /// Calculate the values for the pll multiplier (PLLMUL) and the pll divisior (PLLDIV). + /// + /// These values are chosen depending on the chosen system clock (SYSCLK) and the frequency of the + /// oscillator clock (HSE / HSI). + /// + /// For these devices, PLL_SRC can selected between the internal oscillator (HSI) and + /// the external oscillator (HSE). + /// + /// HSI is divided by 2 before its transferred to PLL_SRC. + /// HSE can be divided between 2..16, before it is transferred to PLL_SRC. + /// After this system clock frequency (SYSCLK) can be changed via multiplier. + /// The value can be multiplied with 2..16. + /// + /// To determine the optimal values, if HSE is chosen as PLL_SRC, the greatest common divisor + /// is calculated and the limitations of the possible values are taken into consideration. + /// + /// HSI is simpler to calculate, but the possible system clocks are less than HSE, because the + /// division is not configurable. + #[cfg(not(any( + feature = "stm32f302xd", + feature = "stm32f302xe", + feature = "stm32f303xd", + feature = "stm32f303xe", + feature = "stm32f398" + )))] + fn calc_pll(&self, sysclk: u32) -> (u32, PllConfig) { + let pllsrcclk = self.hse.unwrap_or(HSI / 2); + // Get the optimal value for the pll divisor (PLL_DIV) and multiplier (PLL_MUL) + // Only for HSE PLL_DIV can be changed + let (pll_mul, pll_div): (u32, Option) = if self.hse.is_some() { + // Get the optimal value for the pll divisor (PLL_DIV) and multiplier (PLL_MUL) + // with the greatest common divisor calculation. + let common_divisor = gcd(sysclk, pllsrcclk); + let mut multiplier = sysclk / common_divisor; + let mut divisor = pllsrcclk / common_divisor; + + // Check if the multiplier can be represented by PLL_MUL + // or if the divisor can be represented by PRE_DIV + if multiplier == 1 || divisor == 1 { + // PLL_MUL minimal value is 2 + multiplier *= 2; + // PRE_DIV minimal value is 2 + divisor *= 2; + } + + // PLL_MUL maximal value is 16 + assert!(divisor <= 16); + // PRE_DIV maximal value is 16 + assert!(multiplier <= 16); + + (multiplier, Some(divisor)) + } + // HSI division is always divided by 2 and has no adjustable division + else { + let pll_mul = sysclk / pllsrcclk; + assert!(pll_mul <= 16); + (pll_mul, None) + }; + + let sysclk = (pllsrcclk / pll_div.unwrap_or(1)) * pll_mul; + assert!(sysclk <= 72_000_000); + + let pll_src = if self.hse.is_some() { + cfgr::PLLSRC_A::HSE_DIV_PREDIV + } else { + cfgr::PLLSRC_A::HSI_DIV2 + }; + + // Convert into register bit field types + let pll_mul_bits = into_pll_mul(pll_mul as u8); + let pll_div_bits = pll_div.map(|pll_div| into_pre_div(pll_div as u8)); + + ( + sysclk, + PllConfig { + src: pll_src, + mul: pll_mul_bits, + div: pll_div_bits, + }, + ) + } + + /// Calculate the values for the pll multiplier (PLLMUL) and the pll divisor (PLLDIV). + /// + /// These values are chosen depending on the chosen system clock (SYSCLK) and the frequency of the oscillator + /// clk (HSI / HSE). + /// + /// For these devices, PLL_SRC can be set to choose between the internal oscillator (HSI) and + /// the external oscillator (HSE). + /// After this the system clock frequency (SYSCLK) can be changed via a division and a + /// multiplication block. + /// It can be divided from with values 1..16 and multiplied from 2..16. + /// + /// To determine the optimal values, the greatest common divisor is calculated and the + /// limitations of the possible values are taken into considiration. + #[cfg(any( + feature = "stm32f302xd", + feature = "stm32f302xe", + feature = "stm32f303xd", + feature = "stm32f303xe", + feature = "stm32f398", + ))] + fn calc_pll(&self, sysclk: u32) -> (u32, PllConfig) { + let pllsrcclk = self.hse.unwrap_or(HSI); + + let (pll_mul, pll_div) = { + // Get the optimal value for the pll divisor (PLL_DIV) and multiplcator (PLL_MUL) + // with the greatest common divisor calculation. + let common_divisor = gcd(sysclk, pllsrcclk); + let mut multiplier = sysclk / common_divisor; + let mut divisor = pllsrcclk / common_divisor; + + // Check if the multiplier can be represented by PLL_MUL + if multiplier == 1 { + // PLL_MUL minimal value is 2 + multiplier *= 2; + divisor *= 2; + } + + // PLL_MUL maximal value is 16 + assert!(multiplier <= 16); + + // PRE_DIV maximal value is 16 + assert!(divisor <= 16); + + (multiplier, divisor) + }; + + let sysclk = (pllsrcclk / pll_div) * pll_mul; + assert!(sysclk <= 72_000_000); + + // Select hardware clock source of the PLL + // TODO Check whether HSI_DIV2 could be useful + let pll_src = if self.hse.is_some() { + cfgr::PLLSRC_A::HSE_DIV_PREDIV + } else { + cfgr::PLLSRC_A::HSI_DIV_PREDIV + }; + + // Convert into register bit field types + let pll_mul_bits = into_pll_mul(pll_mul as u8); + let pll_div_bits = into_pre_div(pll_div as u8); + + ( + sysclk, + PllConfig { + src: pll_src, + mul: pll_mul_bits, + div: Some(pll_div_bits), + }, + ) + } + + /// Get the system clock, the system clock source and the pll_options, if needed. + /// + /// The system clock source is determined by the chosen system clock and the provided hardware + /// clock. + /// This function does only chose the PLL if needed, otherwise it will use the oscillator clock as system clock. + fn get_sysclk(&self) -> (u32, cfgr::SW_A, Option) { + // If a sysclk is given, check if the PLL has to be used, + // else select the system clock source, which is either HSI or HSE. + match (self.sysclk, self.hse) { + // No need to use the PLL + // PLL is needed for USB, but we can make this assumption, to not use PLL here, + // because the two valid USB clocks, 72 Mhz and 48 Mhz, can't be generated + // directly from neither the internal rc (8 Mhz) nor the external + // Oscillator (max 32 Mhz), without using the PLL. + (Some(sysclk), Some(hse)) if sysclk == hse => (hse, cfgr::SW_A::HSE, None), + // No need to use the PLL + (Some(sysclk), None) if sysclk == HSI => (HSI, cfgr::SW_A::HSI, None), + (Some(sysclk), _) => { + let (sysclk, pll_config) = self.calc_pll(sysclk); + (sysclk, cfgr::SW_A::PLL, Some(pll_config)) + } + // Use HSE as system clock + (None, Some(hse)) => (hse, cfgr::SW_A::HSE, None), + // Use HSI as system clock + (None, None) => (HSI, cfgr::SW_A::HSI, None), + } + } + + /// Freezes the clock configuration, making it effective + pub fn freeze(self, acr: &mut ACR) -> Clocks { + let (sysclk, sysclk_source, pll_config) = self.get_sysclk(); + + let (hpre_bits, hpre) = self + .hclk + .map(|hclk| match sysclk / hclk { + 0 => unreachable!(), + 1 => (cfgr::HPRE_A::DIV1, 1), + 2 => (cfgr::HPRE_A::DIV2, 2), + 3..=5 => (cfgr::HPRE_A::DIV4, 4), + 6..=11 => (cfgr::HPRE_A::DIV8, 8), + 12..=39 => (cfgr::HPRE_A::DIV16, 16), + 40..=95 => (cfgr::HPRE_A::DIV64, 64), + 96..=191 => (cfgr::HPRE_A::DIV128, 128), + 192..=383 => (cfgr::HPRE_A::DIV256, 256), + _ => (cfgr::HPRE_A::DIV512, 512), + }) + .unwrap_or((cfgr::HPRE_A::DIV1, 1)); + + let hclk: u32 = sysclk / hpre; + + assert!(hclk <= 72_000_000); + + let (ppre1_bits, ppre1) = self + .pclk1 + .map(|pclk1| match hclk / pclk1 { + 0 => unreachable!(), + 1 => (cfgr::PPRE1_A::DIV1, 1), + 2 => (cfgr::PPRE1_A::DIV2, 2), + 3..=5 => (cfgr::PPRE1_A::DIV4, 4), + 6..=11 => (cfgr::PPRE1_A::DIV8, 8), + _ => (cfgr::PPRE1_A::DIV16, 16), + }) + .unwrap_or((cfgr::PPRE1_A::DIV1, 1)); + + let pclk1 = hclk / u32::from(ppre1); + + assert!(pclk1 <= 36_000_000); + + let (ppre2_bits, ppre2) = self + .pclk2 + .map(|pclk2| match hclk / pclk2 { + 0 => unreachable!(), + 1 => (cfgr::PPRE2_A::DIV1, 1), + 2 => (cfgr::PPRE2_A::DIV2, 2), + 3..=5 => (cfgr::PPRE2_A::DIV4, 4), + 6..=11 => (cfgr::PPRE2_A::DIV8, 8), + _ => (cfgr::PPRE2_A::DIV16, 16), + }) + .unwrap_or((cfgr::PPRE2_A::DIV1, 1)); + + let pclk2 = hclk / u32::from(ppre2); + + assert!(pclk2 <= 72_000_000); + + // Adjust flash wait states according to the + // HCLK frequency (cpu core clock) + acr.acr().modify(|_, w| { + if hclk <= 24_000_000 { + w.latency().ws0() + } else if hclk <= 48_000_000 { + w.latency().ws1() + } else { + w.latency().ws2() + } + }); + + let (usbpre, usbclk_valid) = usb_clocking::is_valid(sysclk, self.hse, pclk1, &pll_config); + + let rcc = unsafe { &*RCC::ptr() }; + + if self.hse.is_some() { + // enable HSE and wait for it to be ready + rcc.cr.modify(|_, w| w.hseon().on()); + + while rcc.cr.read().hserdy().is_not_ready() {} + } + + // enable PLL and wait for it to be ready + if let Some(pll_config) = pll_config { + rcc.cfgr.modify(|_, w| { + w.pllmul() + .variant(pll_config.mul) + .pllsrc() + .variant(pll_config.src) + }); + + if let Some(pll_div) = pll_config.div { + rcc.cfgr2.modify(|_, w| w.prediv().variant(pll_div)); + }; + + rcc.cr.modify(|_, w| w.pllon().on()); + + while rcc.cr.read().pllrdy().is_not_ready() {} + }; + + // set prescalers and clock source + rcc.cfgr.modify(|_, w| { + usb_clocking::set_usbpre(w, usbpre); + + w.ppre2() + .variant(ppre2_bits) + .ppre1() + .variant(ppre1_bits) + .hpre() + .variant(hpre_bits) + .sw() + .variant(sysclk_source) + }); + + Clocks { + hclk: Hertz(hclk), + pclk1: Hertz(pclk1), + pclk2: Hertz(pclk2), + ppre1, + ppre2, + sysclk: Hertz(sysclk), + usbclk_valid, + } + } +} + +/// Frozen clock frequencies +/// +/// The existence of this value indicates that the clock configuration can no longer be changed +#[derive(Clone, Copy)] +pub struct Clocks { + hclk: Hertz, + pclk1: Hertz, + pclk2: Hertz, + ppre1: u8, + ppre2: u8, + sysclk: Hertz, + usbclk_valid: bool, +} + +impl Clocks { + /// Returns the frequency of the AHB + pub fn hclk(&self) -> Hertz { + self.hclk + } + + /// Returns the frequency of the APB1 + pub fn pclk1(&self) -> Hertz { + self.pclk1 + } + + /// Returns the frequency of the APB2 + pub fn pclk2(&self) -> Hertz { + self.pclk2 + } + + pub(crate) fn ppre1(&self) -> u8 { + self.ppre1 + } + + // TODO remove `allow` + #[allow(dead_code)] + pub(crate) fn ppre2(&self) -> u8 { + self.ppre2 + } + + /// Returns the system (core) frequency + pub fn sysclk(&self) -> Hertz { + self.sysclk + } + + /// Returns whether the USBCLK clock frequency is valid for the USB peripheral + pub fn usbclk_valid(&self) -> bool { + self.usbclk_valid + } +} diff --git a/src/rtc.rs b/src/rtc.rs new file mode 100644 index 000000000..c8ed6840b --- /dev/null +++ b/src/rtc.rs @@ -0,0 +1,432 @@ +//! Interface to the real time clock. See STM32F303 reference manual, section 27. + +use crate::rcc::{APB1, BDCR}; +use crate::stm32::{PWR, RTC}; +use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike}; + +/// Invalid input error +#[derive(Debug)] +pub enum Error { + InvalidInputData, +} + +pub const LSE_BITS: u8 = 0b01; + +pub struct Rtc { + pub regs: RTC, + pub prediv_s: u16, + pub prediv_a: u8, +} + +impl Rtc { + /// Create and enable a new RTC, and configure its clock source and prescalers. + pub fn new(regs: RTC, prediv_s: u16, prediv_a: u8, apb1: &mut APB1, bdcr: &mut BDCR) -> Self { + // todo defaults: // prediv_s: 311, prediv_a: 127. From where? + let mut result = Self { + regs, + prediv_s, + prediv_a, + }; + + Self::unlock(apb1); + Self::enable(bdcr); + result.set_24h_fmt(); + + result.modify(|regs| { + regs.prer + .write(|w| unsafe { w.prediv_s().bits(prediv_s).prediv_a().bits(prediv_a) }); + }); + + result + } + + /// Enable the low frequency external oscillator. This is the only mode currently + /// supported, to avoid + fn enable_lse(bdcr: &mut BDCR, bypass: bool) { + bdcr.bdcr() + .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass)); + while bdcr.bdcr().read().lserdy().bit_is_clear() {} + } + + fn unlock(apb1: &mut APB1) { + let pwr = unsafe { &(*PWR::ptr()) }; + apb1.enr().modify(|_, w| { + w + // Enable the backup interface by setting PWREN + .pwren() + .set_bit() + }); + pwr.cr.modify(|_, w| { + w + // Enable access to the backup registers + .dbp() + .set_bit() + }); + + while pwr.cr.read().dbp().bit_is_clear() {} + } + + fn enable(bdcr: &mut BDCR) { + bdcr.bdcr().modify(|_, w| { + w + // RTC Backup Domain reset bit set high + .bdrst() + .set_bit() + }); + + bdcr.bdcr().modify(|_, w| { + w + // RTC clock source selection + .rtcsel() + .bits(LSE_BITS) + // Enable RTC + .rtcen() + .set_bit() + // RTC backup Domain reset bit set low + .bdrst() + .clear_bit() + }); + } + + /// Sets calendar clock to 24 hr format + pub fn set_24h_fmt(&mut self) { + self.regs.cr.modify(|_, w| w.fmt().set_bit()); + } + /// Sets calendar clock to 12 hr format + pub fn set_12h_fmt(&mut self) { + self.regs.cr.modify(|_, w| w.fmt().clear_bit()); + } + + /// Reads current hour format selection + pub fn is_24h_fmt(&self) -> bool { + self.regs.cr.read().fmt().bit() + } + + /// As described in Section 27.3.7 in RM0316, + /// this function is used to disable write protection + /// when modifying an RTC register + fn modify(&mut self, mut closure: F) + where + F: FnMut(&mut RTC) -> (), + { + // Disable write protection + self.regs.wpr.write(|w| unsafe { w.bits(0xCA) }); + self.regs.wpr.write(|w| unsafe { w.bits(0x53) }); + // Enter init mode + let isr = self.regs.isr.read(); + if isr.initf().bit_is_clear() { + self.regs.isr.modify(|_, w| w.init().set_bit()); + while self.regs.isr.read().initf().bit_is_clear() {} + } + // Invoke closure + closure(&mut self.regs); + // Exit init mode + self.regs.isr.modify(|_, w| w.init().clear_bit()); + // wait for last write to be done + while !self.regs.isr.read().initf().bit_is_clear() {} + } +} + +impl Rtcc for Rtc { + type Error = Error; + + /// set time using NaiveTime (ISO 8601 time without timezone) + /// Hour format is 24h + fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> { + self.set_24h_fmt(); + let (ht, hu) = bcd2_encode(time.hour()); + let (mnt, mnu) = bcd2_encode(time.minute()); + let (st, su) = bcd2_encode(time.second()); + Ok(self.modify(|regs| { + regs.tr.write(|w| unsafe { + w.ht() + .bits(ht) + .hu() + .bits(hu) + .mnt() + .bits(mnt) + .mnu() + .bits(mnu) + .st() + .bits(st) + .su() + .bits(su) + .pm() + .clear_bit() + }); + })) + } + + fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> { + if seconds > 59 { + return Err(Error::InvalidInputData); + } + let (st, su) = bcd2_encode(seconds as u32); + Ok(self.modify(|regs| regs.tr.write(|w| unsafe { w.st().bits(st).su().bits(su) }))) + } + + fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> { + if minutes > 59 { + return Err(Error::InvalidInputData); + } + let (mnt, mnu) = bcd2_encode(minutes as u32); + Ok(self.modify(|regs| { + regs.tr + .write(|w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) + })) + } + + fn set_hours(&mut self, hours: rtcc::Hours) -> Result<(), Self::Error> { + let (ht, hu) = hours_to_register(hours)?; + match hours { + Hours::H24(_h) => { + self.set_24h_fmt(); + Ok(self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) }))) + } + Hours::AM(_h) | Hours::PM(_h) => { + self.set_12h_fmt(); + Ok(self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) }))) + } + } + } + + fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> { + if (weekday < 1) | (weekday > 7) { + return Err(Error::InvalidInputData); + } + Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) }))) + } + + fn set_day(&mut self, day: u8) -> Result<(), Self::Error> { + if (day < 1) | (day > 31) { + return Err(Error::InvalidInputData); + } + let (dt, du) = bcd2_encode(day as u32); + Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.dt().bits(dt).du().bits(du) }))) + } + + fn set_month(&mut self, month: u8) -> Result<(), Self::Error> { + if (month < 1) | (month > 12) { + return Err(Error::InvalidInputData); + } + let (mt, mu) = bcd2_encode(month as u32); + Ok(self.modify(|regs| { + regs.dr + .write(|w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) + })) + } + + fn set_year(&mut self, year: u16) -> Result<(), Self::Error> { + if (year < 1970) | (year > 2038) { + return Err(Error::InvalidInputData); + } + let (yt, yu) = bcd2_encode(year as u32); + Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.yt().bits(yt).yu().bits(yu) }))) + } + + /// set date using NaiveDate (ISO 8601 calendar date without timezone) + /// WeekDay is set using set_weekday method + fn set_date(&mut self, date: &NaiveDate) -> Result<(), Self::Error> { + let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); + let (mt, mu) = bcd2_encode(date.month()); + let (dt, du) = bcd2_encode(date.day()); + + Ok(self.modify(|regs| { + regs.dr.write(|w| unsafe { + w.dt() + .bits(dt) + .du() + .bits(du) + .mt() + .bit(mt > 0) + .mu() + .bits(mu) + .yt() + .bits(yt) + .yu() + .bits(yu) + }); + })) + } + + fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> { + // Check if unsigned integer affects encoding to bcd + + self.set_24h_fmt(); + let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); + let (mt, mu) = bcd2_encode(date.month()); + let (dt, du) = bcd2_encode(date.day()); + + let (ht, hu) = bcd2_encode(date.hour()); + let (mnt, mnu) = bcd2_encode(date.minute()); + let (st, su) = bcd2_encode(date.second()); + + self.modify(|regs| { + regs.dr.write(|w| unsafe { + w.dt() + .bits(dt) + .du() + .bits(du) + .mt() + .bit(mt > 0) + .mu() + .bits(mu) + .yt() + .bits(yt) + .yu() + .bits(yu) + }); + }); + Ok(self.modify(|regs| { + regs.tr.write(|w| unsafe { + w.ht() + .bits(ht) + .hu() + .bits(hu) + .mnt() + .bits(mnt) + .mnu() + .bits(mnu) + .st() + .bits(st) + .su() + .bits(su) + .pm() + .clear_bit() + }); + })) + } + + fn get_seconds(&mut self) -> Result { + let tr = self.regs.tr.read(); + let seconds = bcd2_decode(tr.st().bits(), tr.su().bits()); + Ok(seconds as u8) + } + + fn get_minutes(&mut self) -> Result { + let tr = self.regs.tr.read(); + let minutes = bcd2_decode(tr.mnt().bits(), tr.mnu().bits()); + Ok(minutes as u8) + } + + fn get_hours(&mut self) -> Result { + let tr = self.regs.tr.read(); + let hours = bcd2_decode(tr.ht().bits(), tr.hu().bits()); + if self.is_24h_fmt() { + return Ok(rtcc::Hours::H24(hours as u8)); + } + if !tr.pm().bit() { + return Ok(rtcc::Hours::AM(hours as u8)); + } + Ok(rtcc::Hours::PM(hours as u8)) + } + + fn get_time(&mut self) -> Result { + self.set_24h_fmt(); + let seconds = self.get_seconds().unwrap(); + let minutes = self.get_minutes().unwrap(); + let hours = hours_to_u8(self.get_hours().unwrap()); + + Ok(NaiveTime::from_hms( + hours.into(), + minutes.into(), + seconds.into(), + )) + } + + fn get_weekday(&mut self) -> Result { + let dr = self.regs.dr.read(); + let weekday = bcd2_decode(dr.wdu().bits(), 0x00); + Ok(weekday as u8) + } + + fn get_day(&mut self) -> Result { + let dr = self.regs.dr.read(); + let day = bcd2_decode(dr.dt().bits(), dr.du().bits()); + Ok(day as u8) + } + + fn get_month(&mut self) -> Result { + let dr = self.regs.dr.read(); + let mt: u8 = if dr.mt().bit() { 1 } else { 0 }; + let month = bcd2_decode(mt, dr.mu().bits()); + Ok(month as u8) + } + + fn get_year(&mut self) -> Result { + let dr = self.regs.dr.read(); + let year = bcd2_decode(dr.yt().bits(), dr.yu().bits()); + Ok(year as u16) + } + + fn get_date(&mut self) -> Result { + let day = self.get_day().unwrap(); + let month = self.get_month().unwrap(); + let year = self.get_year().unwrap(); + + Ok(NaiveDate::from_ymd(year.into(), month.into(), day.into())) + } + + fn get_datetime(&mut self) -> Result { + self.set_24h_fmt(); + + let day = self.get_day().unwrap(); + let month = self.get_month().unwrap(); + let year = self.get_year().unwrap(); + + let seconds = self.get_seconds().unwrap(); + let minutes = self.get_minutes().unwrap(); + let hours = hours_to_u8(self.get_hours().unwrap()); + + Ok( + NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms( + hours.into(), + minutes.into(), + seconds.into(), + ), + ) + } +} + +// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours (12- or 24-hour format), day (day +// of week), date (day of month), month, and year, expressed in binary coded decimal format +// (BCD). The sub-seconds value is also available in binary format. +// +// The following helper functions are encode into BCD format from integer and +// decode to an integer from a BCD value respectively. +fn bcd2_encode(word: u32) -> (u8, u8) { + let mut value = word as u8; + let mut bcd_high: u8 = 0; + while value >= 10 { + bcd_high += 1; + value -= 10; + } + // let bcd_low = ((bcd_high << 4) | value) as u8; + let bcd_low = value as u8; + (bcd_high, bcd_low) +} + +fn bcd2_decode(fst: u8, snd: u8) -> u32 { + let value = snd | fst << 4; + let value = (value & 0x0F) + ((value & 0xF0) >> 4) * 10; + value as u32 +} + +fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { + match hours { + Hours::H24(h) if h > 23 => Err(Error::InvalidInputData), + Hours::H24(h) => Ok(bcd2_encode(h as u32)), + Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), + Hours::AM(h) => Ok(bcd2_encode(h as u32)), + Hours::PM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), + Hours::PM(h) => Ok(bcd2_encode(h as u32)), + } +} + +fn hours_to_u8(hours: rtcc::Hours) -> u8 { + if let rtcc::Hours::H24(h) = hours { + h + } else { + panic!("hours could not be destructured into rtc::Hours::H24(h)"); + } +} From 6c504cd52dc04bb8a898194d309a42588e99d1a2 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 20:43:09 -0400 Subject: [PATCH 13/31] synced rcc.rs --- examples/dac.rs | 4 ++-- src/rcc.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/examples/dac.rs b/examples/dac.rs index a9d4117c9..e57f1b038 100644 --- a/examples/dac.rs +++ b/examples/dac.rs @@ -5,7 +5,7 @@ use cortex_m; use cortex_m_rt::entry; use embedded_hal::blocking::delay::DelayMs; use hal::{ - dac::{Dac, DacBits, DacId, DacTrait}, + dac::{Dac, DacBits, DacId, SingleChannelDac}, delay::Delay, pac, prelude::*, @@ -37,7 +37,7 @@ fn main() -> ! { dac.try_enable(&mut rcc.apb1).ok(); dac.try_set_voltage(1.5).ok(); - // dac.set_value(128); // or equivalently + // dac.try_set_value(128).ok(); // or equivalently loop { delay.delay_ms(1_000_u16); diff --git a/src/rcc.rs b/src/rcc.rs index 8d3c1780f..afd8b2e4b 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -33,6 +33,15 @@ impl RccExt for RCC { } /// Constrained RCC peripheral +/// +/// An instance of this struct is aquired by calling the +/// [constrain](trait.RccExt.html#tymethod.constrain) function on the +/// [pac::RCC](../pac/struct.RCC.html) struct. +/// +/// ``` +/// let dp = pac::Peripherals::take().unwrap(); +/// let rcc = dp.RCC.constrain(); +/// ``` pub struct Rcc { /// AMBA High-performance Bus (AHB) registers pub ahb: AHB, @@ -47,6 +56,14 @@ pub struct Rcc { } /// AMBA High-performance Bus (AHB) registers +/// +/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. +/// +/// ``` +/// let dp = pac::Peripherals::take().unwrap(); +/// let rcc = dp.RCC.constrain(); +/// use_ahb(&mut rcc.ahb) +/// ``` pub struct AHB { _0: (), } @@ -64,6 +81,14 @@ impl AHB { } /// Advanced Peripheral Bus 1 (APB1) registers +/// +/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. +/// +/// ``` +/// let dp = pac::Peripherals::take().unwrap(); +/// let rcc = dp.RCC.constrain(); +/// use_ahb(&mut rcc.apb1) +/// ``` pub struct APB1 { _0: (), } @@ -81,6 +106,14 @@ impl APB1 { } /// Advanced Peripheral Bus 2 (APB2) registers +/// +/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. +/// +/// ``` +/// let dp = pac::Peripherals::take().unwrap(); +/// let rcc = dp.RCC.constrain(); +/// use_ahb(&mut rcc.apb2) +/// ``` pub struct APB2 { _0: (), } @@ -173,6 +206,14 @@ impl BDCR { } /// Clock configuration +/// +/// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. +/// +/// ``` +/// let dp = pac::Peripherals::take().unwrap(); +/// let rcc = dp.RCC.constrain(); +/// use_ahb(&mut rcc.cfgr) +/// ``` pub struct CFGR { hse: Option, hclk: Option, From ba64ec992f441c31377af9a2eae0e91974812742 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 21:18:40 -0400 Subject: [PATCH 14/31] only allow dac on 303 --- Cargo.toml | 4 ++++ src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5ebcf4749..ffb6fc143 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,3 +113,7 @@ required-features = ["stm32f303"] [[example]] name = "adc" required-features = ["stm32f303"] + +[[example]] +name = "dac" +required-features = ["stm32f303"] diff --git a/src/lib.rs b/src/lib.rs index ccc79813f..9029c06ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ pub use crate::pac::interrupt; #[cfg(feature = "stm32f303")] pub mod adc; -#[cfg(feature = "device-selected")] +#[cfg(feature = "stm32f303")] pub mod dac; #[cfg(feature = "device-selected")] pub mod delay; From f9f8932bbbfffd6acd1d139b8d21f317be4f630e Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 21:23:18 -0400 Subject: [PATCH 15/31] Addressed more of the rtc review --- src/rtc.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index c8ed6840b..9f2a890d5 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -191,7 +191,7 @@ impl Rtcc for Rtc { } fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> { - if (weekday < 1) | (weekday > 7) { + if (weekday < 1) || (weekday > 7) { return Err(Error::InvalidInputData); } Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) }))) @@ -392,34 +392,21 @@ impl Rtcc for Rtc { // of week), date (day of month), month, and year, expressed in binary coded decimal format // (BCD). The sub-seconds value is also available in binary format. // -// The following helper functions are encode into BCD format from integer and +// The following helper functions encode into BCD format from integer and // decode to an integer from a BCD value respectively. fn bcd2_encode(word: u32) -> (u8, u8) { - let mut value = word as u8; - let mut bcd_high: u8 = 0; - while value >= 10 { - bcd_high += 1; - value -= 10; - } - // let bcd_low = ((bcd_high << 4) | value) as u8; - let bcd_low = value as u8; - (bcd_high, bcd_low) + (word / 10, word % 10) } fn bcd2_decode(fst: u8, snd: u8) -> u32 { - let value = snd | fst << 4; - let value = (value & 0x0F) + ((value & 0xF0) >> 4) * 10; - value as u32 + fst * 10 + snd } fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { match hours { - Hours::H24(h) if h > 23 => Err(Error::InvalidInputData), - Hours::H24(h) => Ok(bcd2_encode(h as u32)), - Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), - Hours::AM(h) => Ok(bcd2_encode(h as u32)), - Hours::PM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), - Hours::PM(h) => Ok(bcd2_encode(h as u32)), + Hours::H24(h @ 0..=23) => Ok(bcd2_encode(h as u32)), + Hours::AM(h @ 1..=12) | Hours::AM(h @ 1..=12) => Ok(bcd2_encode(h as u32)), + _ => Err(Error::InvalidInputData), } } From 2d332a806e2908475f6f5cd80840a9857d4e89a3 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 21:54:39 -0400 Subject: [PATCH 16/31] More RTC tweaks: Bug fixes, and review changes --- src/rtc.rs | 118 ++++++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index 9f2a890d5..26327291d 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -2,6 +2,7 @@ use crate::rcc::{APB1, BDCR}; use crate::stm32::{PWR, RTC}; +use core::convert::TryInto; use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike}; /// Invalid input error @@ -28,66 +29,20 @@ impl Rtc { prediv_a, }; - Self::unlock(apb1); - Self::enable(bdcr); + enable_lse(bdcr, false); + unlock(apb1); + enable(bdcr); result.set_24h_fmt(); - result.modify(|regs| { - regs.prer - .write(|w| unsafe { w.prediv_s().bits(prediv_s).prediv_a().bits(prediv_a) }); - }); + // todo: Put back. currently crashing. + // result.regs.prer.modify(|_, w| unsafe { + // w.prediv_s().bits(prediv_s); + // w.prediv_a().bits(prediv_a) + // }); result } - /// Enable the low frequency external oscillator. This is the only mode currently - /// supported, to avoid - fn enable_lse(bdcr: &mut BDCR, bypass: bool) { - bdcr.bdcr() - .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass)); - while bdcr.bdcr().read().lserdy().bit_is_clear() {} - } - - fn unlock(apb1: &mut APB1) { - let pwr = unsafe { &(*PWR::ptr()) }; - apb1.enr().modify(|_, w| { - w - // Enable the backup interface by setting PWREN - .pwren() - .set_bit() - }); - pwr.cr.modify(|_, w| { - w - // Enable access to the backup registers - .dbp() - .set_bit() - }); - - while pwr.cr.read().dbp().bit_is_clear() {} - } - - fn enable(bdcr: &mut BDCR) { - bdcr.bdcr().modify(|_, w| { - w - // RTC Backup Domain reset bit set high - .bdrst() - .set_bit() - }); - - bdcr.bdcr().modify(|_, w| { - w - // RTC clock source selection - .rtcsel() - .bits(LSE_BITS) - // Enable RTC - .rtcen() - .set_bit() - // RTC backup Domain reset bit set low - .bdrst() - .clear_bit() - }); - } - /// Sets calendar clock to 24 hr format pub fn set_24h_fmt(&mut self) { self.regs.cr.modify(|_, w| w.fmt().set_bit()); @@ -395,11 +350,14 @@ impl Rtcc for Rtc { // The following helper functions encode into BCD format from integer and // decode to an integer from a BCD value respectively. fn bcd2_encode(word: u32) -> (u8, u8) { - (word / 10, word % 10) + ( + (word / 10).try_into().unwrap(), + (word % 10).try_into().unwrap(), + ) } fn bcd2_decode(fst: u8, snd: u8) -> u32 { - fst * 10 + snd + (fst * 10 + snd).into() } fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { @@ -417,3 +375,51 @@ fn hours_to_u8(hours: rtcc::Hours) -> u8 { panic!("hours could not be destructured into rtc::Hours::H24(h)"); } } + +/// Enable the low frequency external oscillator. This is the only mode currently +/// supported, to avoid +fn enable_lse(bdcr: &mut BDCR, bypass: bool) { + bdcr.bdcr() + .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass)); + while bdcr.bdcr().read().lserdy().bit_is_clear() {} +} + +fn unlock(apb1: &mut APB1) { + let pwr = unsafe { &(*PWR::ptr()) }; + apb1.enr().modify(|_, w| { + w + // Enable the backup interface by setting PWREN + .pwren() + .set_bit() + }); + pwr.cr.modify(|_, w| { + w + // Enable access to the backup registers + .dbp() + .set_bit() + }); + + while pwr.cr.read().dbp().bit_is_clear() {} +} + +fn enable(bdcr: &mut BDCR) { + bdcr.bdcr().modify(|_, w| { + w + // RTC Backup Domain reset bit set high + .bdrst() + .set_bit() + }); + + bdcr.bdcr().modify(|_, w| { + w + // RTC clock source selection + .rtcsel() + .bits(LSE_BITS) + // Enable RTC + .rtcen() + .set_bit() + // RTC backup Domain reset bit set low + .bdrst() + .clear_bit() + }); +} From d555dde810ebd16af087f7abba7e03b8ceaa198b Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 21:55:29 -0400 Subject: [PATCH 17/31] More rtc tweaks --- src/rtc.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index 26327291d..c3f5efdab 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -34,11 +34,10 @@ impl Rtc { enable(bdcr); result.set_24h_fmt(); - // todo: Put back. currently crashing. - // result.regs.prer.modify(|_, w| unsafe { - // w.prediv_s().bits(prediv_s); - // w.prediv_a().bits(prediv_a) - // }); + result.regs.prer.modify(|_, w| unsafe { + w.prediv_s().bits(prediv_s); + w.prediv_a().bits(prediv_a) + }); result } From 1cb53a83132882848f69d30f157096bf9c2ea100 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 22:04:56 -0400 Subject: [PATCH 18/31] Travis fix --- src/rtc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rtc.rs b/src/rtc.rs index c3f5efdab..ae42e8943 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -363,7 +363,6 @@ fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { match hours { Hours::H24(h @ 0..=23) => Ok(bcd2_encode(h as u32)), Hours::AM(h @ 1..=12) | Hours::AM(h @ 1..=12) => Ok(bcd2_encode(h as u32)), - _ => Err(Error::InvalidInputData), } } From ecbd13c721e39e9f944494994270798c4d042dac Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Wed, 2 Sep 2020 23:22:07 -0400 Subject: [PATCH 19/31] Fixed a bug with hours_to_register --- src/rtc.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index ae42e8943..31be514d4 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -361,8 +361,9 @@ fn bcd2_decode(fst: u8, snd: u8) -> u32 { fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { match hours { - Hours::H24(h @ 0..=23) => Ok(bcd2_encode(h as u32)), - Hours::AM(h @ 1..=12) | Hours::AM(h @ 1..=12) => Ok(bcd2_encode(h as u32)), + Hours::H24(h) => Ok(bcd2_encode(h as u32)), + Hours::AM(h) => Ok(bcd2_encode((h - 1) as u32)), + Hours::PM(h) => Ok(bcd2_encode((h + 11) as u32)), } } From 404070cd2d6811f39fa85c7b6f6f0f40b86c8a77 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Thu, 3 Sep 2020 00:19:29 -0400 Subject: [PATCH 20/31] Ran clippy --- src/dac.rs | 12 +++++------- src/rtc.rs | 54 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/dac.rs b/src/dac.rs index 2fe0e18a2..79b9ad2af 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -106,9 +106,7 @@ impl Dac { } DacId::Two => { self.regs.cr.modify(|_, w| w.ten2().set_bit()); - self.regs - .cr - .modify(|_, w| unsafe { w.tsel2().bits(trigger.bits()) }); + self.regs.cr.modify(|_, w| w.tsel2().bits(trigger.bits())); } } } @@ -120,11 +118,11 @@ impl Dac { match self.id { DacId::One => { self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01) }); - self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b01) }); + self.regs.cr.modify(|_, w| w.mamp1().bits(0b01)); } DacId::Two => { self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01) }); - self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b01) }); + self.regs.cr.modify(|_, w| w.mamp2().bits(0b01)); } } self.set_trigger(trigger); @@ -140,11 +138,11 @@ impl Dac { match self.id { DacId::One => { self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b10) }); - self.regs.cr.modify(|_, w| unsafe { w.mamp1().bits(0b10) }); + self.regs.cr.modify(|_, w| w.mamp1().bits(0b10)); } DacId::Two => { self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b10) }); - self.regs.cr.modify(|_, w| unsafe { w.mamp2().bits(0b10) }); + self.regs.cr.modify(|_, w| w.mamp2().bits(0b10)); } } self.set_trigger(trigger); diff --git a/src/rtc.rs b/src/rtc.rs index 31be514d4..c3c762fa7 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -61,7 +61,7 @@ impl Rtc { /// when modifying an RTC register fn modify(&mut self, mut closure: F) where - F: FnMut(&mut RTC) -> (), + F: FnMut(&mut RTC), { // Disable write protection self.regs.wpr.write(|w| unsafe { w.bits(0xCA) }); @@ -91,7 +91,7 @@ impl Rtcc for Rtc { let (ht, hu) = bcd2_encode(time.hour()); let (mnt, mnu) = bcd2_encode(time.minute()); let (st, su) = bcd2_encode(time.second()); - Ok(self.modify(|regs| { + self.modify(|regs| { regs.tr.write(|w| unsafe { w.ht() .bits(ht) @@ -108,7 +108,9 @@ impl Rtcc for Rtc { .pm() .clear_bit() }); - })) + }); + + Ok(()) } fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> { @@ -116,7 +118,9 @@ impl Rtcc for Rtc { return Err(Error::InvalidInputData); } let (st, su) = bcd2_encode(seconds as u32); - Ok(self.modify(|regs| regs.tr.write(|w| unsafe { w.st().bits(st).su().bits(su) }))) + self.modify(|regs| regs.tr.write(|w| unsafe { w.st().bits(st).su().bits(su) })); + + Ok(()) } fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> { @@ -124,10 +128,12 @@ impl Rtcc for Rtc { return Err(Error::InvalidInputData); } let (mnt, mnu) = bcd2_encode(minutes as u32); - Ok(self.modify(|regs| { + self.modify(|regs| { regs.tr .write(|w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) - })) + }); + + Ok(()) } fn set_hours(&mut self, hours: rtcc::Hours) -> Result<(), Self::Error> { @@ -135,20 +141,24 @@ impl Rtcc for Rtc { match hours { Hours::H24(_h) => { self.set_24h_fmt(); - Ok(self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) }))) + self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); } Hours::AM(_h) | Hours::PM(_h) => { self.set_12h_fmt(); - Ok(self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) }))) + self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); } } + + Ok(()) } fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> { if (weekday < 1) || (weekday > 7) { return Err(Error::InvalidInputData); } - Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) }))) + self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) })); + + Ok(()) } fn set_day(&mut self, day: u8) -> Result<(), Self::Error> { @@ -156,7 +166,9 @@ impl Rtcc for Rtc { return Err(Error::InvalidInputData); } let (dt, du) = bcd2_encode(day as u32); - Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.dt().bits(dt).du().bits(du) }))) + self.modify(|regs| regs.dr.write(|w| unsafe { w.dt().bits(dt).du().bits(du) })); + + Ok(()) } fn set_month(&mut self, month: u8) -> Result<(), Self::Error> { @@ -164,10 +176,12 @@ impl Rtcc for Rtc { return Err(Error::InvalidInputData); } let (mt, mu) = bcd2_encode(month as u32); - Ok(self.modify(|regs| { + self.modify(|regs| { regs.dr .write(|w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) - })) + }); + + Ok(()) } fn set_year(&mut self, year: u16) -> Result<(), Self::Error> { @@ -175,7 +189,9 @@ impl Rtcc for Rtc { return Err(Error::InvalidInputData); } let (yt, yu) = bcd2_encode(year as u32); - Ok(self.modify(|regs| regs.dr.write(|w| unsafe { w.yt().bits(yt).yu().bits(yu) }))) + self.modify(|regs| regs.dr.write(|w| unsafe { w.yt().bits(yt).yu().bits(yu) })); + + Ok(()) } /// set date using NaiveDate (ISO 8601 calendar date without timezone) @@ -185,7 +201,7 @@ impl Rtcc for Rtc { let (mt, mu) = bcd2_encode(date.month()); let (dt, du) = bcd2_encode(date.day()); - Ok(self.modify(|regs| { + self.modify(|regs| { regs.dr.write(|w| unsafe { w.dt() .bits(dt) @@ -200,7 +216,9 @@ impl Rtcc for Rtc { .yu() .bits(yu) }); - })) + }); + + Ok(()) } fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> { @@ -231,7 +249,7 @@ impl Rtcc for Rtc { .bits(yu) }); }); - Ok(self.modify(|regs| { + self.modify(|regs| { regs.tr.write(|w| unsafe { w.ht() .bits(ht) @@ -248,7 +266,9 @@ impl Rtcc for Rtc { .pm() .clear_bit() }); - })) + }); + + Ok(()) } fn get_seconds(&mut self) -> Result { From e5c81478f527b480b820bf1d464d5705e9b91a76 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Thu, 3 Sep 2020 19:21:39 -0400 Subject: [PATCH 21/31] Removed rtc code --- Cargo.toml | 1 - examples/dac.rs | 2 + src/lib.rs | 2 - src/rcc.rs | 15 -- src/rtc.rs | 444 ------------------------------------------------ 5 files changed, 2 insertions(+), 462 deletions(-) delete mode 100644 src/rtc.rs diff --git a/Cargo.toml b/Cargo.toml index ffb6fc143..8e49f6642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ cortex-m-rt = "0.6" embedded-hal = "0.2" nb = "0.1" stm32f3 = "0.11" -rtcc = "0.2" [dependencies.bare-metal] version = "0.2" diff --git a/examples/dac.rs b/examples/dac.rs index e57f1b038..8668eecea 100644 --- a/examples/dac.rs +++ b/examples/dac.rs @@ -1,6 +1,8 @@ #![no_main] #![no_std] +use panic_semihosting; + use cortex_m; use cortex_m_rt::entry; use embedded_hal::blocking::delay::DelayMs; diff --git a/src/lib.rs b/src/lib.rs index 9029c06ea..3ffce1ef2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,8 +133,6 @@ pub mod pwm; #[cfg(feature = "device-selected")] pub mod rcc; #[cfg(feature = "device-selected")] -pub mod rtc; -#[cfg(feature = "device-selected")] pub mod serial; #[cfg(feature = "device-selected")] pub mod spi; diff --git a/src/rcc.rs b/src/rcc.rs index afd8b2e4b..3c8adeb5c 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -20,7 +20,6 @@ impl RccExt for RCC { ahb: AHB { _0: () }, apb1: APB1 { _0: () }, apb2: APB2 { _0: () }, - bdcr: BDCR { _0: () }, cfgr: CFGR { hse: None, hclk: None, @@ -49,8 +48,6 @@ pub struct Rcc { pub apb1: APB1, /// Advanced Peripheral Bus 2 (APB2) registers pub apb2: APB2, - /// RCC Backup Domain - pub bdcr: BDCR, /// Clock configuration pub cfgr: CFGR, } @@ -193,18 +190,6 @@ mod usb_clocking { } } -/// Backup Domain Control register (RCC_BDCR) -pub struct BDCR { - _0: (), -} - -impl BDCR { - pub(crate) fn bdcr(&mut self) -> &rcc::BDCR { - // NOTE(unsafe) this proxy grants exclusive access to this register - unsafe { &(*RCC::ptr()).bdcr } - } -} - /// Clock configuration /// /// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. diff --git a/src/rtc.rs b/src/rtc.rs deleted file mode 100644 index c3c762fa7..000000000 --- a/src/rtc.rs +++ /dev/null @@ -1,444 +0,0 @@ -//! Interface to the real time clock. See STM32F303 reference manual, section 27. - -use crate::rcc::{APB1, BDCR}; -use crate::stm32::{PWR, RTC}; -use core::convert::TryInto; -use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike}; - -/// Invalid input error -#[derive(Debug)] -pub enum Error { - InvalidInputData, -} - -pub const LSE_BITS: u8 = 0b01; - -pub struct Rtc { - pub regs: RTC, - pub prediv_s: u16, - pub prediv_a: u8, -} - -impl Rtc { - /// Create and enable a new RTC, and configure its clock source and prescalers. - pub fn new(regs: RTC, prediv_s: u16, prediv_a: u8, apb1: &mut APB1, bdcr: &mut BDCR) -> Self { - // todo defaults: // prediv_s: 311, prediv_a: 127. From where? - let mut result = Self { - regs, - prediv_s, - prediv_a, - }; - - enable_lse(bdcr, false); - unlock(apb1); - enable(bdcr); - result.set_24h_fmt(); - - result.regs.prer.modify(|_, w| unsafe { - w.prediv_s().bits(prediv_s); - w.prediv_a().bits(prediv_a) - }); - - result - } - - /// Sets calendar clock to 24 hr format - pub fn set_24h_fmt(&mut self) { - self.regs.cr.modify(|_, w| w.fmt().set_bit()); - } - /// Sets calendar clock to 12 hr format - pub fn set_12h_fmt(&mut self) { - self.regs.cr.modify(|_, w| w.fmt().clear_bit()); - } - - /// Reads current hour format selection - pub fn is_24h_fmt(&self) -> bool { - self.regs.cr.read().fmt().bit() - } - - /// As described in Section 27.3.7 in RM0316, - /// this function is used to disable write protection - /// when modifying an RTC register - fn modify(&mut self, mut closure: F) - where - F: FnMut(&mut RTC), - { - // Disable write protection - self.regs.wpr.write(|w| unsafe { w.bits(0xCA) }); - self.regs.wpr.write(|w| unsafe { w.bits(0x53) }); - // Enter init mode - let isr = self.regs.isr.read(); - if isr.initf().bit_is_clear() { - self.regs.isr.modify(|_, w| w.init().set_bit()); - while self.regs.isr.read().initf().bit_is_clear() {} - } - // Invoke closure - closure(&mut self.regs); - // Exit init mode - self.regs.isr.modify(|_, w| w.init().clear_bit()); - // wait for last write to be done - while !self.regs.isr.read().initf().bit_is_clear() {} - } -} - -impl Rtcc for Rtc { - type Error = Error; - - /// set time using NaiveTime (ISO 8601 time without timezone) - /// Hour format is 24h - fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> { - self.set_24h_fmt(); - let (ht, hu) = bcd2_encode(time.hour()); - let (mnt, mnu) = bcd2_encode(time.minute()); - let (st, su) = bcd2_encode(time.second()); - self.modify(|regs| { - regs.tr.write(|w| unsafe { - w.ht() - .bits(ht) - .hu() - .bits(hu) - .mnt() - .bits(mnt) - .mnu() - .bits(mnu) - .st() - .bits(st) - .su() - .bits(su) - .pm() - .clear_bit() - }); - }); - - Ok(()) - } - - fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> { - if seconds > 59 { - return Err(Error::InvalidInputData); - } - let (st, su) = bcd2_encode(seconds as u32); - self.modify(|regs| regs.tr.write(|w| unsafe { w.st().bits(st).su().bits(su) })); - - Ok(()) - } - - fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> { - if minutes > 59 { - return Err(Error::InvalidInputData); - } - let (mnt, mnu) = bcd2_encode(minutes as u32); - self.modify(|regs| { - regs.tr - .write(|w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) - }); - - Ok(()) - } - - fn set_hours(&mut self, hours: rtcc::Hours) -> Result<(), Self::Error> { - let (ht, hu) = hours_to_register(hours)?; - match hours { - Hours::H24(_h) => { - self.set_24h_fmt(); - self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); - } - Hours::AM(_h) | Hours::PM(_h) => { - self.set_12h_fmt(); - self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); - } - } - - Ok(()) - } - - fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> { - if (weekday < 1) || (weekday > 7) { - return Err(Error::InvalidInputData); - } - self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) })); - - Ok(()) - } - - fn set_day(&mut self, day: u8) -> Result<(), Self::Error> { - if (day < 1) | (day > 31) { - return Err(Error::InvalidInputData); - } - let (dt, du) = bcd2_encode(day as u32); - self.modify(|regs| regs.dr.write(|w| unsafe { w.dt().bits(dt).du().bits(du) })); - - Ok(()) - } - - fn set_month(&mut self, month: u8) -> Result<(), Self::Error> { - if (month < 1) | (month > 12) { - return Err(Error::InvalidInputData); - } - let (mt, mu) = bcd2_encode(month as u32); - self.modify(|regs| { - regs.dr - .write(|w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) - }); - - Ok(()) - } - - fn set_year(&mut self, year: u16) -> Result<(), Self::Error> { - if (year < 1970) | (year > 2038) { - return Err(Error::InvalidInputData); - } - let (yt, yu) = bcd2_encode(year as u32); - self.modify(|regs| regs.dr.write(|w| unsafe { w.yt().bits(yt).yu().bits(yu) })); - - Ok(()) - } - - /// set date using NaiveDate (ISO 8601 calendar date without timezone) - /// WeekDay is set using set_weekday method - fn set_date(&mut self, date: &NaiveDate) -> Result<(), Self::Error> { - let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); - let (mt, mu) = bcd2_encode(date.month()); - let (dt, du) = bcd2_encode(date.day()); - - self.modify(|regs| { - regs.dr.write(|w| unsafe { - w.dt() - .bits(dt) - .du() - .bits(du) - .mt() - .bit(mt > 0) - .mu() - .bits(mu) - .yt() - .bits(yt) - .yu() - .bits(yu) - }); - }); - - Ok(()) - } - - fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> { - // Check if unsigned integer affects encoding to bcd - - self.set_24h_fmt(); - let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); - let (mt, mu) = bcd2_encode(date.month()); - let (dt, du) = bcd2_encode(date.day()); - - let (ht, hu) = bcd2_encode(date.hour()); - let (mnt, mnu) = bcd2_encode(date.minute()); - let (st, su) = bcd2_encode(date.second()); - - self.modify(|regs| { - regs.dr.write(|w| unsafe { - w.dt() - .bits(dt) - .du() - .bits(du) - .mt() - .bit(mt > 0) - .mu() - .bits(mu) - .yt() - .bits(yt) - .yu() - .bits(yu) - }); - }); - self.modify(|regs| { - regs.tr.write(|w| unsafe { - w.ht() - .bits(ht) - .hu() - .bits(hu) - .mnt() - .bits(mnt) - .mnu() - .bits(mnu) - .st() - .bits(st) - .su() - .bits(su) - .pm() - .clear_bit() - }); - }); - - Ok(()) - } - - fn get_seconds(&mut self) -> Result { - let tr = self.regs.tr.read(); - let seconds = bcd2_decode(tr.st().bits(), tr.su().bits()); - Ok(seconds as u8) - } - - fn get_minutes(&mut self) -> Result { - let tr = self.regs.tr.read(); - let minutes = bcd2_decode(tr.mnt().bits(), tr.mnu().bits()); - Ok(minutes as u8) - } - - fn get_hours(&mut self) -> Result { - let tr = self.regs.tr.read(); - let hours = bcd2_decode(tr.ht().bits(), tr.hu().bits()); - if self.is_24h_fmt() { - return Ok(rtcc::Hours::H24(hours as u8)); - } - if !tr.pm().bit() { - return Ok(rtcc::Hours::AM(hours as u8)); - } - Ok(rtcc::Hours::PM(hours as u8)) - } - - fn get_time(&mut self) -> Result { - self.set_24h_fmt(); - let seconds = self.get_seconds().unwrap(); - let minutes = self.get_minutes().unwrap(); - let hours = hours_to_u8(self.get_hours().unwrap()); - - Ok(NaiveTime::from_hms( - hours.into(), - minutes.into(), - seconds.into(), - )) - } - - fn get_weekday(&mut self) -> Result { - let dr = self.regs.dr.read(); - let weekday = bcd2_decode(dr.wdu().bits(), 0x00); - Ok(weekday as u8) - } - - fn get_day(&mut self) -> Result { - let dr = self.regs.dr.read(); - let day = bcd2_decode(dr.dt().bits(), dr.du().bits()); - Ok(day as u8) - } - - fn get_month(&mut self) -> Result { - let dr = self.regs.dr.read(); - let mt: u8 = if dr.mt().bit() { 1 } else { 0 }; - let month = bcd2_decode(mt, dr.mu().bits()); - Ok(month as u8) - } - - fn get_year(&mut self) -> Result { - let dr = self.regs.dr.read(); - let year = bcd2_decode(dr.yt().bits(), dr.yu().bits()); - Ok(year as u16) - } - - fn get_date(&mut self) -> Result { - let day = self.get_day().unwrap(); - let month = self.get_month().unwrap(); - let year = self.get_year().unwrap(); - - Ok(NaiveDate::from_ymd(year.into(), month.into(), day.into())) - } - - fn get_datetime(&mut self) -> Result { - self.set_24h_fmt(); - - let day = self.get_day().unwrap(); - let month = self.get_month().unwrap(); - let year = self.get_year().unwrap(); - - let seconds = self.get_seconds().unwrap(); - let minutes = self.get_minutes().unwrap(); - let hours = hours_to_u8(self.get_hours().unwrap()); - - Ok( - NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms( - hours.into(), - minutes.into(), - seconds.into(), - ), - ) - } -} - -// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours (12- or 24-hour format), day (day -// of week), date (day of month), month, and year, expressed in binary coded decimal format -// (BCD). The sub-seconds value is also available in binary format. -// -// The following helper functions encode into BCD format from integer and -// decode to an integer from a BCD value respectively. -fn bcd2_encode(word: u32) -> (u8, u8) { - ( - (word / 10).try_into().unwrap(), - (word % 10).try_into().unwrap(), - ) -} - -fn bcd2_decode(fst: u8, snd: u8) -> u32 { - (fst * 10 + snd).into() -} - -fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { - match hours { - Hours::H24(h) => Ok(bcd2_encode(h as u32)), - Hours::AM(h) => Ok(bcd2_encode((h - 1) as u32)), - Hours::PM(h) => Ok(bcd2_encode((h + 11) as u32)), - } -} - -fn hours_to_u8(hours: rtcc::Hours) -> u8 { - if let rtcc::Hours::H24(h) = hours { - h - } else { - panic!("hours could not be destructured into rtc::Hours::H24(h)"); - } -} - -/// Enable the low frequency external oscillator. This is the only mode currently -/// supported, to avoid -fn enable_lse(bdcr: &mut BDCR, bypass: bool) { - bdcr.bdcr() - .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass)); - while bdcr.bdcr().read().lserdy().bit_is_clear() {} -} - -fn unlock(apb1: &mut APB1) { - let pwr = unsafe { &(*PWR::ptr()) }; - apb1.enr().modify(|_, w| { - w - // Enable the backup interface by setting PWREN - .pwren() - .set_bit() - }); - pwr.cr.modify(|_, w| { - w - // Enable access to the backup registers - .dbp() - .set_bit() - }); - - while pwr.cr.read().dbp().bit_is_clear() {} -} - -fn enable(bdcr: &mut BDCR) { - bdcr.bdcr().modify(|_, w| { - w - // RTC Backup Domain reset bit set high - .bdrst() - .set_bit() - }); - - bdcr.bdcr().modify(|_, w| { - w - // RTC clock source selection - .rtcsel() - .bits(LSE_BITS) - // Enable RTC - .rtcen() - .set_bit() - // RTC backup Domain reset bit set low - .bdrst() - .clear_bit() - }); -} From c722478e36214e47a0ef8fffbd5b1720d23ddeda Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Thu, 3 Sep 2020 19:24:04 -0400 Subject: [PATCH 22/31] First commit --- Cargo.toml | 1 + examples/dac.rs | 47 ----- src/dac.rs | 222 ------------------------ src/lib.rs | 4 +- src/rcc.rs | 15 ++ src/rtc.rs | 444 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 462 insertions(+), 271 deletions(-) delete mode 100644 examples/dac.rs delete mode 100644 src/dac.rs create mode 100644 src/rtc.rs diff --git a/Cargo.toml b/Cargo.toml index 8e49f6642..ffb6fc143 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ cortex-m-rt = "0.6" embedded-hal = "0.2" nb = "0.1" stm32f3 = "0.11" +rtcc = "0.2" [dependencies.bare-metal] version = "0.2" diff --git a/examples/dac.rs b/examples/dac.rs deleted file mode 100644 index 8668eecea..000000000 --- a/examples/dac.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![no_main] -#![no_std] - -use panic_semihosting; - -use cortex_m; -use cortex_m_rt::entry; -use embedded_hal::blocking::delay::DelayMs; -use hal::{ - dac::{Dac, DacBits, DacId, SingleChannelDac}, - delay::Delay, - pac, - prelude::*, -}; -use stm32f3xx_hal as hal; - -#[entry] -fn main() -> ! { - // Set up CPU peripherals - let mut cp = cortex_m::Peripherals::take().unwrap(); - - // Set up microcontroller peripherals - let dp = pac::Peripherals::take().unwrap(); - - // Set up clocks - let mut flash = dp.FLASH.constrain(); - let mut rcc = dp.RCC.constrain(); - let clocks = rcc.cfgr.freeze(&mut flash.acr); - - let mut delay = Delay::new(cp.SYST, clocks); - - // Set up gpio pins if required - let mut gpioa = dp.GPIOA.split(&mut rcc.ahb); - - // Set up the DAC - let mut _dac_pin = gpioa.pa4.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); - - let mut dac = Dac::new(dp.DAC, DacId::One, DacBits::EightR, 3.3); - dac.try_enable(&mut rcc.apb1).ok(); - - dac.try_set_voltage(1.5).ok(); - // dac.try_set_value(128).ok(); // or equivalently - - loop { - delay.delay_ms(1_000_u16); - } -} diff --git a/src/dac.rs b/src/dac.rs deleted file mode 100644 index 79b9ad2af..000000000 --- a/src/dac.rs +++ /dev/null @@ -1,222 +0,0 @@ -//! Configure the internal DAC on the stm32f3xx. -//! Incomplete, but includes basic operation. -//! -//! You should have the approprite dac pin set up as an analog input, to prevent -//! parasitic power consumption. For example: -//! ```rust -//! let _dac1_pin = gpioa.pa4.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); -//! let _dac2_pin = gpioa.pa5.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); -//! ``` -//! - -// todo: DAC trait that will go in a PR to embeddd hal. -// [PR](https://github.com/rust-embedded/embedded-hal/pull/247) - -//! Digital-analog conversion traits -//! -//! A trait used to identify a digital-to-analog converter, and its -//! most fundamental features. - -use crate::{pac, rcc::APB1}; - -pub trait SingleChannelDac { - /// Error type returned by DAC methods - type Error; - type Word; - - /// Enable the DAC. - fn try_enable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; // todo: generalize periph clock - - /// Disable the DAC. - fn try_disable(&mut self, p: &mut APB1) -> Result<(), Self::Error>; - - /// Output a constant signal, given a bit word. - fn try_set_value(&mut self, value: Self::Word) -> Result<(), Self::Error>; - - /// Output a constant signal, given a voltage - fn try_set_voltage(&mut self, volts: f32) -> Result<(), Self::Error>; -} - -#[derive(Clone, Copy, Debug)] -pub enum DacId { - One, - Two, -} - -#[derive(Clone, Copy, Debug)] -pub enum DacBits { - EightR, - TwelveL, - TwelveR, -} - -#[derive(Clone, Copy, Debug)] -pub enum Trigger { - Tim6, - Tim3_8, - Tim7, - Tim15, - Tim2, - Tim4, // Not available on DAC 2. - Exti9, - Swtrig, -} - -impl Trigger { - pub fn bits(&self) -> u8 { - match self { - Self::Tim6 => 0b000, - Self::Tim3_8 => 0b001, - Self::Tim7 => 0b010, - Self::Tim15 => 0b011, - Self::Tim2 => 0b100, - Self::Tim4 => 0b101, - Self::Exti9 => 0b110, - Self::Swtrig => 0b111, - } - } -} - -pub struct Dac { - regs: pac::DAC, - id: DacId, - bits: DacBits, - vref: f32, -} - -impl Dac { - /// Create a new DAC instances - pub fn new(regs: pac::DAC, id: DacId, bits: DacBits, vref: f32) -> Self { - Self { - regs, - id, - bits, - vref, - } - } - - // Select and activate a trigger. See f303 Reference manual, section 16.5.4. - pub fn set_trigger(&mut self, trigger: Trigger) { - match self.id { - DacId::One => { - self.regs.cr.modify(|_, w| w.ten1().set_bit()); - self.regs - .cr - .modify(|_, w| unsafe { w.tsel1().bits(trigger.bits()) }); - } - DacId::Two => { - self.regs.cr.modify(|_, w| w.ten2().set_bit()); - self.regs.cr.modify(|_, w| w.tsel2().bits(trigger.bits())); - } - } - } - - /// Independent trigger with single LFSR generation - /// See f303 Reference Manual section 16.5.2 - pub fn trigger_lfsr(&mut self, trigger: Trigger, data: u32) -> Result<(), DacError> { - // todo: This may not be correct. - match self.id { - DacId::One => { - self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b01) }); - self.regs.cr.modify(|_, w| w.mamp1().bits(0b01)); - } - DacId::Two => { - self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b01) }); - self.regs.cr.modify(|_, w| w.mamp2().bits(0b01)); - } - } - self.set_trigger(trigger); - self.try_set_value(data)?; - - Ok(()) - } - - /// Independent trigger with single triangle generation - /// See f303 Reference Manual section 16.5.2 - pub fn trigger_triangle(&mut self, trigger: Trigger, data: u32) -> Result<(), DacError> { - // todo: This may not be correct. - match self.id { - DacId::One => { - self.regs.cr.modify(|_, w| unsafe { w.wave1().bits(0b10) }); - self.regs.cr.modify(|_, w| w.mamp1().bits(0b10)); - } - DacId::Two => { - self.regs.cr.modify(|_, w| unsafe { w.wave2().bits(0b10) }); - self.regs.cr.modify(|_, w| w.mamp2().bits(0b10)); - } - } - self.set_trigger(trigger); - self.try_set_value(data)?; - - Ok(()) - } -} - -pub struct DacError {} - -impl SingleChannelDac for Dac { - type Error = DacError; - type Word = u32; - - /// Enable the DAC. - fn try_enable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { - match self.id { - DacId::One => { - apb1.enr().modify(|_, w| w.dac1en().set_bit()); - self.regs.cr.modify(|_, w| w.en1().set_bit()); - } - DacId::Two => { - apb1.enr().modify(|_, w| w.dac2en().set_bit()); - self.regs.cr.modify(|_, w| w.en2().set_bit()); - } - } - - Ok(()) - } - - fn try_disable(&mut self, apb1: &mut APB1) -> Result<(), DacError> { - match self.id { - DacId::One => { - self.regs.cr.modify(|_, w| w.en1().clear_bit()); - apb1.enr().modify(|_, w| w.dac1en().clear_bit()); - } - DacId::Two => { - self.regs.cr.modify(|_, w| w.en2().clear_bit()); - apb1.enr().modify(|_, w| w.dac2en().clear_bit()); - } - } - - Ok(()) - } - - /// Set the DAC value as an integer. - fn try_set_value(&mut self, val: u32) -> Result<(), DacError> { - match self.id { - DacId::One => match self.bits { - DacBits::EightR => self.regs.dhr8r1.modify(|_, w| unsafe { w.bits(val) }), - DacBits::TwelveL => self.regs.dhr12l1.modify(|_, w| unsafe { w.bits(val) }), - DacBits::TwelveR => self.regs.dhr12r1.modify(|_, w| unsafe { w.bits(val) }), - }, - DacId::Two => match self.bits { - DacBits::EightR => self.regs.dhr8r2.modify(|_, w| unsafe { w.bits(val) }), - DacBits::TwelveL => self.regs.dhr12l2.modify(|_, w| unsafe { w.bits(val) }), - DacBits::TwelveR => self.regs.dhr12r2.modify(|_, w| unsafe { w.bits(val) }), - }, - } - - Ok(()) - } - - /// Set the DAC voltage. `v` is in Volts. - fn try_set_voltage(&mut self, volts: f32) -> Result<(), DacError> { - let val = match self.bits { - DacBits::EightR => ((volts / self.vref) * 255.) as u32, - DacBits::TwelveL => ((volts / self.vref) * 4_095.) as u32, - DacBits::TwelveR => ((volts / self.vref) * 4_095.) as u32, - }; - - self.try_set_value(val)?; - - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs index 3ffce1ef2..f20697460 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,8 +114,6 @@ pub use crate::pac::interrupt; #[cfg(feature = "stm32f303")] pub mod adc; -#[cfg(feature = "stm32f303")] -pub mod dac; #[cfg(feature = "device-selected")] pub mod delay; #[cfg(feature = "stm32f303")] @@ -133,6 +131,8 @@ pub mod pwm; #[cfg(feature = "device-selected")] pub mod rcc; #[cfg(feature = "device-selected")] +pub mod rtc; +#[cfg(feature = "device-selected")] pub mod serial; #[cfg(feature = "device-selected")] pub mod spi; diff --git a/src/rcc.rs b/src/rcc.rs index 3c8adeb5c..afd8b2e4b 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -20,6 +20,7 @@ impl RccExt for RCC { ahb: AHB { _0: () }, apb1: APB1 { _0: () }, apb2: APB2 { _0: () }, + bdcr: BDCR { _0: () }, cfgr: CFGR { hse: None, hclk: None, @@ -48,6 +49,8 @@ pub struct Rcc { pub apb1: APB1, /// Advanced Peripheral Bus 2 (APB2) registers pub apb2: APB2, + /// RCC Backup Domain + pub bdcr: BDCR, /// Clock configuration pub cfgr: CFGR, } @@ -190,6 +193,18 @@ mod usb_clocking { } } +/// Backup Domain Control register (RCC_BDCR) +pub struct BDCR { + _0: (), +} + +impl BDCR { + pub(crate) fn bdcr(&mut self) -> &rcc::BDCR { + // NOTE(unsafe) this proxy grants exclusive access to this register + unsafe { &(*RCC::ptr()).bdcr } + } +} + /// Clock configuration /// /// An instance of this struct is aquired from the [Rcc](../struct.Rcc.html) struct. diff --git a/src/rtc.rs b/src/rtc.rs new file mode 100644 index 000000000..c3c762fa7 --- /dev/null +++ b/src/rtc.rs @@ -0,0 +1,444 @@ +//! Interface to the real time clock. See STM32F303 reference manual, section 27. + +use crate::rcc::{APB1, BDCR}; +use crate::stm32::{PWR, RTC}; +use core::convert::TryInto; +use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike}; + +/// Invalid input error +#[derive(Debug)] +pub enum Error { + InvalidInputData, +} + +pub const LSE_BITS: u8 = 0b01; + +pub struct Rtc { + pub regs: RTC, + pub prediv_s: u16, + pub prediv_a: u8, +} + +impl Rtc { + /// Create and enable a new RTC, and configure its clock source and prescalers. + pub fn new(regs: RTC, prediv_s: u16, prediv_a: u8, apb1: &mut APB1, bdcr: &mut BDCR) -> Self { + // todo defaults: // prediv_s: 311, prediv_a: 127. From where? + let mut result = Self { + regs, + prediv_s, + prediv_a, + }; + + enable_lse(bdcr, false); + unlock(apb1); + enable(bdcr); + result.set_24h_fmt(); + + result.regs.prer.modify(|_, w| unsafe { + w.prediv_s().bits(prediv_s); + w.prediv_a().bits(prediv_a) + }); + + result + } + + /// Sets calendar clock to 24 hr format + pub fn set_24h_fmt(&mut self) { + self.regs.cr.modify(|_, w| w.fmt().set_bit()); + } + /// Sets calendar clock to 12 hr format + pub fn set_12h_fmt(&mut self) { + self.regs.cr.modify(|_, w| w.fmt().clear_bit()); + } + + /// Reads current hour format selection + pub fn is_24h_fmt(&self) -> bool { + self.regs.cr.read().fmt().bit() + } + + /// As described in Section 27.3.7 in RM0316, + /// this function is used to disable write protection + /// when modifying an RTC register + fn modify(&mut self, mut closure: F) + where + F: FnMut(&mut RTC), + { + // Disable write protection + self.regs.wpr.write(|w| unsafe { w.bits(0xCA) }); + self.regs.wpr.write(|w| unsafe { w.bits(0x53) }); + // Enter init mode + let isr = self.regs.isr.read(); + if isr.initf().bit_is_clear() { + self.regs.isr.modify(|_, w| w.init().set_bit()); + while self.regs.isr.read().initf().bit_is_clear() {} + } + // Invoke closure + closure(&mut self.regs); + // Exit init mode + self.regs.isr.modify(|_, w| w.init().clear_bit()); + // wait for last write to be done + while !self.regs.isr.read().initf().bit_is_clear() {} + } +} + +impl Rtcc for Rtc { + type Error = Error; + + /// set time using NaiveTime (ISO 8601 time without timezone) + /// Hour format is 24h + fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> { + self.set_24h_fmt(); + let (ht, hu) = bcd2_encode(time.hour()); + let (mnt, mnu) = bcd2_encode(time.minute()); + let (st, su) = bcd2_encode(time.second()); + self.modify(|regs| { + regs.tr.write(|w| unsafe { + w.ht() + .bits(ht) + .hu() + .bits(hu) + .mnt() + .bits(mnt) + .mnu() + .bits(mnu) + .st() + .bits(st) + .su() + .bits(su) + .pm() + .clear_bit() + }); + }); + + Ok(()) + } + + fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> { + if seconds > 59 { + return Err(Error::InvalidInputData); + } + let (st, su) = bcd2_encode(seconds as u32); + self.modify(|regs| regs.tr.write(|w| unsafe { w.st().bits(st).su().bits(su) })); + + Ok(()) + } + + fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> { + if minutes > 59 { + return Err(Error::InvalidInputData); + } + let (mnt, mnu) = bcd2_encode(minutes as u32); + self.modify(|regs| { + regs.tr + .write(|w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) + }); + + Ok(()) + } + + fn set_hours(&mut self, hours: rtcc::Hours) -> Result<(), Self::Error> { + let (ht, hu) = hours_to_register(hours)?; + match hours { + Hours::H24(_h) => { + self.set_24h_fmt(); + self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); + } + Hours::AM(_h) | Hours::PM(_h) => { + self.set_12h_fmt(); + self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); + } + } + + Ok(()) + } + + fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> { + if (weekday < 1) || (weekday > 7) { + return Err(Error::InvalidInputData); + } + self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) })); + + Ok(()) + } + + fn set_day(&mut self, day: u8) -> Result<(), Self::Error> { + if (day < 1) | (day > 31) { + return Err(Error::InvalidInputData); + } + let (dt, du) = bcd2_encode(day as u32); + self.modify(|regs| regs.dr.write(|w| unsafe { w.dt().bits(dt).du().bits(du) })); + + Ok(()) + } + + fn set_month(&mut self, month: u8) -> Result<(), Self::Error> { + if (month < 1) | (month > 12) { + return Err(Error::InvalidInputData); + } + let (mt, mu) = bcd2_encode(month as u32); + self.modify(|regs| { + regs.dr + .write(|w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) + }); + + Ok(()) + } + + fn set_year(&mut self, year: u16) -> Result<(), Self::Error> { + if (year < 1970) | (year > 2038) { + return Err(Error::InvalidInputData); + } + let (yt, yu) = bcd2_encode(year as u32); + self.modify(|regs| regs.dr.write(|w| unsafe { w.yt().bits(yt).yu().bits(yu) })); + + Ok(()) + } + + /// set date using NaiveDate (ISO 8601 calendar date without timezone) + /// WeekDay is set using set_weekday method + fn set_date(&mut self, date: &NaiveDate) -> Result<(), Self::Error> { + let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); + let (mt, mu) = bcd2_encode(date.month()); + let (dt, du) = bcd2_encode(date.day()); + + self.modify(|regs| { + regs.dr.write(|w| unsafe { + w.dt() + .bits(dt) + .du() + .bits(du) + .mt() + .bit(mt > 0) + .mu() + .bits(mu) + .yt() + .bits(yt) + .yu() + .bits(yu) + }); + }); + + Ok(()) + } + + fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> { + // Check if unsigned integer affects encoding to bcd + + self.set_24h_fmt(); + let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); + let (mt, mu) = bcd2_encode(date.month()); + let (dt, du) = bcd2_encode(date.day()); + + let (ht, hu) = bcd2_encode(date.hour()); + let (mnt, mnu) = bcd2_encode(date.minute()); + let (st, su) = bcd2_encode(date.second()); + + self.modify(|regs| { + regs.dr.write(|w| unsafe { + w.dt() + .bits(dt) + .du() + .bits(du) + .mt() + .bit(mt > 0) + .mu() + .bits(mu) + .yt() + .bits(yt) + .yu() + .bits(yu) + }); + }); + self.modify(|regs| { + regs.tr.write(|w| unsafe { + w.ht() + .bits(ht) + .hu() + .bits(hu) + .mnt() + .bits(mnt) + .mnu() + .bits(mnu) + .st() + .bits(st) + .su() + .bits(su) + .pm() + .clear_bit() + }); + }); + + Ok(()) + } + + fn get_seconds(&mut self) -> Result { + let tr = self.regs.tr.read(); + let seconds = bcd2_decode(tr.st().bits(), tr.su().bits()); + Ok(seconds as u8) + } + + fn get_minutes(&mut self) -> Result { + let tr = self.regs.tr.read(); + let minutes = bcd2_decode(tr.mnt().bits(), tr.mnu().bits()); + Ok(minutes as u8) + } + + fn get_hours(&mut self) -> Result { + let tr = self.regs.tr.read(); + let hours = bcd2_decode(tr.ht().bits(), tr.hu().bits()); + if self.is_24h_fmt() { + return Ok(rtcc::Hours::H24(hours as u8)); + } + if !tr.pm().bit() { + return Ok(rtcc::Hours::AM(hours as u8)); + } + Ok(rtcc::Hours::PM(hours as u8)) + } + + fn get_time(&mut self) -> Result { + self.set_24h_fmt(); + let seconds = self.get_seconds().unwrap(); + let minutes = self.get_minutes().unwrap(); + let hours = hours_to_u8(self.get_hours().unwrap()); + + Ok(NaiveTime::from_hms( + hours.into(), + minutes.into(), + seconds.into(), + )) + } + + fn get_weekday(&mut self) -> Result { + let dr = self.regs.dr.read(); + let weekday = bcd2_decode(dr.wdu().bits(), 0x00); + Ok(weekday as u8) + } + + fn get_day(&mut self) -> Result { + let dr = self.regs.dr.read(); + let day = bcd2_decode(dr.dt().bits(), dr.du().bits()); + Ok(day as u8) + } + + fn get_month(&mut self) -> Result { + let dr = self.regs.dr.read(); + let mt: u8 = if dr.mt().bit() { 1 } else { 0 }; + let month = bcd2_decode(mt, dr.mu().bits()); + Ok(month as u8) + } + + fn get_year(&mut self) -> Result { + let dr = self.regs.dr.read(); + let year = bcd2_decode(dr.yt().bits(), dr.yu().bits()); + Ok(year as u16) + } + + fn get_date(&mut self) -> Result { + let day = self.get_day().unwrap(); + let month = self.get_month().unwrap(); + let year = self.get_year().unwrap(); + + Ok(NaiveDate::from_ymd(year.into(), month.into(), day.into())) + } + + fn get_datetime(&mut self) -> Result { + self.set_24h_fmt(); + + let day = self.get_day().unwrap(); + let month = self.get_month().unwrap(); + let year = self.get_year().unwrap(); + + let seconds = self.get_seconds().unwrap(); + let minutes = self.get_minutes().unwrap(); + let hours = hours_to_u8(self.get_hours().unwrap()); + + Ok( + NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms( + hours.into(), + minutes.into(), + seconds.into(), + ), + ) + } +} + +// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours (12- or 24-hour format), day (day +// of week), date (day of month), month, and year, expressed in binary coded decimal format +// (BCD). The sub-seconds value is also available in binary format. +// +// The following helper functions encode into BCD format from integer and +// decode to an integer from a BCD value respectively. +fn bcd2_encode(word: u32) -> (u8, u8) { + ( + (word / 10).try_into().unwrap(), + (word % 10).try_into().unwrap(), + ) +} + +fn bcd2_decode(fst: u8, snd: u8) -> u32 { + (fst * 10 + snd).into() +} + +fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { + match hours { + Hours::H24(h) => Ok(bcd2_encode(h as u32)), + Hours::AM(h) => Ok(bcd2_encode((h - 1) as u32)), + Hours::PM(h) => Ok(bcd2_encode((h + 11) as u32)), + } +} + +fn hours_to_u8(hours: rtcc::Hours) -> u8 { + if let rtcc::Hours::H24(h) = hours { + h + } else { + panic!("hours could not be destructured into rtc::Hours::H24(h)"); + } +} + +/// Enable the low frequency external oscillator. This is the only mode currently +/// supported, to avoid +fn enable_lse(bdcr: &mut BDCR, bypass: bool) { + bdcr.bdcr() + .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass)); + while bdcr.bdcr().read().lserdy().bit_is_clear() {} +} + +fn unlock(apb1: &mut APB1) { + let pwr = unsafe { &(*PWR::ptr()) }; + apb1.enr().modify(|_, w| { + w + // Enable the backup interface by setting PWREN + .pwren() + .set_bit() + }); + pwr.cr.modify(|_, w| { + w + // Enable access to the backup registers + .dbp() + .set_bit() + }); + + while pwr.cr.read().dbp().bit_is_clear() {} +} + +fn enable(bdcr: &mut BDCR) { + bdcr.bdcr().modify(|_, w| { + w + // RTC Backup Domain reset bit set high + .bdrst() + .set_bit() + }); + + bdcr.bdcr().modify(|_, w| { + w + // RTC clock source selection + .rtcsel() + .bits(LSE_BITS) + // Enable RTC + .rtcen() + .set_bit() + // RTC backup Domain reset bit set low + .bdrst() + .clear_bit() + }); +} From 87ab0a2b2a86361ce8c2c5fade2875bd49ba02d3 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Thu, 3 Sep 2020 19:28:18 -0400 Subject: [PATCH 23/31] Removed dac example in cargo.toml --- Cargo.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ffb6fc143..78a0b6a5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,8 +112,4 @@ required-features = ["stm32f303"] [[example]] name = "adc" -required-features = ["stm32f303"] - -[[example]] -name = "dac" -required-features = ["stm32f303"] +required-features = ["stm32f303"] \ No newline at end of file From 350ac467fa217fd0a5fcf5280a3356bb815c98a5 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Thu, 3 Sep 2020 19:39:05 -0400 Subject: [PATCH 24/31] Added note about prediv_s and prediv_a, with reference --- src/rtc.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rtc.rs b/src/rtc.rs index c3c762fa7..c0e4875fc 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -1,4 +1,6 @@ //! Interface to the real time clock. See STM32F303 reference manual, section 27. +//! For more details, see +//! [ST AN4759](https:/www.st.com%2Fresource%2Fen%2Fapplication_note%2Fdm00226326-using-the-hardware-realtime-clock-rtc-and-the-tamper-management-unit-tamp-with-stm32-microcontrollers-stmicroelectronics.pdf&usg=AOvVaw3PzvL2TfYtwS32fw-Uv37h) use crate::rcc::{APB1, BDCR}; use crate::stm32::{PWR, RTC}; @@ -21,8 +23,10 @@ pub struct Rtc { impl Rtc { /// Create and enable a new RTC, and configure its clock source and prescalers. + /// From AN4759, Table 7, when using the LSE (The only clock source this module + /// supports currently), set `prediv_s` to 255, and `prediv_a` to 127 to get a + /// calendar clock of 1Hz. pub fn new(regs: RTC, prediv_s: u16, prediv_a: u8, apb1: &mut APB1, bdcr: &mut BDCR) -> Self { - // todo defaults: // prediv_s: 311, prediv_a: 127. From where? let mut result = Self { regs, prediv_s, From 5600431761312b52b8d1051d9b9f6ecd0f678ae2 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Thu, 3 Sep 2020 20:26:56 -0400 Subject: [PATCH 25/31] fmt/travis --- src/rtc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index c0e4875fc..45729b85c 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -1,5 +1,5 @@ //! Interface to the real time clock. See STM32F303 reference manual, section 27. -//! For more details, see +//! For more details, see //! [ST AN4759](https:/www.st.com%2Fresource%2Fen%2Fapplication_note%2Fdm00226326-using-the-hardware-realtime-clock-rtc-and-the-tamper-management-unit-tamp-with-stm32-microcontrollers-stmicroelectronics.pdf&usg=AOvVaw3PzvL2TfYtwS32fw-Uv37h) use crate::rcc::{APB1, BDCR}; @@ -24,7 +24,7 @@ pub struct Rtc { impl Rtc { /// Create and enable a new RTC, and configure its clock source and prescalers. /// From AN4759, Table 7, when using the LSE (The only clock source this module - /// supports currently), set `prediv_s` to 255, and `prediv_a` to 127 to get a + /// supports currently), set `prediv_s` to 255, and `prediv_a` to 127 to get a /// calendar clock of 1Hz. pub fn new(regs: RTC, prediv_s: u16, prediv_a: u8, apb1: &mut APB1, bdcr: &mut BDCR) -> Self { let mut result = Self { From 35905a2ad8afa3b81ab1ca61230f815d2dd5e252 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Fri, 4 Sep 2020 12:40:33 -0400 Subject: [PATCH 26/31] Many tweaks --- Cargo.toml | 2 +- src/rtc.rs | 200 +++++++++++++++++++++++------------------------------ 2 files changed, 87 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 78a0b6a5a..5ebcf4749 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,4 +112,4 @@ required-features = ["stm32f303"] [[example]] name = "adc" -required-features = ["stm32f303"] \ No newline at end of file +required-features = ["stm32f303"] diff --git a/src/rtc.rs b/src/rtc.rs index 45729b85c..6cfad6d5d 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -2,8 +2,8 @@ //! For more details, see //! [ST AN4759](https:/www.st.com%2Fresource%2Fen%2Fapplication_note%2Fdm00226326-using-the-hardware-realtime-clock-rtc-and-the-tamper-management-unit-tamp-with-stm32-microcontrollers-stmicroelectronics.pdf&usg=AOvVaw3PzvL2TfYtwS32fw-Uv37h) +use crate::pac::{PWR, RTC}; use crate::rcc::{APB1, BDCR}; -use crate::stm32::{PWR, RTC}; use core::convert::TryInto; use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike}; @@ -17,8 +17,6 @@ pub const LSE_BITS: u8 = 0b01; pub struct Rtc { pub regs: RTC, - pub prediv_s: u16, - pub prediv_a: u8, } impl Rtc { @@ -26,15 +24,21 @@ impl Rtc { /// From AN4759, Table 7, when using the LSE (The only clock source this module /// supports currently), set `prediv_s` to 255, and `prediv_a` to 127 to get a /// calendar clock of 1Hz. - pub fn new(regs: RTC, prediv_s: u16, prediv_a: u8, apb1: &mut APB1, bdcr: &mut BDCR) -> Self { - let mut result = Self { - regs, - prediv_s, - prediv_a, - }; - - enable_lse(bdcr, false); - unlock(apb1); + /// The `bypass` argument is `true` if you're using an external oscillator that + /// doesn't connect to `OSC32_IN`, such as a MEMS resonator. + pub fn new( + regs: RTC, + prediv_s: u16, + prediv_a: u8, + bypass: bool, + apb1: &mut APB1, + bdcr: &mut BDCR, + pwr: &mut PWR, + ) -> Self { + let mut result = Self { regs }; + + enable_lse(bdcr, bypass); + unlock(apb1, pwr); enable(bdcr); result.set_24h_fmt(); @@ -95,23 +99,14 @@ impl Rtcc for Rtc { let (ht, hu) = bcd2_encode(time.hour()); let (mnt, mnu) = bcd2_encode(time.minute()); let (st, su) = bcd2_encode(time.second()); - self.modify(|regs| { - regs.tr.write(|w| unsafe { - w.ht() - .bits(ht) - .hu() - .bits(hu) - .mnt() - .bits(mnt) - .mnu() - .bits(mnu) - .st() - .bits(st) - .su() - .bits(su) - .pm() - .clear_bit() - }); + self.regs.tr.modify(|_, w| unsafe { + w.ht().bits(ht); + w.hu().bits(hu); + w.mnt().bits(mnt); + w.mnu().bits(mnu); + w.st().bits(st); + w.su().bits(su); + w.pm().clear_bit() }); Ok(()) @@ -122,7 +117,10 @@ impl Rtcc for Rtc { return Err(Error::InvalidInputData); } let (st, su) = bcd2_encode(seconds as u32); - self.modify(|regs| regs.tr.write(|w| unsafe { w.st().bits(st).su().bits(su) })); + self.modify(|regs| { + regs.tr + .modify(|_, w| unsafe { w.st().bits(st).su().bits(su) }) + }); Ok(()) } @@ -134,22 +132,28 @@ impl Rtcc for Rtc { let (mnt, mnu) = bcd2_encode(minutes as u32); self.modify(|regs| { regs.tr - .write(|w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) + .modify(|_, w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) }); Ok(()) } - fn set_hours(&mut self, hours: rtcc::Hours) -> Result<(), Self::Error> { + fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> { let (ht, hu) = hours_to_register(hours)?; match hours { Hours::H24(_h) => { self.set_24h_fmt(); - self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); + self.modify(|regs| { + regs.tr + .modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) }) + }); } Hours::AM(_h) | Hours::PM(_h) => { self.set_12h_fmt(); - self.modify(|regs| regs.tr.write(|w| unsafe { w.ht().bits(ht).hu().bits(hu) })); + self.modify(|regs| { + regs.tr + .modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) }) + }); } } @@ -160,40 +164,46 @@ impl Rtcc for Rtc { if (weekday < 1) || (weekday > 7) { return Err(Error::InvalidInputData); } - self.modify(|regs| regs.dr.write(|w| unsafe { w.wdu().bits(weekday) })); + self.modify(|regs| regs.dr.modify(|_, w| unsafe { w.wdu().bits(weekday) })); Ok(()) } fn set_day(&mut self, day: u8) -> Result<(), Self::Error> { - if (day < 1) | (day > 31) { + if (day < 1) || (day > 31) { return Err(Error::InvalidInputData); } let (dt, du) = bcd2_encode(day as u32); - self.modify(|regs| regs.dr.write(|w| unsafe { w.dt().bits(dt).du().bits(du) })); + self.modify(|regs| { + regs.dr + .modify(|_, w| unsafe { w.dt().bits(dt).du().bits(du) }) + }); Ok(()) } fn set_month(&mut self, month: u8) -> Result<(), Self::Error> { - if (month < 1) | (month > 12) { + if (month < 1) || (month > 12) { return Err(Error::InvalidInputData); } let (mt, mu) = bcd2_encode(month as u32); self.modify(|regs| { regs.dr - .write(|w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) + .modify(|_, w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) }); Ok(()) } fn set_year(&mut self, year: u16) -> Result<(), Self::Error> { - if (year < 1970) | (year > 2038) { + if (year < 1970) || (year > 2038) { return Err(Error::InvalidInputData); } let (yt, yu) = bcd2_encode(year as u32); - self.modify(|regs| regs.dr.write(|w| unsafe { w.yt().bits(yt).yu().bits(yu) })); + self.modify(|regs| { + regs.dr + .modify(|_, w| unsafe { w.yt().bits(yt).yu().bits(yu) }) + }); Ok(()) } @@ -205,21 +215,13 @@ impl Rtcc for Rtc { let (mt, mu) = bcd2_encode(date.month()); let (dt, du) = bcd2_encode(date.day()); - self.modify(|regs| { - regs.dr.write(|w| unsafe { - w.dt() - .bits(dt) - .du() - .bits(du) - .mt() - .bit(mt > 0) - .mu() - .bits(mu) - .yt() - .bits(yt) - .yu() - .bits(yu) - }); + self.regs.dr.modify(|_, w| unsafe { + w.dt().bits(dt); + w.du().bits(du); + w.mt().bit(mt > 0); + w.mu().bits(mu); + w.yt().bits(yt); + w.yu().bits(yu) }); Ok(()) @@ -237,39 +239,23 @@ impl Rtcc for Rtc { let (mnt, mnu) = bcd2_encode(date.minute()); let (st, su) = bcd2_encode(date.second()); - self.modify(|regs| { - regs.dr.write(|w| unsafe { - w.dt() - .bits(dt) - .du() - .bits(du) - .mt() - .bit(mt > 0) - .mu() - .bits(mu) - .yt() - .bits(yt) - .yu() - .bits(yu) - }); + self.regs.dr.modify(|_, w| unsafe { + w.dt().bits(dt); + w.du().bits(du); + w.mt().bit(mt > 0); + w.mu().bits(mu); + w.yt().bits(yt); + w.yu().bits(yu) }); - self.modify(|regs| { - regs.tr.write(|w| unsafe { - w.ht() - .bits(ht) - .hu() - .bits(hu) - .mnt() - .bits(mnt) - .mnu() - .bits(mnu) - .st() - .bits(st) - .su() - .bits(su) - .pm() - .clear_bit() - }); + + self.regs.tr.modify(|_, w| unsafe { + w.ht().bits(ht); + w.hu().bits(hu); + w.mnt().bits(mnt); + w.mnu().bits(mnu); + w.st().bits(st); + w.su().bits(su); + w.pm().clear_bit() }); Ok(()) @@ -287,16 +273,16 @@ impl Rtcc for Rtc { Ok(minutes as u8) } - fn get_hours(&mut self) -> Result { + fn get_hours(&mut self) -> Result { let tr = self.regs.tr.read(); let hours = bcd2_decode(tr.ht().bits(), tr.hu().bits()); if self.is_24h_fmt() { - return Ok(rtcc::Hours::H24(hours as u8)); + return Ok(Hours::H24(hours as u8)); } if !tr.pm().bit() { - return Ok(rtcc::Hours::AM(hours as u8)); + return Ok(Hours::AM(hours as u8)); } - Ok(rtcc::Hours::PM(hours as u8)) + Ok(Hours::PM(hours as u8)) } fn get_time(&mut self) -> Result { @@ -391,8 +377,8 @@ fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { } } -fn hours_to_u8(hours: rtcc::Hours) -> u8 { - if let rtcc::Hours::H24(h) = hours { +fn hours_to_u8(hours: Hours) -> u8 { + if let Hours::H24(h) = hours { h } else { panic!("hours could not be destructured into rtc::Hours::H24(h)"); @@ -400,15 +386,14 @@ fn hours_to_u8(hours: rtcc::Hours) -> u8 { } /// Enable the low frequency external oscillator. This is the only mode currently -/// supported, to avoid +/// supported, to avoid exposing the `CR` and `CRS` registers. fn enable_lse(bdcr: &mut BDCR, bypass: bool) { bdcr.bdcr() .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass)); while bdcr.bdcr().read().lserdy().bit_is_clear() {} } -fn unlock(apb1: &mut APB1) { - let pwr = unsafe { &(*PWR::ptr()) }; +fn unlock(apb1: &mut APB1, pwr: &mut PWR) { apb1.enr().modify(|_, w| { w // Enable the backup interface by setting PWREN @@ -426,23 +411,10 @@ fn unlock(apb1: &mut APB1) { } fn enable(bdcr: &mut BDCR) { + bdcr.bdcr().modify(|_, w| w.bdrst().enabled()); bdcr.bdcr().modify(|_, w| { - w - // RTC Backup Domain reset bit set high - .bdrst() - .set_bit() - }); - - bdcr.bdcr().modify(|_, w| { - w - // RTC clock source selection - .rtcsel() - .bits(LSE_BITS) - // Enable RTC - .rtcen() - .set_bit() - // RTC backup Domain reset bit set low - .bdrst() - .clear_bit() + w.rtcsel().lse(); + w.rtcen().enabled(); + w.bdrst().disabled() }); } From be0590507644ac4aaf436d2a1278220a49f98fa6 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Fri, 4 Sep 2020 13:03:09 -0400 Subject: [PATCH 27/31] More review changes --- src/rtc.rs | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index 6cfad6d5d..3458ec80d 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -141,22 +141,14 @@ impl Rtcc for Rtc { fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> { let (ht, hu) = hours_to_register(hours)?; match hours { - Hours::H24(_h) => { - self.set_24h_fmt(); - self.modify(|regs| { - regs.tr - .modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) }) - }); - } - Hours::AM(_h) | Hours::PM(_h) => { - self.set_12h_fmt(); - self.modify(|regs| { - regs.tr - .modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) }) - }); - } + Hours::H24(_h) => self.set_24h_fmt(), + Hours::AM(_h) | Hours::PM(_h) => self.set_12h_fmt(), } + self.regs + .tr + .modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) }); + Ok(()) } @@ -228,8 +220,6 @@ impl Rtcc for Rtc { } fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> { - // Check if unsigned integer affects encoding to bcd - self.set_24h_fmt(); let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); let (mt, mu) = bcd2_encode(date.month()); @@ -289,7 +279,7 @@ impl Rtcc for Rtc { self.set_24h_fmt(); let seconds = self.get_seconds().unwrap(); let minutes = self.get_minutes().unwrap(); - let hours = hours_to_u8(self.get_hours().unwrap()); + let hours = hours_to_u8(self.get_hours()?)?; Ok(NaiveTime::from_hms( hours.into(), @@ -340,7 +330,7 @@ impl Rtcc for Rtc { let seconds = self.get_seconds().unwrap(); let minutes = self.get_minutes().unwrap(); - let hours = hours_to_u8(self.get_hours().unwrap()); + let hours = hours_to_u8(self.get_hours()?)?; Ok( NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms( @@ -377,11 +367,12 @@ fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { } } -fn hours_to_u8(hours: Hours) -> u8 { +fn hours_to_u8(hours: Hours) -> Result { + if let Hours::H24(h) = hours { - h + Ok(h) } else { - panic!("hours could not be destructured into rtc::Hours::H24(h)"); + Err(Error::InvalidInputData) } } From 835939cf48053b0c5b2d2fdc58a89a8fdb9b74cf Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Fri, 4 Sep 2020 13:47:24 -0400 Subject: [PATCH 28/31] fmt/travis --- src/rtc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rtc.rs b/src/rtc.rs index 3458ec80d..941b47e44 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -368,7 +368,6 @@ fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { } fn hours_to_u8(hours: Hours) -> Result { - if let Hours::H24(h) = hours { Ok(h) } else { From c6f0e582180c2abcb364547522c0b1f1b3da4cab Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Sun, 6 Sep 2020 11:40:58 -0400 Subject: [PATCH 29/31] More cleanup --- src/rtc.rs | 72 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index 941b47e44..c831c8230 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -96,9 +96,9 @@ impl Rtcc for Rtc { /// Hour format is 24h fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> { self.set_24h_fmt(); - let (ht, hu) = bcd2_encode(time.hour()); - let (mnt, mnu) = bcd2_encode(time.minute()); - let (st, su) = bcd2_encode(time.second()); + let (ht, hu) = bcd2_encode(time.hour())?; + let (mnt, mnu) = bcd2_encode(time.minute())?; + let (st, su) = bcd2_encode(time.second())?; self.regs.tr.modify(|_, w| unsafe { w.ht().bits(ht); w.hu().bits(hu); @@ -116,7 +116,7 @@ impl Rtcc for Rtc { if seconds > 59 { return Err(Error::InvalidInputData); } - let (st, su) = bcd2_encode(seconds as u32); + let (st, su) = bcd2_encode(seconds as u32)?; self.modify(|regs| { regs.tr .modify(|_, w| unsafe { w.st().bits(st).su().bits(su) }) @@ -129,7 +129,7 @@ impl Rtcc for Rtc { if minutes > 59 { return Err(Error::InvalidInputData); } - let (mnt, mnu) = bcd2_encode(minutes as u32); + let (mnt, mnu) = bcd2_encode(minutes as u32)?; self.modify(|regs| { regs.tr .modify(|_, w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) }) @@ -165,7 +165,7 @@ impl Rtcc for Rtc { if (day < 1) || (day > 31) { return Err(Error::InvalidInputData); } - let (dt, du) = bcd2_encode(day as u32); + let (dt, du) = bcd2_encode(day as u32)?; self.modify(|regs| { regs.dr .modify(|_, w| unsafe { w.dt().bits(dt).du().bits(du) }) @@ -178,7 +178,7 @@ impl Rtcc for Rtc { if (month < 1) || (month > 12) { return Err(Error::InvalidInputData); } - let (mt, mu) = bcd2_encode(month as u32); + let (mt, mu) = bcd2_encode(month as u32)?; self.modify(|regs| { regs.dr .modify(|_, w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) }) @@ -191,7 +191,7 @@ impl Rtcc for Rtc { if (year < 1970) || (year > 2038) { return Err(Error::InvalidInputData); } - let (yt, yu) = bcd2_encode(year as u32); + let (yt, yu) = bcd2_encode(year as u32)?; self.modify(|regs| { regs.dr .modify(|_, w| unsafe { w.yt().bits(yt).yu().bits(yu) }) @@ -200,12 +200,16 @@ impl Rtcc for Rtc { Ok(()) } - /// set date using NaiveDate (ISO 8601 calendar date without timezone) - /// WeekDay is set using set_weekday method + /// Set the date using NaiveDate (ISO 8601 calendar date without timezone). + /// WeekDay is set using the `set_weekday` method fn set_date(&mut self, date: &NaiveDate) -> Result<(), Self::Error> { - let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); - let (mt, mu) = bcd2_encode(date.month()); - let (dt, du) = bcd2_encode(date.day()); + if date.year() < 1970 { + return Err(Error::InvalidInputData); + } + + let (yt, yu) = bcd2_encode((date.year() - 1970) as u32)?; + let (mt, mu) = bcd2_encode(date.month())?; + let (dt, du) = bcd2_encode(date.day())?; self.regs.dr.modify(|_, w| unsafe { w.dt().bits(dt); @@ -220,14 +224,18 @@ impl Rtcc for Rtc { } fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> { + if date.year() < 1970 { + return Err(Error::InvalidInputData); + } + self.set_24h_fmt(); - let (yt, yu) = bcd2_encode((date.year() - 1970) as u32); - let (mt, mu) = bcd2_encode(date.month()); - let (dt, du) = bcd2_encode(date.day()); + let (yt, yu) = bcd2_encode((date.year() - 1970) as u32)?; + let (mt, mu) = bcd2_encode(date.month())?; + let (dt, du) = bcd2_encode(date.day())?; - let (ht, hu) = bcd2_encode(date.hour()); - let (mnt, mnu) = bcd2_encode(date.minute()); - let (st, su) = bcd2_encode(date.second()); + let (ht, hu) = bcd2_encode(date.hour())?; + let (mnt, mnu) = bcd2_encode(date.minute())?; + let (st, su) = bcd2_encode(date.second())?; self.regs.dr.modify(|_, w| unsafe { w.dt().bits(dt); @@ -348,11 +356,21 @@ impl Rtcc for Rtc { // // The following helper functions encode into BCD format from integer and // decode to an integer from a BCD value respectively. -fn bcd2_encode(word: u32) -> (u8, u8) { - ( - (word / 10).try_into().unwrap(), - (word % 10).try_into().unwrap(), - ) +fn bcd2_encode(word: u32) -> Result<(u8, u8), Error> { + let l = match (word / 10).try_into() { + Ok(v) => v, + Err(_) => { + return Err(Error::InvalidInputData); + } + }; + let r = match (word % 10).try_into() { + Ok(v) => v, + Err(_) => { + return Err(Error::InvalidInputData); + } + }; + + Ok((l, r)) } fn bcd2_decode(fst: u8, snd: u8) -> u32 { @@ -361,9 +379,9 @@ fn bcd2_decode(fst: u8, snd: u8) -> u32 { fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> { match hours { - Hours::H24(h) => Ok(bcd2_encode(h as u32)), - Hours::AM(h) => Ok(bcd2_encode((h - 1) as u32)), - Hours::PM(h) => Ok(bcd2_encode((h + 11) as u32)), + Hours::H24(h) => Ok(bcd2_encode(h as u32))?, + Hours::AM(h) => Ok(bcd2_encode((h - 1) as u32))?, + Hours::PM(h) => Ok(bcd2_encode((h + 11) as u32))?, } } From 17472a9aaaae7c3d0767a8ef45bb5df35dd93cc4 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Tue, 8 Sep 2020 12:11:58 -0400 Subject: [PATCH 30/31] Changed some modify to write --- src/rtc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rtc.rs b/src/rtc.rs index c831c8230..6a4bed284 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -99,7 +99,7 @@ impl Rtcc for Rtc { let (ht, hu) = bcd2_encode(time.hour())?; let (mnt, mnu) = bcd2_encode(time.minute())?; let (st, su) = bcd2_encode(time.second())?; - self.regs.tr.modify(|_, w| unsafe { + self.regs.tr.write(|w| unsafe { w.ht().bits(ht); w.hu().bits(hu); w.mnt().bits(mnt); @@ -211,7 +211,7 @@ impl Rtcc for Rtc { let (mt, mu) = bcd2_encode(date.month())?; let (dt, du) = bcd2_encode(date.day())?; - self.regs.dr.modify(|_, w| unsafe { + self.regs.dr.write(|w| unsafe { w.dt().bits(dt); w.du().bits(du); w.mt().bit(mt > 0); @@ -237,7 +237,7 @@ impl Rtcc for Rtc { let (mnt, mnu) = bcd2_encode(date.minute())?; let (st, su) = bcd2_encode(date.second())?; - self.regs.dr.modify(|_, w| unsafe { + self.regs.dr.write(|w| unsafe { w.dt().bits(dt); w.du().bits(du); w.mt().bit(mt > 0); @@ -246,7 +246,7 @@ impl Rtcc for Rtc { w.yu().bits(yu) }); - self.regs.tr.modify(|_, w| unsafe { + self.regs.tr.write(|w| unsafe { w.ht().bits(ht); w.hu().bits(hu); w.mnt().bits(mnt); From d443bd1d417dcc1ea6a46a01fdebba42fbcde665 Mon Sep 17 00:00:00 2001 From: David O'Connor Date: Tue, 8 Sep 2020 12:36:20 -0400 Subject: [PATCH 31/31] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed9e7a6c..0f5b17252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Support for 16-bit words with SPI ([#107](https://github.com/stm32-rs/stm32f3xx-hal/pull/107)) - SPI support for reclock after initialization ([#98](https://github.com/stm32-rs/stm32f3xx-hal/pull/98)) - Support for `stm32f302x6` and `stm32f302x8` devices ([#132](https://github.com/stm32-rs/stm32f3xx-hal/pull/132)) +- Support for the onboard real-time clock (RTC) ([#136](https://github.com/stm32-rs/stm32f3xx-hal/pull/136)) ## [v0.5.0] - 2020-07-21