Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adc: multichannel continuous conversion #214

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Add `QeiOptions` struct to configure slave mode and auto reload value of QEI interface
- Implement multiplication and division for frequency wrappers (#193)
- Add support for CRC
- ADC multichannel continuous conversion

### Changed

Expand Down
83 changes: 68 additions & 15 deletions examples/adc-dma-rx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@
#![no_main]
#![no_std]

use panic_halt as _;
use panic_semihosting as _;

use cortex_m::{asm, singleton};
use cortex_m::singleton;
use cortex_m_semihosting::hprintln;

use cortex_m_rt::entry;
use stm32f1xx_hal::{adc, pac, prelude::*};
use stm32f1xx_hal::gpio::gpioa::{PA0, PA2};
use stm32f1xx_hal::{adc, gpio::Analog, pac, prelude::*};

pub struct AdcPins(PA0<Analog>, PA2<Analog>);
impl adc::SetChannels<AdcPins> for adc::Adc<pac::ADC1> {
fn set_samples(&mut self) {
self.set_channel_sample_time(0, adc::SampleTime::T_28);
self.set_channel_sample_time(2, adc::SampleTime::T_28);
}
fn set_sequence(&mut self) {
self.set_regular_sequence(&[0, 2, 0, 2]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the sequence 4 samples?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No specific reason, I started from rustdoc example :)

stm32f1xx-hal/src/adc.rs

Lines 531 to 544 in 1357e5a

/// Set channel sequence and sample times for custom pins
///
/// Example:
/// ```rust, ignore
/// pub struct AdcPins(PA0<Analog>, PA2<Analog>);
/// impl SetChannels<AdcPins> for Adc<ADC1> {
/// fn set_samples(&mut self) {
/// self.set_channel_sample_time(0, adc::SampleTime::T_28);
/// self.set_channel_sample_time(2, adc::SampleTime::T_28);
/// }
/// fn set_sequence(&mut self) {
/// self.set_regular_sequence(&[0, 2, 0, 2]);
/// }
/// }

}
}

#[entry]
fn main() -> ! {
Expand All @@ -34,21 +47,61 @@ fn main() -> ! {

// Configure pa0 as an analog input
let adc_ch0 = gpioa.pa0.into_analog(&mut gpioa.crl);
let adc_ch2 = gpioa.pa2.into_analog(&mut gpioa.crl);

// single-channel continuous conversion
let (adc1, adc_ch0, dma_ch1) = {
let adc_dma = adc1.with_dma(adc_ch0, dma_ch1);
let buf = singleton!(: [u16; 8] = [0; 8]).unwrap();

// The read method consumes the buf and self, starts the adc and dma transfer and returns a
// RxDma struct. The wait method consumes the RxDma struct, waits for the whole transfer to
// be completed and then returns the updated buf and underlying adc_dma struct. For non
// blocking, one can call the is_done method of RxDma and only call wait after that method
// returns true.
let (_buf, adc_dma) = adc_dma.read(buf).wait();
hprintln!("single-channel continuous conversion={:?}", _buf).ok();

// Consumes the AdcDma struct, restores adc configuration to previous state and returns the
// Adc struct in normal mode.
adc_dma.split()
};

// multi-channel single-shot conversion
let (adc1, AdcPins(adc_ch0, adc_ch2), dma_ch1) = {
let pins = AdcPins(adc_ch0, adc_ch2);
let adc_dma = adc1.with_scan_dma::<_, adc::Single>(pins, dma_ch1);

let buf = singleton!(: [u16; 4] = [0; 4]).unwrap();
let (_buf, adc_dma) = adc_dma.read(buf).wait();
hprintln!(
"multi-channel single-shot conversion, exact buffer={:?}",
_buf
)
.ok();

let buf = singleton!(: [u16; 8] = [0; 8]).unwrap();
let (_buf, adc_dma) = adc_dma.read(buf).wait();
hprintln!(
"multi-channel single-shot conversion, larger buffer={:?}",
_buf
)
.ok();

adc_dma.split()
};

let adc_dma = adc1.with_dma(adc_ch0, dma_ch1);
let buf = singleton!(: [u16; 8] = [0; 8]).unwrap();
// multi-channel continuous conversion
let (_adc1, AdcPins(_adc_ch0, _adc_ch2), _dma_ch1) = {
let pins = AdcPins(adc_ch0, adc_ch2);
let adc_dma = adc1.with_scan_dma::<_, adc::Continuous>(pins, dma_ch1);
let buf = singleton!(: [u16; 8] = [0; 8]).unwrap();

// The read method consumes the buf and self, starts the adc and dma transfer and returns a
// RxDma struct. The wait method consumes the RxDma struct, waits for the whole transfer to be
// completed and then returns the updated buf and underlying adc_dma struct. For non blocking,
// one can call the is_done method of RxDma and only call wait after that method returns true.
let (_buf, adc_dma) = adc_dma.read(buf).wait();
asm::bkpt();
let (_buf, adc_dma) = adc_dma.read(buf).wait();
hprintln!("multi-channel continuous conversion={:?}", _buf).ok();

// Consumes the AdcDma struct, restores adc configuration to previous state and returns the
// Adc struct in normal mode.
let (_adc1, _adc_ch0, _dma_ch1) = adc_dma.split();
asm::bkpt();
adc_dma.split()
};

loop {}
}
47 changes: 41 additions & 6 deletions src/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,24 @@ use crate::pac::ADC3;

/// Continuous mode
pub struct Continuous;
/// Scan mode
pub struct Scan;
/// Single conversion mode
pub struct Single;

pub trait ConversionMode {
fn is_continuous() -> bool;
}

impl ConversionMode for Continuous {
fn is_continuous() -> bool {
true
}
}

impl ConversionMode for Single {
fn is_continuous() -> bool {
false
}
}

/// ADC configuration
pub struct Adc<ADC> {
Expand Down Expand Up @@ -344,6 +360,11 @@ macro_rules! adc_hal {
self.rb.sqr1.modify(|_, w| w.l().bits((len-1) as u8));
}

#[allow(unused)]
fn sequence_len(&self) -> usize {
self.rb.sqr1.read().l().bits() as usize + 1
}

/**
Performs an ADC conversion

Expand Down Expand Up @@ -567,7 +588,7 @@ impl<PINS> TransferPayload for AdcDma<PINS, Continuous> {
}
}

impl<PINS> TransferPayload for AdcDma<PINS, Scan> {
impl<PINS> TransferPayload for AdcDma<PINS, Single> {
fn start(&mut self) {
self.channel.start();
self.payload.adc.rb.cr2.modify(|_, w| w.adon().set_bit());
Expand Down Expand Up @@ -601,7 +622,7 @@ impl Adc<ADC1> {
}
}

pub fn with_scan_dma<PINS>(mut self, pins: PINS, dma_ch: C1) -> AdcDma<PINS, Scan>
pub fn with_scan_dma<PINS, T>(mut self, pins: PINS, dma_ch: C1) -> AdcDma<PINS, T>
where
Self: SetChannels<PINS>,
{
Expand Down Expand Up @@ -651,7 +672,7 @@ where
}
}

impl<PINS> AdcDma<PINS, Scan>
impl<PINS> AdcDma<PINS, Single>
where
Self: TransferPayload,
{
Expand Down Expand Up @@ -709,15 +730,29 @@ impl<B, PINS, MODE> crate::dma::ReadDma<B, u16> for AdcDma<PINS, MODE>
where
Self: TransferPayload,
B: as_slice::AsMutSlice<Element = u16>,
MODE: ConversionMode,
{
fn read(mut self, buffer: &'static mut B) -> Transfer<W, &'static mut B, Self> {
{
let buffer = buffer.as_mut_slice();

let conversion_len = if MODE::is_continuous() {
buffer.len()
} else {
// for non-continous conversion, conversion sequence length should match with DMA
// transfer length, to prevent DMA from waiting for a conversion indefinitely
let sequence_len = self.payload.adc.sequence_len();
if buffer.len() < sequence_len {
panic!("short buffer on ADC conversion: buffer.len()={:?}, adc.seqeuence_len()={:?}", buffer.len(), sequence_len);
}
sequence_len
};

self.channel
.set_peripheral_address(unsafe { &(*ADC1::ptr()).dr as *const _ as u32 }, false);
self.channel
.set_memory_address(buffer.as_ptr() as u32, true);
self.channel.set_transfer_length(buffer.len());
self.channel.set_transfer_length(conversion_len);
}
atomic::compiler_fence(Ordering::Release);
self.channel.ch().cr.modify(|_, w| {
Expand Down