Skip to content

Instantly share code, notes, and snippets.

@strom-und-spiele
Created November 19, 2019 11:38
Show Gist options
  • Save strom-und-spiele/8155f19258adfd66214d2d0aac956a43 to your computer and use it in GitHub Desktop.
Save strom-und-spiele/8155f19258adfd66214d2d0aac956a43 to your computer and use it in GitHub Desktop.
using adc, dma and dac with the stm32f3discovery
#![no_std]
#![no_main]
//! VCU - Vehicle Control Unit on the stm32f3discovery (STM32 F303 VCT6)
extern crate panic_halt;
use cortex_m::asm;
use cortex_m_rt::entry;
//use volatile::ReadOnly;
use stm32f3xx_hal::{
prelude::*,
stm32::{self, RCC},
};
#[entry]
/// Main Thread
fn main() -> ! {
let peripherals = stm32::Peripherals::take().unwrap();
let mut rcc = peripherals.RCC.constrain();
let mut gpio_a = peripherals.GPIOA.split(&mut rcc.ahb);
let adc1 = peripherals.ADC1;
// ADC12 "split"
let adc12 = peripherals.ADC1_2;
// DMA1 "split"
let dma1 = peripherals.DMA1;
// set up pin pa0, pa1 as analog pin
let _adc1_in1_pin = gpio_a.pa0.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr);
let _adc1_in2_pin = gpio_a.pa1.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr);
let _dac1_out1_pin = gpio_a.pa4.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr);
let _dac1_out2_pin = gpio_a.pa5.into_analog(&mut gpio_a.moder, &mut gpio_a.pupdr);
// enabling adc, dac and dma clk (there is a crate only API for that)
let ahbenr = unsafe { &(*RCC::ptr()).ahbenr };
let apb1enr = unsafe { &(*RCC::ptr()).apb1enr };
apb1enr.modify(|_, w| w.dac1en().enabled());
ahbenr.modify(|_, w| w.adc12en().enabled().dma1en().enabled());
// set adc clk config accordingly with prescale = 4
unsafe {
adc12.ccr.modify(|_, w| w.ckmode().bits(0b11));
}
// init target for dma
let adc_values: [u16; 2] = [0x07ff; 2];
// Set up DMA channels
// The following sequence should be followed to configure a DMA channel x (where x is the channel number).
// 1. Set the peripheral register address in the DMA_CPARx register. The data will be moved
// from/ to this address to/ from the memory after the peripheral event.
dma1.ch1
.par
.write(|w| w.pa().bits(&adc1 as *const _ as u32 + 0x40));
// 2. Set the memory address in the DMA_CMARx register. The data will be written to or
// read from this memory after the peripheral event.
dma1.ch1
.mar
.write(|w| w.ma().bits(&adc_values as *const u16 as u32));
// 3. Configure the total number of data to be transferred in the DMA_CNDTRx register. After
// each peripheral event, this value will be decremented.
dma1.ch1.ndtr.write(|w| w.ndt().bits(2));
// 4. Configure the channel priority using the PL[1:0] bits in the DMA_CCRx register
dma1.ch1.cr.modify(|_, w| w.pl().medium());
// 5. Configure data transfer direction, circular mode, peripheral & memory incremented mode,
// peripheral & memory data size, and interrupt after half and/or full transfer in the
// DMA_CCRx register
#[rustfmt::skip]
dma1.ch1.cr.modify(|_, w| { w
.dir().from_peripheral()
.circ().enabled()
.pinc().disabled()
.minc().enabled()
.psize().bits16()
.msize().bits16()
.teie().disabled()
.tcie().disabled()
});
// 6. Activate the channel by setting the ENABLE bit in the DMA_CCRx register. As soon as
// the channel is enabled, it can serve any DMA request from the peripheral connected on
// the channel.
dma1.ch1.cr.modify(|_, w| w.en().enabled());
// Software procedure to calibrate the ADC
// 1. Ensure ADVREGEN[1:0]=01 (voltage regulator enabled)
// (deeppwd got merged as high bit in advregen - see ref manual)
if !(adc1.cr.read().deeppwd().bit_is_clear() & adc1.cr.read().advregen().bit_is_set()) {
// need to go through 00 first
adc1.cr
.modify(|_, w| w.deeppwd().clear_bit().advregen().clear_bit());
adc1.cr
.modify(|_, w| w.deeppwd().clear_bit().advregen().set_bit());
}
// and that ADC voltage regulator startup time has elapsed.
// (let's just wait a few clkcycles)
asm::delay(100);
//
// 2. Ensure that ADEN=0.
if adc1.cr.read().aden().bit_is_set() {
adc1.cr.modify(|_, w| w.aden().clear_bit());
}
// 3. Select the input mode for this calibration by setting ADCALDIF=0 (Single-ended input)
adc1.cr.modify(|_, w| w.adcaldif().clear_bit());
// 4. Start calibration by setting bit ADCAL=1.
adc1.cr.modify(|_, w| w.adcal().set_bit());
// 5. Wait for ADCAL to return to zero.
while adc1.cr.read().adcal().bit_is_set() {}
// (opt) return CALFACT_S[6:0] of ADCx_CALFACT register
// hprintln!("calfact_s = 0x{:x}", adc1.calfact.read().calfact_s().bits()).unwrap();
// adc clk is hclk/4
asm::delay(16);
// Software procedure to enable the ADC
// 1. Set ADEN=1.
// Note: ADEN bit cannot be set during ADCAL=1 and 4 ADC clock cycle after the ADCAL bit is
// cleared by hardware
adc1.cr.modify(|_, w| w.aden().set_bit());
// 2. Wait until ADRDY=1 (ADRDY is set after the ADC startup time).
// This can be done using the associated interrupt (setting ADRDYIE=1).
while adc1.isr.read().adrdy().bit_is_clear() {}
// # Set up ADC channel
// ## select sequence length
// v-- = sequence len - 1
adc1.sqr1.modify(|_, w| unsafe { w.l3().bits(1) });
// ## add channels to the sequence: ADC1_IN1 -v v-- ADC1_IN2
adc1.sqr1
.modify(|_, w| unsafe { w.sq1().bits(1).sq2().bits(2) });
// ## select sample time for each channel
adc1.smpr1
.modify(|_, w| unsafe { w.smp1().bits(0b111).smp2().bits(0b111) });
// # select conversion mode (continue after read)
adc1.cfgr.modify(|_, w| w.cont().set_bit());
// ignore if data is overwritten
adc1.cfgr.modify(|_, w| w.ovrmod().set_bit());
// enable DMA and set it to circular mode
adc1.cfgr
.modify(|_, w| w.dmaen().set_bit().dmacfg().set_bit());
// # start ADC converstion
adc1.cr.modify(|_, w| w.adstart().set_bit());
// ///////////// set up dac
let dac = stm32::DAC::ptr();
let dac_cr = unsafe { &(*dac).cr };
let dac_swtrigr = unsafe { &(*dac).swtrigr };
let dac_dhr12rd = unsafe { &(*dac).dhr12rd };
// enable both dac channels and triggers
#[rustfmt::skip]
dac_cr.modify(|_, w| { w
.ten1().enabled()
.ten2().enabled()
.tsel1().software()
.tsel2().software()
});
dac_cr.modify(|_, w| w.en1().enabled().en2().enabled());
loop {
#[rustfmt::skip]
dac_dhr12rd.write(|w| unsafe { w
.dacc1dhr().bits(adc_values[0])
.dacc2dhr().bits(adc_values[1])
});
#[rustfmt::skip]
dac_swtrigr.write(|w| w.
swtrig1().enabled()
.swtrig2().enabled()
);
}
}
@strom-und-spiele
Copy link
Author

strom-und-spiele commented Nov 19, 2019

Edit: into_analog is supported since stm32f3xx_hal v0.4.0., so the following is deprecated.


Note that as of now into_analog is not available. It can be added to stm32f3xx_hal by:

--- a/src/gpio.rs
+++ b/src/gpio.rs
@@ -28,6 +28,9 @@ pub struct PullDown;
 /// Pulled up input (type state)
 pub struct PullUp;
 
+/// Analog input (type state)
+pub struct Analog;
+
 /// Output mode (type state)
 pub struct Output<MODE> {
     _mode: PhantomData<MODE>,
@@ -197,6 +200,7 @@ macro_rules! gpio {
                 use crate::rcc::AHB;
                 use super::{
                     AF4, AF5, AF6, AF7, AF14, Floating, GpioExt, Input, OpenDrain, Output,
+                    Analog,
                     PullDown, PullUp, PushPull,
                     PXx, Gpio,
                 };
@@ -576,6 +580,26 @@ macro_rules! gpio {
 
                             $PXi { _mode: PhantomData }
                         }
+
+                        /// Configure the pin to operate as an analog input pin
+                        pub fn into_analog(
+                            self,
+                            moder: &mut MODER,
+                            pupdr: &mut PUPDR,
+                        ) -> $PXi<Analog> {
+                            let offset : u32 = 2 * $i;
+
+                            moder.moder().modify(|r, w| unsafe {
+                                w.bits(r.bits() | (0b11 << offset))
+                            });
+
+                            // set to floating as this is the only defined value
+                            pupdr.pupdr().modify(|r, w| unsafe {
+                                w.bits(r.bits() & !(0b11 << offset))
+                            });
+
+                            $PXi { _mode: PhantomData }
+                        }
                     }
 
                     impl $PXi<Output<OpenDrain>> {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment