Adding documentation
This commit is contained in:
parent
8f399ec12b
commit
3088a002c0
|
@ -14,8 +14,8 @@
|
||||||
///! both transfers are completed before reading the data. This is usually not significant for
|
///! 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.
|
///! busy-waiting because the transfers should complete at approximately the same time.
|
||||||
use super::{
|
use super::{
|
||||||
hal, DMAReq, DmaConfig, MemoryToPeripheral, PeripheralToMemory, Priority, TargetAddress,
|
hal, DMAReq, DmaConfig, MemoryToPeripheral, PeripheralToMemory, Priority,
|
||||||
Transfer,
|
TargetAddress, Transfer,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The desired ADC input buffer size. This is use configurable.
|
// The desired ADC input buffer size. This is use configurable.
|
||||||
|
|
58
src/dac.rs
58
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 super::hal;
|
||||||
use heapless::consts;
|
use heapless::consts;
|
||||||
|
|
||||||
|
/// Controller structure for managing the DAC outputs.
|
||||||
pub struct DacOutputs {
|
pub struct DacOutputs {
|
||||||
dac0_spi: hal::spi::Spi<hal::stm32::SPI4, hal::spi::Enabled, u16>,
|
dac0_spi: hal::spi::Spi<hal::stm32::SPI4, hal::spi::Enabled, u16>,
|
||||||
dac1_spi: hal::spi::Spi<hal::stm32::SPI5, hal::spi::Enabled, u16>,
|
dac1_spi: hal::spi::Spi<hal::stm32::SPI5, hal::spi::Enabled, u16>,
|
||||||
outputs: heapless::spsc::Queue<(u16, u16), consts::U32>,
|
|
||||||
timer: hal::timer::Timer<hal::stm32::TIM3>,
|
timer: hal::timer::Timer<hal::stm32::TIM3>,
|
||||||
|
|
||||||
|
// 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 {
|
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(
|
pub fn new(
|
||||||
dac0_spi: hal::spi::Spi<hal::stm32::SPI4, hal::spi::Enabled, u16>,
|
mut dac0_spi: hal::spi::Spi<hal::stm32::SPI4, hal::spi::Enabled, u16>,
|
||||||
dac1_spi: hal::spi::Spi<hal::stm32::SPI5, hal::spi::Enabled, u16>,
|
mut dac1_spi: hal::spi::Spi<hal::stm32::SPI5, hal::spi::Enabled, u16>,
|
||||||
mut timer: hal::timer::Timer<hal::stm32::TIM3>,
|
mut timer: hal::timer::Timer<hal::stm32::TIM3>,
|
||||||
) -> Self {
|
) -> 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());
|
dac0_spi.inner().cr1.modify(|_, w| w.cstart().started());
|
||||||
dac1_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.pause();
|
||||||
timer.reset_counter();
|
timer.reset_counter();
|
||||||
timer.clear_irq();
|
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) {
|
pub fn push(&mut self, dac0_value: u16, dac1_value: u16) {
|
||||||
self.outputs.enqueue((dac0_value, dac1_value)).unwrap();
|
self.outputs.enqueue((dac0_value, dac1_value)).unwrap();
|
||||||
self.timer.resume();
|
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) {
|
pub fn update(&mut self) {
|
||||||
self.timer.clear_irq();
|
self.timer.clear_irq();
|
||||||
match self.outputs.dequeue() {
|
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) {
|
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 {
|
unsafe {
|
||||||
core::ptr::write_volatile(
|
core::ptr::write_volatile(
|
||||||
&self.dac0_spi.inner().txdr as *const _ as *mut u16,
|
&self.dac0_spi.inner().txdr as *const _ as *mut u16,
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -954,6 +954,16 @@ const APP: () = {
|
||||||
panic!("ADC0 input overrun");
|
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" {
|
extern "C" {
|
||||||
// hw interrupt handlers for RTIC to use for scheduling tasks
|
// hw interrupt handlers for RTIC to use for scheduling tasks
|
||||||
// one per priority
|
// one per priority
|
||||||
|
|
Loading…
Reference in New Issue