Skip to content

Commit 35ee921

Browse files
committed
new ADC with trigger from timer and use of 2xdma
1 parent a674aaa commit 35ee921

File tree

3 files changed

+341
-1
lines changed

3 files changed

+341
-1
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// has been tested on nucleo-32 with STM32G031
2+
// command build: cargo build --example adc_ext_trig_double_dma_serial --features stm32g031
3+
// command run: cargo run --example adc_ext_trig_double_dma_serial --features stm32g031
4+
5+
#![deny(warnings)]
6+
// #![deny(unsafe_code)]
7+
#![no_main]
8+
#![no_std]
9+
10+
extern crate cortex_m;
11+
extern crate cortex_m_rt as rt;
12+
use cortex_m_semihosting::{ hprintln};
13+
14+
extern crate nb;
15+
extern crate panic_halt;
16+
extern crate stm32g0xx_hal as hal;
17+
extern crate stm32g0;
18+
19+
20+
use hal::prelude::*;
21+
use hal::stm32;
22+
use hal::serial::*;
23+
use rt::entry;
24+
25+
use core::cell::{RefCell};
26+
use cortex_m::{ interrupt::Mutex};
27+
28+
use crate::hal::{
29+
stm32::{interrupt, Interrupt},
30+
};
31+
use hal::analog::adc::{Precision, SampleTime, InjTrigSource}; //, VTemp
32+
33+
use hal::dma::{self, Channel, Target};
34+
35+
use crate::hal::analog::adc::InjectMode;
36+
use crate::hal::analog::adc::DmaMode;
37+
38+
// Make dma globally available
39+
static G_DMA: Mutex<RefCell<Option<hal::dma::Channels>>> = Mutex::new(RefCell::new(None));
40+
41+
const BUFFER_SIZE: u16 = 4;
42+
// Make the buffer pointer globally available
43+
static G_DMA_BUFFER_ADDR: Mutex<RefCell<Option<u32>>> = Mutex::new(RefCell::new(None));
44+
45+
#[interrupt]
46+
fn DMA_CHANNEL1() {
47+
static mut DMA: Option<hal::dma::Channels> = None;
48+
static mut DMA_BUFFER_ADDR: Option<u32> = None;
49+
50+
let dma_ch = DMA.get_or_insert_with(|| {
51+
cortex_m::interrupt::free(|cs| {
52+
// Move dma here, leaving a None in its place
53+
G_DMA.borrow(cs).replace(None).unwrap()
54+
})
55+
});
56+
57+
let dma_buf_addr = DMA_BUFFER_ADDR.get_or_insert_with(|| {
58+
cortex_m::interrupt::free(|cs| {
59+
// Move dma buffer pointer here, leaving a None in its place
60+
G_DMA_BUFFER_ADDR.borrow(cs).replace(None).unwrap()
61+
})
62+
});
63+
64+
let tx_dma_buf_first_addr: u32 = *dma_buf_addr;
65+
let tx_dma_buf_second_addr: u32 = *dma_buf_addr + (BUFFER_SIZE) as u32;
66+
// Address is in byte, value in 2Bytes, this is why second dma buffer ist added with BUFFER_SIZE
67+
// and not BUFFER_SIZE/2
68+
69+
70+
unsafe {
71+
let dma = &(*stm32g0::stm32g031::DMA::ptr());
72+
let htif1 = dma.isr.read().htif1().bit();
73+
let tcif1 = dma.isr.read().tcif1().bit();
74+
// set the global clear bit of DMA channel1
75+
dma.ifcr.write(|w| w.cgif1().set_bit());
76+
77+
dma_ch.ch2.disable();
78+
dma_ch.ch2.set_transfer_length(BUFFER_SIZE as u16);
79+
if htif1 == true {
80+
dma_ch.ch2.set_memory_address(tx_dma_buf_first_addr, true);
81+
dma_ch.ch2.enable();
82+
// hprintln!("DMA_CHANNEL1 half transfer compleated {:?} {:?}", htif1, tx_dma_buf_first_addr).unwrap();
83+
} else if tcif1 == true {
84+
dma_ch.ch2.set_memory_address(tx_dma_buf_second_addr, true);
85+
dma_ch.ch2.enable();
86+
// hprintln!("DMA_CHANNEL1 transfer compleated {:?} {:?}", tcif1, tx_dma_buf_second_addr).unwrap();
87+
}
88+
}
89+
}
90+
91+
#[entry]
92+
fn main() -> ! {
93+
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
94+
let mut rcc = dp.RCC.constrain();
95+
96+
let gpioa = dp.GPIOA.split(&mut rcc);
97+
98+
let mut timer = dp.TIM2.timer(&mut rcc);
99+
100+
let usart1 = dp
101+
.USART1
102+
.usart(
103+
gpioa.pa9, // TX: pa9, => CN3 Pin-D5
104+
gpioa.pa10, // RX: pa10, => CN3 Pin-D4
105+
FullConfig::default().baudrate(460800.bps())
106+
.fifo_enable(), // enable fifo, so that dma can fill it fast, otherwise it may not finish before ch1 is requested again
107+
&mut rcc,
108+
)
109+
.unwrap();
110+
111+
// DMA example
112+
//==================================================
113+
let adc_buffer1: [u16; BUFFER_SIZE as usize] = [0;BUFFER_SIZE as usize];
114+
115+
let mut dma = dp.DMA.split(&mut rcc, dp.DMAMUX);
116+
117+
let adc_ptr = unsafe { &(*stm32g0::stm32g031::ADC::ptr()) };
118+
let adc_data_register_addr = &adc_ptr.dr as *const _ as u32;
119+
120+
let adc_buffer1_addr: u32 = adc_buffer1.as_ptr() as u32;
121+
122+
dma.ch1.set_word_size(dma::WordSize::BITS16);
123+
dma.ch1.set_direction(dma::Direction::FromPeripheral);
124+
dma.ch1.set_memory_address(adc_buffer1_addr, true);
125+
dma.ch1.set_peripheral_address(adc_data_register_addr, false);
126+
dma.ch1.set_transfer_length(adc_buffer1.len() as u16);
127+
128+
hprintln!("adc_data_register_addr {:?}", adc_buffer1_addr).unwrap(); // will output addr in dec
129+
// in gdb read the data bytes with: x /32xh 0x??????? (last is addr in hex)
130+
// or put addr in dec format: x /32xh 536878092
131+
// https://sourceware.org/gdb/current/onlinedocs/gdb/Memory.html
132+
133+
// dma ch1 reads from ADC register into memory
134+
dma.ch1.select_peripheral(stm32g0xx_hal::dmamux::DmaMuxIndex::ADC);
135+
// The dma continuesly fills the buffer, when its full, it starts over again
136+
dma.ch1.set_circular_mode(true);
137+
138+
// Enabel dma irq for half and full buffer, when reached, so that the second dma ch2 can be started
139+
dma.ch1.listen(hal::dma::Event::HalfTransfer);
140+
dma.ch1.listen(hal::dma::Event::TransferComplete);
141+
142+
let (mut tx, mut _rx) = usart1.split();
143+
144+
let usart = unsafe { &(*stm32::USART1::ptr()) };
145+
let tx_data_register_addr = &usart.tdr as *const _ as u32;
146+
147+
dma.ch2.set_direction(dma::Direction::FromMemory);
148+
dma.ch2.set_peripheral_address(tx_data_register_addr, false);
149+
dma.ch2.set_word_size(hal::dma::WordSize::BITS8);
150+
151+
dma.ch2.select_peripheral(tx.dmamux());
152+
153+
tx.enable_dma();
154+
155+
// start dma transfer
156+
dma.ch1.enable();
157+
158+
cortex_m::interrupt::free(|cs| *G_DMA.borrow(cs).borrow_mut() = Some(dma));
159+
cortex_m::interrupt::free(|cs| *G_DMA_BUFFER_ADDR.borrow(cs).borrow_mut() = Some(adc_buffer1_addr));
160+
161+
162+
163+
//==================================================
164+
// Set up adc
165+
166+
let mut adc = dp.ADC.constrain(&mut rcc);
167+
adc.set_sample_time(SampleTime::T_80);
168+
adc.set_precision(Precision::B_12);
169+
let mut pa3 = gpioa.pa5.into_analog();
170+
let u_raw: u32 = adc.read(&mut pa3).expect("adc read failed");
171+
let u = u_raw.saturating_sub(32) as f32 / 4_096_f32 * 3.3;
172+
hprintln!("u: {:.4} V ", u).unwrap();
173+
174+
adc.set_oversamling_ratio(3);
175+
adc.set_oversamling_shift(4);
176+
adc.oversamling_enable();
177+
adc.prepare_injected(&mut pa3, InjTrigSource::TRG_2);
178+
adc.start_injected();
179+
180+
181+
// Enable timer to trigger external sources in mms value of cr2
182+
// 011: Compare Pulse - The trigger output send a positive pulse when the CC1IF flag is to be
183+
// set (even if it was already high), as soon as a capture or a compare match occurred.
184+
// ouput is (TRGO)
185+
// according to reference manual chapter 22.4.2
186+
// this is only available on timer TIM2, TIM3, TIM4 and TIM1
187+
unsafe{
188+
// get pointer of timer 2
189+
let tim = &(*stm32g0::stm32g031::TIM2::ptr());
190+
//
191+
tim.cr2.modify(|_,w| w.mms().bits(3 as u8));
192+
}
193+
194+
// enable dma to be called, when adc is ready to read
195+
adc.dma_enable(true);
196+
adc.dma_circualr_mode(true);
197+
198+
// don't enabel the timer bevor the dma
199+
// Set up a timer expiring after
200+
timer.start(50.us());
201+
timer.listen();
202+
203+
//enable DMA_CHANNEL1 interrupt
204+
unsafe {
205+
cortex_m::peripheral::NVIC::unmask(Interrupt::DMA_CHANNEL1);
206+
}
207+
208+
loop {
209+
210+
}
211+
}

src/analog/adc.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ pub enum AsyncClockDiv {
7272
AsyncD256 = 8,
7373
}
7474

75+
/// ADC injected trigger source selection
76+
#[derive(Copy, Clone, PartialEq)]
77+
pub enum InjTrigSource {
78+
TRG_0 = 0b000, // TIM1_TRGO2
79+
TRG_1 = 0b001, // TIM1_CC4
80+
TRG_2 = 0b010, // TIM2_TRGO
81+
TRG_3 = 0b011, // TIM3_TRGO
82+
TRG_4 = 0b100, // TIM15_TRGO
83+
TRG_5 = 0b101, // TIM6_TRGO
84+
TRG_6 = 0b110, // TIM4_TRGO
85+
TRG_7 = 0b111, // EXTI11
86+
}
87+
88+
7589
/// Analog to Digital converter interface
7690
pub struct Adc {
7791
rb: ADC,
@@ -178,6 +192,44 @@ impl Adc {
178192
pub fn release(self) -> ADC {
179193
self.rb
180194
}
195+
196+
/// The nuber of bits, the oversampling result is shifted in bits at the end of oversampling
197+
pub fn set_oversamling_shift(&mut self, nrbits:u8) {
198+
self.rb.cfgr2.modify(|_, w| unsafe {w.ovss().bits(nrbits)});
199+
}
200+
201+
/// Oversampling of adc according to datasheet of stm32g0, when oversampling is enabled
202+
/// 000: 2x
203+
/// 001: 4x
204+
/// 010: 8x
205+
/// 011: 16x
206+
/// 100: 32x
207+
/// 101: 64x
208+
/// 110: 128x
209+
/// 111: 256x
210+
211+
pub fn set_oversamling_ratio(&mut self, multyply:u8) {
212+
self.rb.cfgr2.modify(|_, w| unsafe {w.ovsr().bits(multyply)});
213+
}
214+
215+
pub fn oversamling_enable(&mut self) {
216+
self.rb.cfgr2.modify(|_, w| unsafe {w.ovse().set_bit()});
217+
}
218+
219+
pub fn start_injected(&mut self) {
220+
self.rb.cr.modify(|_,w| w.adstart().set_bit());
221+
// ADSTART bit is cleared to 0 bevor using this function
222+
// enable self.rb.isr.eos() flag is set after each converstion
223+
self.rb.ier.modify(|_, w| w.eocie().set_bit()); // end of sequence interupt enable
224+
}
225+
226+
227+
pub fn stop_injected(&mut self) { // ?????? or is it reset after each conversion?
228+
// ADSTART bit is cleared to 0 bevor using this function
229+
// disable EOS interrupt
230+
// maybe self.rb.cr.adstp().set_bit() must be performed before interrupt is disabled + wait abortion
231+
self.rb.ier.modify(|_, w| w.eocie().clear_bit()); // end of sequence interupt disable
232+
}
181233
}
182234

183235
pub trait AdcExt {
@@ -190,6 +242,83 @@ impl AdcExt for ADC {
190242
}
191243
}
192244

245+
pub trait InjectMode<ADC, Pin: Channel<ADC>> {
246+
/// Error type returned by ADC methods
247+
type Error;
248+
fn prepare_injected(&mut self, _pin: &mut Pin, triger_source: InjTrigSource);
249+
}
250+
251+
impl<PIN> InjectMode<Adc, PIN> for Adc
252+
where
253+
// WORD: From<u16>,
254+
PIN: Channel<Adc, ID = u8>,
255+
{
256+
type Error = ();
257+
258+
fn prepare_injected(&mut self, _pin: &mut PIN, triger_source: InjTrigSource){
259+
// set the clock mode to synchronous one
260+
// self.rb.cfgr2.ckmode().bits(CLCOKMODE) // CLOCKMODE = 01 or 10 for PCLK/2 or PCLK/4
261+
262+
263+
// self.set_injected_trigger_source(triger_source as InjTrigSource);
264+
self.rb.cfgr1.modify(|_, w| unsafe {
265+
w.exten()
266+
.bits(1)
267+
.extsel()
268+
.bits(triger_source as u8)
269+
});
270+
271+
self.rb.cfgr1.modify(|_, w| unsafe {
272+
w.res() // set ADC resolution bits (ADEN must be =0)
273+
.bits(self.precision as u8)
274+
.align() // set alignment bit is (ADSTART must be 0)
275+
.bit(self.align == Align::Left)
276+
});
277+
278+
self.power_up();
279+
280+
self.rb
281+
.smpr // set sampling time set 1 (ADSTART must be 0)
282+
.modify(|_, w| unsafe { w.smp1().bits(self.sample_time as u8) });
283+
284+
self.rb
285+
.chselr() // set activ channel acording chapter 15.12.9 (ADC_CFGR1; CHSELRMOD=0)
286+
.modify(|_, w| unsafe { w.chsel().bits(1 << PIN::channel()) });
287+
288+
}
289+
290+
291+
}
292+
293+
pub trait DmaMode<ADC> {
294+
/// Error type returned by ADC methods
295+
type Error;
296+
fn dma_enable(&mut self, enable: bool);
297+
fn dma_circualr_mode(&mut self, enable: bool);
298+
}
299+
300+
impl DmaMode<Adc> for Adc {
301+
302+
type Error = ();
303+
304+
fn dma_enable(&mut self, enable: bool) {
305+
if enable {
306+
self.rb.cfgr1.modify(|_,w| w.dmaen().set_bit()); // enable dma beeing called
307+
} else {
308+
self.rb.cfgr1.modify(|_,w| w.dmaen().clear_bit()); // disable dma beeing called
309+
}
310+
}
311+
312+
fn dma_circualr_mode(&mut self, enable: bool) {
313+
if enable {
314+
self.rb.cfgr1.modify(|_,w| w.dmacfg().set_bit()); // activate circular mode
315+
} else {
316+
self.rb.cfgr1.modify(|_,w| w.dmacfg().clear_bit()); // disable circular mode
317+
}
318+
}
319+
}
320+
321+
193322
impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc
194323
where
195324
WORD: From<u16>,

src/dma.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ pub trait Channel: private::Channel {
168168
}
169169

170170
/// Set the word size.
171-
fn set_word_size<W>(&mut self, wsize: WordSize) {
171+
fn set_word_size(&mut self, wsize: WordSize) {
172172
self.ch().cr.modify(|_, w| unsafe {
173173
w.psize().bits(wsize as u8);
174174
w.msize().bits(wsize as u8)

0 commit comments

Comments
 (0)