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.
This commit is contained in:
Thomas Kolb 2023-01-06 01:20:43 +01:00
parent 1a6e442815
commit 28bd3b20ab
2 changed files with 119 additions and 32 deletions

80
src/ext_adc.rs Normal file
View file

@ -0,0 +1,80 @@
use cortex_m::singleton;
use rp2040_hal::dma::{bidirectional, Channel};
use fugit::HertzU32;
use fugit::RateExtU32;
pub struct AdcContext<D, TXC, RXC, CSP>
where
TXC: rp2040_hal::dma::ChannelIndex,
RXC: rp2040_hal::dma::ChannelIndex,
D: rp2040_hal::spi::SpiDevice
{
dma_tx_chan: Channel<TXC>,
dma_rx_chan: Channel<RXC>,
tx_buf: &'static mut [u8; 3],
rx_buf: &'static mut [u8; 3],
spi: rp2040_hal::spi::Spi<rp2040_hal::spi::Enabled, D, 8>,
cs_pin: CSP
}
impl<D, TXC, RXC, E, CSP> AdcContext<D, TXC, RXC, CSP>
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<Error=E>
{
pub fn init(
device: D,
periph_freq: HertzU32,
resets: &mut rp2040_pac::RESETS,
tx_chan: Channel<TXC>,
rx_chan: Channel<RXC>,
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::<D, TXC, RXC, CSP> {
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)
}
}

View file

@ -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::<hal::gpio::FunctionSpi>();
let _spi_mosi = pins.gpio3.into_mode::<hal::gpio::FunctionSpi>();
let _spi_miso = pins.gpio4.into_mode::<hal::gpio::FunctionSpi>();
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");
}
}
}