Skip to content

Commit a30cadc

Browse files
authored
Merge pull request #58 from oldsheep68/master
new ADC with trigger from timer and use of 2xdma
2 parents a674aaa + 3732abe commit a30cadc

File tree

3 files changed

+329
-1
lines changed

3 files changed

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

src/analog/adc.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ 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+
7588
/// Analog to Digital converter interface
7689
pub struct Adc {
7790
rb: ADC,
@@ -178,6 +191,48 @@ impl Adc {
178191
pub fn release(self) -> ADC {
179192
self.rb
180193
}
194+
195+
/// The nuber of bits, the oversampling result is shifted in bits at the end of oversampling
196+
pub fn set_oversamling_shift(&mut self, nrbits: u8) {
197+
self.rb
198+
.cfgr2
199+
.modify(|_, w| unsafe { w.ovss().bits(nrbits) });
200+
}
201+
202+
/// Oversampling of adc according to datasheet of stm32g0, when oversampling is enabled
203+
/// 000: 2x
204+
/// 001: 4x
205+
/// 010: 8x
206+
/// 011: 16x
207+
/// 100: 32x
208+
/// 101: 64x
209+
/// 110: 128x
210+
/// 111: 256x
211+
212+
pub fn set_oversamling_ratio(&mut self, multyply: u8) {
213+
self.rb
214+
.cfgr2
215+
.modify(|_, w| unsafe { w.ovsr().bits(multyply) });
216+
}
217+
218+
pub fn oversamling_enable(&mut self) {
219+
self.rb.cfgr2.modify(|_, w| unsafe { w.ovse().set_bit() });
220+
}
221+
222+
pub fn start_injected(&mut self) {
223+
self.rb.cr.modify(|_, w| w.adstart().set_bit());
224+
// ADSTART bit is cleared to 0 bevor using this function
225+
// enable self.rb.isr.eos() flag is set after each converstion
226+
self.rb.ier.modify(|_, w| w.eocie().set_bit()); // end of sequence interupt enable
227+
}
228+
229+
pub fn stop_injected(&mut self) {
230+
// ?????? or is it reset after each conversion?
231+
// ADSTART bit is cleared to 0 bevor using this function
232+
// disable EOS interrupt
233+
// maybe self.rb.cr.adstp().set_bit() must be performed before interrupt is disabled + wait abortion
234+
self.rb.ier.modify(|_, w| w.eocie().clear_bit()); // end of sequence interupt disable
235+
}
181236
}
182237

183238
pub trait AdcExt {
@@ -190,6 +245,74 @@ impl AdcExt for ADC {
190245
}
191246
}
192247

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