diff --git a/.cargo/config b/.cargo/config index f907e493c..f27a4a749 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [target.thumbv7em-none-eabihf] -runner = "arm-none-eabi-gdb" +runner = "arm-none-eabi-gdb -q -x openocd.gdb" rustflags = [ "-C", "link-arg=-Tlink.x", ] diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f8edb0f8..e01f67530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - SPI4 peripheral for supported devices. ([#99](https://github.com/stm32-rs/stm32f3xx-hal/pull/99)) - Support for I2C transfer of more than 255 bytes, and 0 byte write ([#154](https://github.com/stm32-rs/stm32f3xx-hal/pull/154)) +- DAC support for `stm32f303` devices ([#133](https://github.com/stm32-rs/stm32f3xx-hal/pull/133)) - Support for HSE bypass and CSS ([#156](https://github.com/stm32-rs/stm32f3xx-hal/pull/156)) ### Changed diff --git a/Cargo.toml b/Cargo.toml index 41b1b4651..28e10af15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,7 +118,7 @@ required-features = ["rt", "stm32f303xc", "stm32-usbd"] [[example]] name = "spi" -required-features = ["stm32f303"] +required-features = ["stm32f303xc"] [[example]] name = "can" @@ -126,8 +126,12 @@ required-features = ["rt", "can", "stm32f303"] [[example]] name = "serial_dma" -required-features = ["stm32f303"] +required-features = ["stm32f303xc"] [[example]] name = "adc" -required-features = ["stm32f303"] +required-features = ["stm32f303xc"] + +[[example]] +name = "dac" +required-features = ["stm32f303xc"] diff --git a/examples/dac.rs b/examples/dac.rs new file mode 100644 index 000000000..b5fd1a355 --- /dev/null +++ b/examples/dac.rs @@ -0,0 +1,73 @@ +#![no_main] +#![no_std] + +use panic_semihosting as _; + +use cortex_m::asm; + +use cortex_m_rt::entry; +use stm32f3xx_hal::{ + dac::{Dac, DacBits, Trigger}, + pac, + prelude::*, +}; + +#[entry] +fn main() -> ! { + // 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); + + // Set up the DAC output pin. + // The output pin you choose selects the respective DAC channel: + // - PA4: channel 1 + // - PA5: channel 2 + let mut gpioa = dp.GPIOA.split(&mut rcc.ahb); + let dac_pin = gpioa.pa4.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); + + // Set up the DAC + let reference_voltage = 2.915; + let mut dac = Dac::new(dp.DAC, dac_pin, DacBits::EightR, reference_voltage); + dac.enable(&mut rcc.apb1); + + // primitively wait about one second on a default stm32f3discovery + let wait = 8_000_000; + asm::delay(wait); + + // directly set the output voltage to 1.5 V (valid values: 0..=reference_voltage) + dac.set_voltage(1.5); + asm::delay(3 * wait); + + // equivalently: + dac.set_value(256 / 2); + + // set the output off for 3 seconds + dac.set_value(0); + asm::delay(3 * wait); + + // setup a trigger. From now on, setting a value or voltage will be stored but not converted + dac.set_trigger(Trigger::SoftwareTrigger); + + // this is never converted: + dac.set_voltage(1.5); + asm::delay(wait); + + // toggle the dac value every 2 seconds + loop { + dac.set_value(0); + asm::delay(wait); + + dac.trigger_software_trigger(); + asm::delay(wait); + + dac.set_value(255); + asm::delay(wait); + + dac.trigger_software_trigger(); + asm::delay(wait); + } +} diff --git a/src/dac.rs b/src/dac.rs new file mode 100644 index 000000000..6f1ed4ebf --- /dev/null +++ b/src/dac.rs @@ -0,0 +1,190 @@ +//! Configure the internal DAC on the stm32f3xx. +//! Incomplete, but includes basic operation. + +use crate::{ + gpio::{ + gpioa::{PA4, PA5}, + Analog, + }, + pac, + rcc::APB1, +}; +use core::convert::Infallible; + +/// Trait representing a single-channel digital-to-analog converter (DAC). +pub trait SingleChannelDac { + /// Error type returned by DAC methods + type Error; + + /// Output a constant signal, given a bit word. + fn try_set_value(&mut self, value: Word) -> Result<(), Self::Error>; +} + +/// This is an abstraction to select the correct DAC channel for a given +/// (analog) output pin. +pub trait Pin { + const CHANNEL: Channel; +} + +impl Pin for PA4 { + const CHANNEL: Channel = Channel::One; +} + +impl Pin for PA5 { + const CHANNEL: Channel = Channel::Two; +} + +/// Select the channel +#[derive(Clone, Copy)] +pub enum Channel { + /// Channel 1 + One, + /// Channel 2 + Two, +} + +/// Three options are available to set DAC precision. +#[derive(Clone, Copy)] +pub enum DacBits { + /// Eight bit precision, right-aligned. + EightR, + /// 12-bit precision, left-aligned. + TwelveL, + /// 12-bit precision, right-aligned. + TwelveR, +} + +/// Select an external event to trigger the DAC. +#[derive(Clone, Copy)] +pub enum Trigger { + Timer2, + Timer3Or8, + Timer4, + Timer6, + Timer7, + Timer15, + Exti9, + SoftwareTrigger, +} + +impl Trigger { + fn bits(&self) -> u8 { + match self { + Self::Timer6 => 0b000, + Self::Timer3Or8 => 0b001, + Self::Timer7 => 0b010, + Self::Timer15 => 0b011, + Self::Timer2 => 0b100, + Self::Timer4 => 0b101, + Self::Exti9 => 0b110, + Self::SoftwareTrigger => 0b111, + } + } +} + +pub struct Dac

{ + regs: pac::DAC, + pin: P, + bits: DacBits, + vref: f32, +} + +impl Dac

{ + const CHANNEL: Channel = P::CHANNEL; + + /// Create a new DAC instance + pub fn new(regs: pac::DAC, pin: P, bits: DacBits, vref: f32) -> Self { + Self { + regs, + pin, + bits, + vref, + } + } + + /// Destruct the DAC abstraction and give back all owned peripherals. + pub fn free(self) -> (pac::DAC, P) { + (self.regs, self.pin) + } + + /// Enable the DAC. + pub fn enable(&mut self, apb1: &mut APB1) { + apb1.enr().modify(|_, w| w.dac1en().enabled()); + self.regs.cr.modify(|_, w| match Self::CHANNEL { + Channel::One => w.en1().enabled(), + Channel::Two => w.en2().enabled(), + }); + } + + /// Disable the DAC + pub fn disable(&mut self, apb1: &mut APB1) { + self.regs.cr.modify(|_, w| match Self::CHANNEL { + Channel::One => w.en1().disabled(), + Channel::Two => w.en2().disabled(), + }); + apb1.enr().modify(|_, w| w.dac1en().disabled()); + } + + /// Set the DAC value as an integer. + pub fn set_value(&mut self, val: u32) { + match Self::CHANNEL { + Channel::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) }), + }, + Channel::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. + pub 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); + } + + // Select and activate a trigger. See f303 Reference manual, section 16.5.4. + pub fn set_trigger(&mut self, trigger: Trigger) { + self.regs.cr.modify(|_, w| match Self::CHANNEL { + Channel::One => { + w.ten1().enabled(); + unsafe { w.tsel1().bits(trigger.bits()) } + } + Channel::Two => { + w.ten2().enabled(); + w.tsel2().bits(trigger.bits()) + } + }); + } + + /// Takes the value stored via set_value or set_voltage and converts it to + /// output on the Pin. + /// + /// For this to have an effect, you need to first enable the software + /// trigger with `set_trigger`. + pub fn trigger_software_trigger(&mut self) { + self.regs.swtrigr.write(|w| match Self::CHANNEL { + Channel::One => w.swtrig1().enabled(), + Channel::Two => w.swtrig2().enabled(), + }); + } +} + +impl SingleChannelDac for Dac

{ + type Error = Infallible; + + /// Set the DAC value as an integer. + fn try_set_value(&mut self, val: u32) -> Result<(), Infallible> { + self.set_value(val); + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 338dc755c..824dda466 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,8 @@ 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(any(feature = "stm32f302", feature = "stm32f303"))]