From 28bd3b20ab95324f76691ae82bb63ba9160c0539 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Fri, 6 Jan 2023 01:20:43 +0100 Subject: [PATCH] Read multiple ADC channels (maybe) At least this is compiling without errors now. - Added the ext_adc module abstracting the ADC communication (with the AdcContext struct). - SPI and necessary DMA channels are now passed into the AdcContext struct and moved around on each transfer. - Transfers seem to be triggered, but the returned values are still wrong (either 0 or 4095). This still needs to be debugged. --- src/ext_adc.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 71 ++++++++++++++++++++++++-------------------- 2 files changed, 119 insertions(+), 32 deletions(-) create mode 100644 src/ext_adc.rs diff --git a/src/ext_adc.rs b/src/ext_adc.rs new file mode 100644 index 0000000..05e9cdd --- /dev/null +++ b/src/ext_adc.rs @@ -0,0 +1,80 @@ +use cortex_m::singleton; +use rp2040_hal::dma::{bidirectional, Channel}; +use fugit::HertzU32; +use fugit::RateExtU32; + +pub struct AdcContext +where + TXC: rp2040_hal::dma::ChannelIndex, + RXC: rp2040_hal::dma::ChannelIndex, + D: rp2040_hal::spi::SpiDevice +{ + dma_tx_chan: Channel, + dma_rx_chan: Channel, + tx_buf: &'static mut [u8; 3], + rx_buf: &'static mut [u8; 3], + spi: rp2040_hal::spi::Spi, + cs_pin: CSP +} + +impl AdcContext +where + TXC: rp2040_hal::dma::ChannelIndex, + RXC: rp2040_hal::dma::ChannelIndex, + D: rp2040_hal::spi::SpiDevice, + E: core::fmt::Debug, + CSP: embedded_hal::digital::v2::OutputPin +{ + pub fn init( + device: D, + periph_freq: HertzU32, + resets: &mut rp2040_pac::RESETS, + tx_chan: Channel, + rx_chan: Channel, + mut cs_pin: CSP + ) -> Self + { + let spi_if = rp2040_hal::spi::Spi::<_, _, 8>::new(device); + + // Exchange the uninitialised SPI driver for an initialised one + let spi_if = spi_if.init( + resets, + periph_freq, + 1_000_000u32.Hz(), + &embedded_hal::spi::MODE_0, + ); + + cs_pin.set_high().unwrap(); + + AdcContext:: { + dma_rx_chan: rx_chan, + dma_tx_chan: tx_chan, + rx_buf: singleton!(: [u8; 3] = [0x06, 0x40, 0x00]).unwrap(), + tx_buf: singleton!(: [u8; 3] = [0; 3]).unwrap(), + spi: spi_if, + cs_pin: cs_pin + } + } + + pub fn sample_adc_channel(mut self, chan: u8) -> (Self, u16) + { + self.tx_buf[1] = chan << 6; + + // clear chip select + self.cs_pin.set_low().unwrap(); + + // Use BidirectionalConfig to simultaneously write to spi from tx_buf and read into rx_buf + let transfer = bidirectional::Config::new( + (self.dma_tx_chan, self.dma_rx_chan), + self.tx_buf, + self.spi, + self.rx_buf).start(); + + // Wait for both DMA channels to finish + ((self.dma_tx_chan, self.dma_rx_chan), self.tx_buf, self.spi, self.rx_buf) = transfer.wait(); + self.cs_pin.set_high().unwrap(); + + let result = (((self.rx_buf[1] & 0x0F) as u16) << 8) | self.rx_buf[2] as u16; + (self, result) + } +} diff --git a/src/main.rs b/src/main.rs index 2c88e6d..c08af92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use panic_halt as _; use rp2040_hal as hal; // Some traits we need -use cortex_m::singleton; +//use cortex_m::singleton; use embedded_hal::PwmPin; use fugit::RateExtU32; use rp2040_hal::clocks::Clock; @@ -29,13 +29,15 @@ use rp2040_hal::clocks::Clock; use hal::pac; // SPI + DMA -use hal::dma::{bidirectional, DMAExt}; +use hal::dma::DMAExt; // UART related types use hal::uart::{DataBits, StopBits, UartConfig}; use heapless::String; +mod ext_adc; + /// The linker will place this boot block at the start of our program image. We /// need this to help the ROM bootloader get our code up and running. /// Note: This boot block is not necessary when using a rp-hal based BSP @@ -156,41 +158,19 @@ fn main() -> ! { let _spi_sclk = pins.gpio2.into_mode::(); let _spi_mosi = pins.gpio3.into_mode::(); let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::spi::Spi::<_, _, 8>::new(pac.SPI0); - - // Exchange the uninitialised SPI driver for an initialised one - let spi = spi.init( - &mut pac.RESETS, - clocks.peripheral_clock.freq(), - 1_000_000u32.Hz(), - &embedded_hal::spi::MODE_0, - ); // Initialize DMA. let dma = pac.DMA.split(&mut pac.RESETS); - // Use DMA to read ADC (0xC0 in byte 1 is the channel mask) - let tx_buf = singleton!(: [u8; 3] = [0x06, 0x40, 0x00]).unwrap(); - let rx_buf = singleton!(: [u8; 3] = [0; 3]).unwrap(); + let mut adc_ctrl = ext_adc::AdcContext::init( + pac.SPI0, + clocks.peripheral_clock.freq(), + &mut pac.RESETS, + dma.ch0, + dma.ch1, + spi_cs + ); - // clear chip select - spi_cs.set_low().unwrap(); - - // Use BidirectionalConfig to simultaneously write to spi from tx_buf and read into rx_buf - let transfer = bidirectional::Config::new((dma.ch0, dma.ch1), tx_buf, spi, rx_buf).start(); - // Wait for both DMA channels to finish - let ((_ch0, _ch1), tx_buf, _spi, rx_buf) = transfer.wait(); - spi_cs.set_high().unwrap(); - - let adc_value = (((rx_buf[1] & 0x0F) as u16) << 8) | rx_buf[2] as u16; - - { - let data: String<16> = String::from(adc_value); - - uart.write_full_blocking(b"Read ADC value: "); - uart.write_full_blocking(data.as_bytes()); - uart.write_full_blocking(b"\r\n"); - } // Infinite loop, fading LED up and down loop { @@ -205,5 +185,32 @@ fn main() -> ! { channel2.set_duty(ch_val[1]); channel3.set_duty(ch_val[2]); delay.delay_us(50); + + let mut adc_value: [u16; 4] = [0; 4]; + + (adc_ctrl, adc_value[0]) = adc_ctrl.sample_adc_channel(0); + delay.delay_us(50); + (adc_ctrl, adc_value[1]) = adc_ctrl.sample_adc_channel(1); + delay.delay_us(50); + (adc_ctrl, adc_value[2]) = adc_ctrl.sample_adc_channel(2); + delay.delay_us(50); + (adc_ctrl, adc_value[3]) = adc_ctrl.sample_adc_channel(3); + + { + let mut data: String<16>; + + uart.write_full_blocking(b"ADC channels: ["); + + for i in 0..adc_value.len() { + data = String::from(adc_value[0]); + uart.write_full_blocking(data.as_bytes()); + + if i < 3 { + uart.write_full_blocking(b", "); + } + } + + uart.write_full_blocking(b"]\r\n"); + } } }