Skip to content

Commit 83742a1

Browse files
committed
Adds DMA support for ADC
1 parent 42bb6ec commit 83742a1

File tree

5 files changed

+313
-50
lines changed

5 files changed

+313
-50
lines changed

examples/adc-dma-circ.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//! ADC interface circular DMA RX transfer test
2+
3+
#![no_main]
4+
#![no_std]
5+
6+
use panic_halt as _;
7+
8+
use cortex_m::{asm, singleton};
9+
10+
use stm32f1xx_hal::{
11+
prelude::*,
12+
pac,
13+
adc,
14+
dma::Half,
15+
};
16+
use cortex_m_rt::entry;
17+
18+
#[entry]
19+
fn main() -> ! {
20+
// Aquire peripherals
21+
let p = pac::Peripherals::take().unwrap();
22+
let mut flash = p.FLASH.constrain();
23+
let mut rcc = p.RCC.constrain();
24+
25+
// Configure ADC clocks
26+
// Default value is the slowest possible ADC clock: PCLK2 / 8. Meanwhile ADC
27+
// clock is configurable. So its frequency may be tweaked to meet certain
28+
// practical needs. User specified value is be approximated using supported
29+
// prescaler values 2/4/6/8.
30+
let _clocks = rcc.cfgr.adcclk(2.mhz()).freeze(&mut flash.acr);
31+
32+
let dma_ch1 = p.DMA1.split(&mut rcc.ahb).1;
33+
34+
// Setup ADC
35+
let adc1 = adc::Adc::adc1(p.ADC1, &mut rcc.apb2);
36+
37+
// Setup GPIOA
38+
let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
39+
40+
// Configure pa0 as an analog input
41+
let adc_ch0 = gpioa.pa0.into_analog(&mut gpioa.crl);
42+
43+
let adc_dma = adc1.with_dma(adc_ch0, dma_ch1);
44+
let buf = singleton!(: [[u16; 8]; 2] = [[0; 8]; 2]).unwrap();
45+
46+
let mut circ_buffer = adc_dma.circ_read(buf);
47+
48+
while circ_buffer.readable_half().unwrap() != Half::First {}
49+
50+
let _first_half = circ_buffer.peek(|half, _| *half).unwrap();
51+
52+
while circ_buffer.readable_half().unwrap() != Half::Second {}
53+
54+
let _second_half = circ_buffer.peek(|half, _| *half).unwrap();
55+
56+
let (_buf, adc_dma) = circ_buffer.stop();
57+
let (_adc1, _adc_ch0, _dma_ch1) = adc_dma.split();
58+
asm::bkpt();
59+
60+
loop {}
61+
}

examples/adc-dma-rx.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//! ADC interface DMA RX transfer test
2+
3+
#![no_main]
4+
#![no_std]
5+
6+
use panic_halt as _;
7+
8+
use cortex_m::{asm, singleton};
9+
10+
use stm32f1xx_hal::{
11+
prelude::*,
12+
pac,
13+
adc,
14+
};
15+
use cortex_m_rt::entry;
16+
17+
#[entry]
18+
fn main() -> ! {
19+
// Aquire peripherals
20+
let p = pac::Peripherals::take().unwrap();
21+
let mut flash = p.FLASH.constrain();
22+
let mut rcc = p.RCC.constrain();
23+
24+
// Configure ADC clocks
25+
// Default value is the slowest possible ADC clock: PCLK2 / 8. Meanwhile ADC
26+
// clock is configurable. So its frequency may be tweaked to meet certain
27+
// practical needs. User specified value is be approximated using supported
28+
// prescaler values 2/4/6/8.
29+
let _clocks = rcc.cfgr.adcclk(2.mhz()).freeze(&mut flash.acr);
30+
31+
let dma_ch1 = p.DMA1.split(&mut rcc.ahb).1;
32+
33+
// Setup ADC
34+
let adc1 = adc::Adc::adc1(p.ADC1, &mut rcc.apb2);
35+
36+
// Setup GPIOA
37+
let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
38+
39+
// Configure pa0 as an analog input
40+
let adc_ch0 = gpioa.pa0.into_analog(&mut gpioa.crl);
41+
42+
let adc_dma = adc1.with_dma(adc_ch0, dma_ch1);
43+
let buf = singleton!(: [u16; 8] = [0; 8]).unwrap();
44+
45+
let (_buf, adc_dma) = adc_dma.read(buf).wait();
46+
asm::bkpt();
47+
48+
let (_adc1, _adc_ch0, _dma_ch1) = adc_dma.split();
49+
asm::bkpt();
50+
51+
loop {}
52+
}

src/adc.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::gpio::Analog;
66
use crate::gpio::{gpioa, gpiob, gpioc};
77
use crate::rcc::APB2;
88
use crate::time::Hertz;
9+
use crate::dma::{Receive, TransferPayload, dma1::C1, CircBuffer, Transfer, W, RxDma};
10+
use core::sync::atomic::{self, Ordering};
911

1012
use crate::stm32::ADC1;
1113
#[cfg(any(
@@ -490,3 +492,118 @@ adc_hal! {
490492
adc2rst
491493
),
492494
}
495+
496+
pub struct AdcPayload<PIN: Channel<ADC1>> {
497+
adc: Adc<ADC1>,
498+
pin: PIN,
499+
}
500+
501+
pub type AdcDma<PIN> = RxDma<AdcPayload<PIN>, C1>;
502+
503+
impl<PIN> Receive for AdcDma<PIN> where PIN: Channel<ADC1> {
504+
type RxChannel = C1;
505+
type TransmittedWord = u16;
506+
}
507+
508+
impl<PIN> TransferPayload for AdcDma<PIN> where PIN: Channel<ADC1> {
509+
fn start(&mut self) {
510+
self.channel.start();
511+
self.payload.adc.rb.cr2.modify(|_, w| w.cont().set_bit());
512+
self.payload.adc.rb.cr2.modify(|_, w| w.adon().set_bit());
513+
}
514+
fn stop(&mut self) {
515+
self.channel.stop();
516+
self.payload.adc.rb.cr2.modify(|_, w| w.cont().clear_bit());
517+
}
518+
}
519+
520+
impl Adc<ADC1> {
521+
pub fn with_dma<PIN>(mut self, pin: PIN, dma_ch: C1) -> AdcDma<PIN>
522+
where
523+
PIN: Channel<ADC1, ID = u8>,
524+
{
525+
self.rb.cr1.modify(|_, w| w.discen().clear_bit());
526+
self.rb.cr2.modify(|_, w| w.align().bit(self.align.into()));
527+
self.set_chan_smps(PIN::channel());
528+
self.rb.sqr3.modify(|_, w| unsafe { w.sq1().bits(PIN::channel()) });
529+
self.rb.cr2.modify(|_, w| w.dma().set_bit());
530+
531+
let payload = AdcPayload {
532+
adc: self,
533+
pin,
534+
};
535+
RxDma {
536+
payload,
537+
channel: dma_ch,
538+
}
539+
}
540+
}
541+
542+
impl<PIN> AdcDma<PIN> where PIN: Channel<ADC1> {
543+
pub fn split(mut self) -> (Adc<ADC1>, PIN, C1) {
544+
self.stop();
545+
546+
let AdcDma {payload, channel} = self;
547+
payload.adc.rb.cr2.modify(|_, w| w.dma().clear_bit());
548+
payload.adc.rb.cr1.modify(|_, w| w.discen().set_bit());
549+
550+
(payload.adc, payload.pin, channel)
551+
}
552+
}
553+
554+
impl<B, PIN> crate::dma::CircReadDma<B, u16> for AdcDma<PIN>
555+
where
556+
B: AsMut<[u16]>,
557+
PIN: Channel<ADC1>,
558+
{
559+
fn circ_read(mut self, buffer: &'static mut [B; 2]) -> CircBuffer<B, Self> {
560+
{
561+
let buffer = buffer[0].as_mut();
562+
self.channel.set_peripheral_address(unsafe{ &(*ADC1::ptr()).dr as *const _ as u32 }, false);
563+
self.channel.set_memory_address(buffer.as_ptr() as u32, true);
564+
self.channel.set_transfer_length(buffer.len() * 2);
565+
566+
atomic::compiler_fence(Ordering::Release);
567+
568+
self.channel.ch().cr.modify(|_, w| { w
569+
.mem2mem() .clear_bit()
570+
.pl() .medium()
571+
.msize() .bit16()
572+
.psize() .bit16()
573+
.circ() .set_bit()
574+
.dir() .clear_bit()
575+
});
576+
}
577+
578+
self.start();
579+
580+
CircBuffer::new(buffer, self)
581+
}
582+
}
583+
584+
impl<B, PIN> crate::dma::ReadDma<B, u16> for AdcDma<PIN>
585+
where
586+
B: AsMut<[u16]>,
587+
PIN: Channel<ADC1>,
588+
{
589+
fn read(mut self, buffer: &'static mut B) -> Transfer<W, &'static mut B, Self> {
590+
{
591+
let buffer = buffer.as_mut();
592+
self.channel.set_peripheral_address(unsafe{ &(*ADC1::ptr()).dr as *const _ as u32 }, false);
593+
self.channel.set_memory_address(buffer.as_ptr() as u32, true);
594+
self.channel.set_transfer_length(buffer.len());
595+
}
596+
atomic::compiler_fence(Ordering::Release);
597+
self.channel.ch().cr.modify(|_, w| { w
598+
.mem2mem() .clear_bit()
599+
.pl() .medium()
600+
.msize() .bit16()
601+
.psize() .bit16()
602+
.circ() .clear_bit()
603+
.dir() .clear_bit()
604+
});
605+
self.start();
606+
607+
Transfer::w(buffer, self)
608+
}
609+
}

0 commit comments

Comments
 (0)