-
Notifications
You must be signed in to change notification settings - Fork 53
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
Feature: DMAMUX support #12
Comments
Do you have some design in mind already? I have a project now where I need uart+dma, so maybe I can help a bit. |
I do not have any particular design in mind yet and I would be very happy with PRs about this feature. |
Do you by any chance have Rust code using DMAMUX without using HAL? Just the PAC. Otherwise I think that's something I will start with. I found this application note AN5224. |
Yes, I have written specialized ADC driver with hardcoded channels that is using DMAMUX. use cortex_m::asm;
use hal::stm32;
use hal::prelude::*;
use hal::analog::adc::{Precision, SampleTime};
use hal::timer::Timer;
use hal::time::Hertz;
pub fn init(adc_dma_buf: &[u16; 6]) {
// notice that G0 HAL does not have ADC DMA support yet, therefore it is done here manually
let rcc = unsafe { &(*stm32::RCC::ptr()) };
let adc = unsafe { &(*stm32::ADC::ptr()) };
let dma = unsafe { &(*stm32::DMA::ptr()) };
let dmamux = unsafe { &(*stm32::DMAMUX::ptr()) };
// set ADC clock source to System clock
rcc.ccipr.modify(|_, w| unsafe { w.adcsel().bits(0b00) });
// enable ADC clock
rcc.apbenr2.modify(|_, w| w.adcen().set_bit());
// async ADC clock can be sourced from SYSCLK, HSI16 and PLLP
// it can be prescaled using ADC_CCR PRESC[3:0] from 1 to 256
// however async clock introduces jitter if ADC conversions are triggered using
// external trigger like timer
// therefore here ADC clock is derived from APB PCLK/4
// notice that for this synchronous source ADC_CCR PRESC[3:0] cannot be used as prescaler
// if SYSCLOCK is 64 MHz and APB Prescaler is 1 then PCLK is also 64 MHz
// and ADC clock is PCLK/4 = 64/4 = 16 MHz
adc.cfgr2.write(|w| unsafe { w.ckmode().bits(0b10) });
// enable ADC voltage regulator
adc.cr.modify(|_, w| w.advregen().set_bit());
for _ in 0 .. 1_000 { asm::nop() }
// perform calibration
adc.cr.modify(|_, w| w.adcal().set_bit());
while adc.isr.read().eocal().bit_is_clear() {}
// configure ADC settings
unsafe {
adc.cfgr1.write(|w|
w
.res().bits(Precision::B_12 as u8)
// discontinuous mode, software or hardware event is needed to start sequence conversion
.discen().clear_bit()
// right aligned
.align().clear_bit()
// DMA circular mode
.dmacfg().set_bit()
// generate DMA requests
.dmaen().set_bit()
// select TIM6_TRGO as hardware trigger
.extsel().bits(0b101)
// enable hardware trigger on rising edge
.exten().bits(0b01)
);
// set sampling time to 40
// tconv = Sampling time + 12.5 x ADC clock cycles
// with 16 MHz clock it is:
// tconv = (40 + 12.5) * ADC clock cycles = 92.5 * 0.063 = 3.281 us per channel
adc.smpr.modify(|_, w| w.smp1().bits(SampleTime::T_40 as u8));
}
// select ADC channels
adc.chselr().write(|w| unsafe { w.chsel().bits(0x4B9) });
// reset DMA
rcc.ahbrstr.modify(|_, w| w.dmarst().set_bit());
rcc.ahbrstr.modify(|_, w| w.dmarst().clear_bit());
// enable DMA clock
rcc.ahbenr.modify(|_,w| w.dmaen().set_bit());
// setup DMA channel 1 to read out ADC data
unsafe {
dma.ccr1.write( |w|
w
// priority level 2
.pl().bits(0b10)
// memory/peripheral request size: 16-bit (1)
.msize().bits(0b01)
.psize().bits(0b01)
// cirular mode
.circ().set_bit()
// increment memory ptr, do not increment periph ptr
.minc().set_bit()
.pinc().clear_bit()
// disable 'memory-to-memory' mode
.mem2mem().clear_bit()
// use 'peripheral -> memory' transfer direction
.dir().clear_bit()
// enable only transfer complete interrupt
.teie().clear_bit()
.htie().clear_bit()
.tcie().set_bit()
);
// route DMA channel 1 to ADC - on STM32G0 it is done using DMAMUX
// notice that DMAMUX is 0-indexed, however DMA channel is 1 indexed
// check dmareq_id from reference manual: "table 42. DMAMUX: assignment of multiplexer inputs to resources"
// so 5 means use ADC as DMA input source
dmamux.dmamux_c0cr.write( |w| w.dmareq_id().bits(5).ege().set_bit());
// setup DMA to transfer data from ADC_DR register to adc_dma_buf
let adc_data_register_addr = &adc.dr as *const _ as u32;
let adc_dma_buf_addr : u32 = adc_dma_buf as *const [u16; 6] as u32;
dma.cndtr1.write( |w| w.ndt().bits(adc_dma_buf.len() as u16));
dma.cpar1.write( |w| w.pa().bits(adc_data_register_addr) );
dma.cmar1.write( |w| w.ma().bits(adc_dma_buf_addr));
}
}
pub fn start(dma: &mut stm32::DMA, adc: &mut stm32::ADC, mut trigger: Timer<hal::stm32::TIM6>, samplerate: Hertz) {
// enable DMA
dma.ccr1.modify(|_r,w| w.en().set_bit());
// enable ADC
adc.isr.modify(|_, w| w.adrdy().set_bit());
adc.cr.modify(|_, w| w.aden().set_bit());
while adc.isr.read().adrdy().bit_is_clear() {}
// ADC conversions are started using TIM6 TRGO event
let tim6 = unsafe { &(*stm32::TIM6::ptr()) };
// update mode - the update event is selected as a trigger output (TRGO)
tim6.cr2.modify(|_, w| unsafe { w.mms().bits(0b010) });
trigger.start(samplerate);
// start listening TRGO events
adc.cr.modify(|_, w| w.adstart().set_bit());
}
pub fn raw_to_mv(raw: u16) -> u16 {
let vref: u32 = 3300;
(raw as u32 * vref / 4096) as u16
} |
I managed to make a working UART - DMA transfer with help of your example, see this gist. So I kinda understand now what DMAMUX is for. However the manual mentions you can also do peripheral-to-peripheral DMA transfers. But it is not clear to me how you can configure that. Since the DMAMUX |
I think it is ok if |
Dmamux support is now merged. |
G0 is different from other STM32 MCUs because this uses a thing called
DMAMUX
. Well actuallyH7
,L4+
andWB
series are also using it but currently it is not implemented in their Rust HALs. So we can be the first ones to tackle this problem.If this is done it allows to port a lot of DMA based drivers from stm32l0xx-hal which are relatively mature.
27.05.2020: Recently rust embedded wg suggested that this is way to go with DMA: https://github.com/ra-kete/dma-poc.
And an example how it is used: stm32-rs/stm32f3xx-hal#86
The text was updated successfully, but these errors were encountered: