Changed: - Move most library components to new [`vorago-shared-periphs`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs) which is mostly re-exported in this crate. - Overhaul and simplification of several HAL APIs. The system configuration and IRQ router peripheral instance generally does not need to be passed to HAL API anymore. - All HAL drivers are now type erased. The constructors will still expect and consume the PAC singleton component for resource management purposes, but are not cached anymore. - Refactoring of GPIO library to be more inline with embassy GPIO API. Added: - I2C clock timeout feature support.
160 lines
4.5 KiB
Rust
160 lines
4.5 KiB
Rust
//! Digital to Analog Converter (DAC) driver.
|
|
//!
|
|
//! ## Examples
|
|
//!
|
|
//! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs)
|
|
use core::ops::Deref;
|
|
|
|
use vorago_shared_periphs::{
|
|
disable_peripheral_clock, enable_peripheral_clock, reset_peripheral_for_cycles,
|
|
PeripheralSelect,
|
|
};
|
|
|
|
use crate::{clock::Clocks, pac};
|
|
|
|
pub type DacRegisterBlock = pac::dac0::RegisterBlock;
|
|
|
|
/// Common trait implemented by all PAC peripheral access structures. The register block
|
|
/// format is the same for all DAC blocks.
|
|
pub trait DacMarker: Deref<Target = DacRegisterBlock> {
|
|
const IDX: u8;
|
|
|
|
fn ptr() -> *const DacRegisterBlock;
|
|
}
|
|
|
|
impl DacMarker for pac::Dac0 {
|
|
const IDX: u8 = 0;
|
|
|
|
#[inline(always)]
|
|
fn ptr() -> *const DacRegisterBlock {
|
|
Self::ptr()
|
|
}
|
|
}
|
|
|
|
impl DacMarker for pac::Dac1 {
|
|
const IDX: u8 = 1;
|
|
|
|
#[inline(always)]
|
|
fn ptr() -> *const DacRegisterBlock {
|
|
Self::ptr()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
pub enum DacSettling {
|
|
NoSettling = 0,
|
|
Apb2Times25 = 1,
|
|
Apb2Times50 = 2,
|
|
Apb2Times75 = 3,
|
|
Apb2Times100 = 4,
|
|
Apb2Times125 = 5,
|
|
Apb2Times150 = 6,
|
|
}
|
|
|
|
pub struct Dac(*const DacRegisterBlock);
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
pub struct ValueTooLarge;
|
|
|
|
impl Dac {
|
|
/// Create a new [Dac] driver instance.
|
|
///
|
|
/// The [Clocks] structure is expected here as well to ensure the clock was set up properly.
|
|
pub fn new<Dac: DacMarker>(dac: Dac, dac_settling: DacSettling, _clocks: &Clocks) -> Self {
|
|
enable_peripheral_clock(PeripheralSelect::Dac);
|
|
|
|
dac.ctrl1().write(|w| {
|
|
w.dac_en().set_bit();
|
|
// SAFETY: Enum values are valid values only.
|
|
unsafe { w.dac_settling().bits(dac_settling as u8) }
|
|
});
|
|
let mut dac = Self(Dac::ptr());
|
|
dac.clear_fifo();
|
|
dac.clear_irqs();
|
|
dac
|
|
}
|
|
|
|
pub const fn regs(&self) -> &DacRegisterBlock {
|
|
unsafe { &*self.0 }
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn clear_irqs(&mut self) {
|
|
self.regs().irq_clr().write(|w| {
|
|
w.fifo_oflow().set_bit();
|
|
w.fifo_uflow().set_bit();
|
|
w.dac_done().set_bit();
|
|
w.trig_error().set_bit()
|
|
});
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn clear_fifo(&mut self) {
|
|
self.regs().fifo_clr().write(|w| unsafe { w.bits(1) });
|
|
}
|
|
|
|
/// Load next value into the FIFO.
|
|
///
|
|
/// Uses the [nb] API to allow blocking and non-blocking usage.
|
|
#[inline(always)]
|
|
pub fn load_value(&mut self, val: u16) -> nb::Result<(), ValueTooLarge> {
|
|
if val > 2_u16.pow(12) - 1 {
|
|
return Err(nb::Error::Other(ValueTooLarge));
|
|
}
|
|
let regs = self.regs();
|
|
if regs.status().read().fifo_entry_cnt().bits() >= 32_u8 {
|
|
return Err(nb::Error::WouldBlock);
|
|
}
|
|
regs.fifo_data().write(|w| unsafe { w.bits(val.into()) });
|
|
Ok(())
|
|
}
|
|
|
|
/// This loads and triggers the next value immediately. It also clears the FIFO before
|
|
/// loading the passed value.
|
|
#[inline(always)]
|
|
pub fn load_and_trigger_manually(&mut self, val: u16) -> Result<(), ValueTooLarge> {
|
|
if val > 2_u16.pow(12) - 1 {
|
|
return Err(ValueTooLarge);
|
|
}
|
|
self.clear_fifo();
|
|
// This should never block, the FIFO was cleared. We checked the value as well, so unwrap
|
|
// is okay here.
|
|
nb::block!(self.load_value(val)).unwrap();
|
|
self.trigger_manually();
|
|
Ok(())
|
|
}
|
|
|
|
/// Manually trigger the DAC. This will de-queue the next value inside the FIFO
|
|
/// to be processed by the DAC.
|
|
#[inline(always)]
|
|
pub fn trigger_manually(&self) {
|
|
self.regs().ctrl0().write(|w| w.man_trig_en().set_bit());
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn enable_external_trigger(&self) {
|
|
self.regs().ctrl0().write(|w| w.ext_trig_en().set_bit());
|
|
}
|
|
|
|
pub fn is_settled(&self) -> nb::Result<(), ()> {
|
|
if self.regs().status().read().dac_busy().bit_is_set() {
|
|
return Err(nb::Error::WouldBlock);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn reset(&mut self) {
|
|
enable_peripheral_clock(PeripheralSelect::Dac);
|
|
reset_peripheral_for_cycles(PeripheralSelect::Dac, 2);
|
|
}
|
|
|
|
/// Stops the DAC, which disables its peripheral clock.
|
|
#[inline(always)]
|
|
pub fn stop(self) {
|
|
disable_peripheral_clock(PeripheralSelect::Dac);
|
|
}
|
|
}
|