|
| 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 | +} |
0 commit comments