diff --git a/src/adc.rs b/src/adc.rs index 039a7c3..e3310f4 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -14,8 +14,8 @@ ///! both transfers are completed before reading the data. This is usually not significant for ///! busy-waiting because the transfers should complete at approximately the same time. use super::{ - hal, DMAReq, DmaConfig, MemoryToPeripheral, PeripheralToMemory, Priority, TargetAddress, - Transfer, + hal, DMAReq, DmaConfig, MemoryToPeripheral, PeripheralToMemory, Priority, + TargetAddress, Transfer, }; // The desired ADC input buffer size. This is use configurable. diff --git a/src/dac.rs b/src/dac.rs index d2b36a3..8829385 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -1,21 +1,44 @@ +///! Stabilizer DAC output control +///! +///! Stabilizer output DACs do not currently rely on DMA requests for generating output. +///! Instead, the DACs utilize an internal queue for storing output codes. A timer then periodically +///! generates an interrupt which triggers an update of the DACs via a write over SPI. use super::hal; use heapless::consts; +/// Controller structure for managing the DAC outputs. pub struct DacOutputs { dac0_spi: hal::spi::Spi, dac1_spi: hal::spi::Spi, - outputs: heapless::spsc::Queue<(u16, u16), consts::U32>, timer: hal::timer::Timer, + + // The queue is provided a default length of 32 updates, but this queue can be updated by the + // end user to be larger if necessary. + outputs: heapless::spsc::Queue<(u16, u16), consts::U32>, } impl DacOutputs { + /// Construct a new set of DAC output controls + /// + /// # Args + /// * `dac0_spi` - The SPI interface to the DAC0 output. + /// * `dac1_spi` - The SPI interface to the DAC1 output. + /// * `timer` - The timer used to generate periodic events for updating the DACs. pub fn new( - dac0_spi: hal::spi::Spi, - dac1_spi: hal::spi::Spi, + mut dac0_spi: hal::spi::Spi, + mut dac1_spi: hal::spi::Spi, mut timer: hal::timer::Timer, ) -> Self { + // Start the DAC SPI interfaces in infinite transaction mode. CS is configured in + // auto-suspend mode. dac0_spi.inner().cr1.modify(|_, w| w.cstart().started()); dac1_spi.inner().cr1.modify(|_, w| w.cstart().started()); + + dac0_spi.listen(hal::spi::Event::Error); + dac1_spi.listen(hal::spi::Event::Error); + + // Stop the timer and begin listening for timeouts. Timeouts will be used as a means to + // generate new DAC outputs. timer.pause(); timer.reset_counter(); timer.clear_irq(); @@ -29,11 +52,28 @@ impl DacOutputs { } } + /// Push a set of new DAC output codes to the internal queue. + /// + /// # Note + /// The earlier DAC output codes will be generated within 1 update cycle of the codes. This is a + /// fixed latency currently. + /// + /// This function will panic if too many codes are written. + /// + /// # Args + /// * `dac0_value` - The value to enqueue for a DAC0 update. + /// * `dac1_value` - The value to enqueue for a DAC1 update. pub fn push(&mut self, dac0_value: u16, dac1_value: u16) { self.outputs.enqueue((dac0_value, dac1_value)).unwrap(); self.timer.resume(); } + /// Update the DAC codes with the next set of values in the internal queue. + /// + /// # Note + /// This is intended to be called from the TIM3 update ISR. + /// + /// If the last value in the queue is used, the timer is stopped. pub fn update(&mut self) { self.timer.clear_irq(); match self.outputs.dequeue() { @@ -46,7 +86,19 @@ impl DacOutputs { }; } + /// Write immediate values to the DAC outputs. + /// + /// # Note + /// The DACs will be updated as soon as the SPI transfer completes, which will be nominally + /// 320nS after this function call. + /// + /// # Args + /// * `dac0_value` - The output code to write to DAC0. + /// * `dac1_value` - The output code to write to DAC1. pub fn write(&mut self, dac0_value: u16, dac1_value: u16) { + // In order to optimize throughput and minimize latency, the DAC codes are written directly + // into the SPI TX FIFO. No error checking is conducted. Errors are handled via interrupts + // instead. unsafe { core::ptr::write_volatile( &self.dac0_spi.inner().txdr as *const _ as *mut u16, diff --git a/src/main.rs b/src/main.rs index b7cf7db..b5fff64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -954,6 +954,16 @@ const APP: () = { panic!("ADC0 input overrun"); } + #[task(binds = SPI4, priority = 1)] + fn spi4(_: spi4::Context) { + panic!("DAC0 output error"); + } + + #[task(binds = SPI5, priority = 1)] + fn spi5(_: spi5::Context) { + panic!("DAC1 output error"); + } + extern "C" { // hw interrupt handlers for RTIC to use for scheduling tasks // one per priority