2025-06-10 8:01 PM - edited 2025-06-10 8:38 PM
fn setup_dma(
dma_ch: &stm32g0xx_hal::pac::dma::CH,
peripheral_address: u32,
buffer_address: u32,
buffer_size: u16,
) {
let dma_mux: &stm32g0xx_hal::pac::dmamux::RegisterBlock = unsafe { &*DMAMUX::ptr() };
let rcc: &stm32g0xx_hal::pac::rcc::RegisterBlock = unsafe { &*RCC::ptr() };
rcc.ahbenr.modify(|_, w| w.dmaen().set_bit()); // Enable DMA clock
dma_mux
.c0cr
.write(|w| unsafe { w.dmareq_id().bits(DmaMuxIndex::TIM1_CH1 as u8) });
dma_ch.par.write(|w| unsafe { w.bits(peripheral_address) });
dma_ch.mar.write(|w| unsafe { w.bits(buffer_address) });
dma_ch.ndtr.write(|w| unsafe { w.ndt().bits(buffer_size) });
rprint!("DMA PAR: {:#X}\n", dma_ch.par.read().bits());
rprint!("DMA MAR: {:#X}\n", dma_ch.mar.read().bits());
rprint!("DMA NDTR: {:#X}\n", dma_ch.ndtr.read().bits());
// Configure the DMA channel// Transfer complete interrupt enable
dma_ch.cr.write(|w| {
unsafe {
w.mem2mem()
.clear_bit() // Memory-to-memory mode disabled
.pl()
.bits(stm32g0xx_hal::dma::Priority::VeryHigh as u8)
.msize()
.bits(stm32g0xx_hal::dma::WordSize::BITS32 as u8)
.psize()
.bits(stm32g0xx_hal::dma::WordSize::BITS32 as u8)
.minc()
.clear_bit()
.pinc()
.clear_bit()
.circ()
.clear_bit()
.dir()
.set_bit()
.teie()
.set_bit()
.tcie()
.set_bit()
}
});
rprintln!("DMA CR: {:#X}\n", dma_ch.cr.read().bits());
}
pub fn write_parallel_data_dma(buffer: &mut [u32]) -> &[u32] {
let dma: &stm32g0xx_hal::pac::dma::RegisterBlock = unsafe { &*DMA::ptr() };
let gpioa = unsafe { &*stm32g0xx_hal::pac::GPIOA::ptr() };
rprintln!(
"Moder: {:#X} PUPDR: {:#X}\n",
gpioa.moder.read().bits(),
gpioa.pupdr.read().bits()
);
setup_dma(
&dma.ch1,
&gpioa.odr as *const _ as u32,
buffer.as_mut_ptr() as u32,
buffer.len() as u16,
);
rprint!("ISR before: {}\n", dma.isr.read().bits());
// Start DMA transfer
dma.ch1.cr.modify(|_, w| w.en().set_bit());
asm::delay(100000);
rprint!("GPIOA LCKR: {}\n", gpioa.lckr.read().bits());
// Wait for DMA interrupt cause flag to be set
loop {
rprint!("DMA NDTR: {}\n", dma.ch1.ndtr.read().bits());
rprint!("ISR during: {}\n", dma.isr.read().bits());
let isr_val = dma.isr.read();
if isr_val.tcif1().bit_is_set() {
rprintln!("DMA transfer completed successfully");
break;
} else if isr_val.teif1().bit_is_set() {
panic!(
"DMA did not complete successfully. ISR state: {:#018b}",
isr_val.bits()
);
}
}
// Stop DMA
dma.ch1.cr.modify(|_, w| w.en().clear_bit());
buffer
}
Output:
Moder: 0xEBFE5555 PUPDR: 0x24000000
20:07:31.135:
20:07:31.135: DMA PAR: 0x50000014
20:07:31.135: DMA MAR: 0x20001F60
20:07:31.135: DMA NDTR: 0x8
20:07:31.135: DMA CR: 0x3A1A
20:07:31.135:
20:07:31.135: ISR before: 0
20:07:31.135: GPIOA LCKR: 0
20:07:31.135: DMA NDTR: 8
20:07:31.135: ISR during: 9
20:07:31.135: panicked at src/main.rs:171:13:
20:07:31.135: DMA did not complete successfully. ISR state: 0b0000000000001001
I appreciate the assistance!
Solved! Go to Solution.
2025-06-10 10:38 PM - edited 2025-06-10 10:40 PM
Indeed, the GPIO are not accessable by DMA on a G0 and many others which use a Cortex-M0+ core implementing a single-cycle IO port.
See the reference manual, Figure 1. System architecture.
The (debatable) advantage is faster and more predictable GPIO bit-banging.
hth
KnarfB
2025-06-10 10:21 PM
With a few small additions, I'm able to write a 2000 char string to USART2 with no issues. Copying those additions back to my original code doesn't change its failing behavior.
2025-06-10 10:38 PM - edited 2025-06-10 10:40 PM
Indeed, the GPIO are not accessable by DMA on a G0 and many others which use a Cortex-M0+ core implementing a single-cycle IO port.
See the reference manual, Figure 1. System architecture.
The (debatable) advantage is faster and more predictable GPIO bit-banging.
hth
KnarfB
2025-06-10 11:41 PM - edited 2025-06-11 12:17 AM
That is unfortunate for my application, which involves a lot of dumb <but fast> transfers that would benefit from 3X less power draw. Bummer. Thanks KnarfB!