Created
November 19, 2019 11:38
-
-
Save strom-und-spiele/8155f19258adfd66214d2d0aac956a43 to your computer and use it in GitHub Desktop.
using adc, dma and dac with the stm32f3discovery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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() | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Edit:
into_analog
is supported sincestm32f3xx_hal v0.4.0.
, so the following is deprecated.Note that as of now
into_analog
is not available. It can be added tostm32f3xx_hal
by: