Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions hal/src/peripherals/adc/d11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::{
#[cfg(feature = "async")]
use super::{FutureAdc, async_api};

use crate::dac::DacWriteHandle;
use crate::{calibration, pac};
use pac::Peripherals;
use pac::Sysctrl;
Expand Down Expand Up @@ -238,6 +239,15 @@ impl<I: AdcInstance> Adc<I> {
}

impl<I: AdcInstance + PrimaryAdc> Adc<I> {
#[inline]
/// Reads the output of DAC0
pub async fn read_dac0_output(
&mut self,
_channel: &DacWriteHandle<'_>,
) -> u16 {
self.read_channel(0x1C)
}

#[inline]
/// Returns the CPU temperature in degrees C
///
Expand Down Expand Up @@ -283,6 +293,15 @@ impl<I: AdcInstance + PrimaryAdc, F> FutureAdc<I, F>
where
F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
{

/// Reads the output of DAC0
pub async fn read_dac0_output(
&mut self,
_channel: &DacWriteHandle<'_>,
) -> u16 {
self.read_channel(0x1C).await
}

/// Reads the CPU temperature. Value returned is in Celcius
pub async fn read_cpu_temperature(&mut self, sysctrl: &mut Sysctrl) -> f32 {
let old_state = sysctrl.vref().read().tsen().bit();
Expand Down
47 changes: 42 additions & 5 deletions hal/src/peripherals/adc/d5x/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ pub mod pin;
use pac::Supc;

#[cfg(feature = "async")]
use super::{FutureAdc, async_api};
use super::{async_api, FutureAdc};

use super::{
ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource,
Error, Flags, PrimaryAdc, SampleCount,
Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc,
SampleCount, ADC_SETTINGS_INTERNAL_READ,
};
use crate::{
calibration,
dac::{Dac0, Dac1, DacWriteHandle, Differential, Single},
pac,
};
use crate::{calibration, pac};

/// ADC instance 0
pub struct Adc0 {
Expand Down Expand Up @@ -154,6 +158,21 @@ impl<I: AdcInstance> Adc<I> {
}

impl<I: AdcInstance + PrimaryAdc> Adc<I> {
#[inline]
/// Reads the output of DAC0 when in the DAC is in single mode
pub fn read_dac0_single_output(&mut self, _channel: &DacWriteHandle<'_, Single<Dac0>>) -> u16 {
self.read_channel(0x1E)
}

#[inline]
/// Reads the output of DAC0 when in the DAC is in differential mode
pub fn read_dac0_differential_output(
&mut self,
_channel: &DacWriteHandle<'_, Differential<Dac0, Dac1>>,
) -> u16 {
self.read_channel(0x1E)
}

#[inline]
/// Reads the CPU temperature in degrees C.
///
Expand Down Expand Up @@ -235,7 +254,7 @@ impl<I: AdcInstance> Adc<I> {
Flags::from_bits_truncate(bits)
}

#[cfg(feature="async")]
#[cfg(feature = "async")]
/// Clear the specified interrupt flags
#[inline]
pub(super) fn clear_flags(&mut self, flags: &Flags) {
Expand Down Expand Up @@ -297,6 +316,24 @@ impl<I: AdcInstance + PrimaryAdc, F> FutureAdc<I, F>
where
F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
{
#[inline]
/// Reads the output of DAC0 when in the DAC is in single mode
pub async fn read_dac0_single_output(
&mut self,
_channel: &DacWriteHandle<'_, Single<Dac0>>,
) -> u16 {
self.read_channel(0x1E).await
}

#[inline]
/// Reads the output of DAC0 when in the DAC is in differential mode
pub async fn read_dac0_differential_output(
&mut self,
_channel: &DacWriteHandle<'_, Differential<Dac0, Dac1>>,
) -> u16 {
self.read_channel(0x1E).await
}

/// Reads the CPU temperature in degrees C.
///
/// n.b. Microchip's errata document for SAM D5x/E5x states:
Expand Down
243 changes: 243 additions & 0 deletions hal/src/peripherals/dac/d11.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
//! Digital-to-Analog Converter

use pac::Pm;

use crate::{
clock::DacClock,
gpio::{AlternateB, Pin, PA02},
pac,
};

#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// The DAC clock exceeds the maximum
/// of 350Khz
ClockTooFast,
}

pub struct Dac {
inner: pac::Dac,
}

pub struct DacWriteHandle<'a> {
pin: Pin<PA02, AlternateB>,
reg: &'a pac::Dac,
}

impl<'a> DacWriteHandle<'a> {
pub fn new(dac: &'a pac::Dac, pin: Pin<PA02, AlternateB>) -> Self {
Self { reg: dac, pin }
}
}

impl Dac {
/// Construct a new DAC. The DAC VREF is set to 3.3V
/// (VDDANA) to allow for maximum output voltage of 3.3V
///
/// This function also checks the clock frequency provided
/// to the DAC, erroring out if the Clock exceeds the maximum
/// DAC clock frequency:
///
/// * **SAMD/E5x** - 12Mhz
/// * **SAMC/D21** - 350Khz
/// * **SAMD11** - 350Khz
pub fn new(dac: pac::Dac, clk: DacClock, pm: &mut Pm) -> Result<Self, Error> {
if clk.freq().to_Hz() > 350_000 {
return Err(Error::ClockTooFast);
}
pm.apbcmask().modify(|_, w| w.dac_().set_bit());
dac.ctrla().write(|w| w.swrst().set_bit());
let s = Self { inner: dac };
s.sync();
s.with_disable(|dac| {
// Set VREF
dac.ctrlb().modify(|_, w| w.refsel().avcc());
});
Ok(s)
}

pub(crate) fn with_disable<R, F: FnOnce(&pac::Dac) -> R>(&self, f: F) -> R {
self.inner.ctrla().write(|w| w.enable().clear_bit());
self.sync();
let ret = f(&self.inner);
self.inner.ctrla().write(|w| w.enable().set_bit());
self.sync();
ret
}

pub fn sync(&self) {
while self.inner.status().read().syncbusy().bit_is_set() {
core::hint::spin_loop();
}
}

/// Converts input voltage in millivolts to DAC output value (RAW)
/// The input voltage is clamped between 0 and 3300mv,
/// resulting in an output between 0 and 1024
///
/// Use this function for single channel mode DAC
pub fn voltage_to_raw(mv: u16) -> u16 {
// Up to 4096 output
const RATIO: f32 = 1024.0 / 3300.0;
let targ = core::cmp::min(3300, mv) as f32;
(targ * RATIO) as u16
}

/// Get a handle to DAC 0 output. This consumes PA02, since
/// the DAC is now enabled and using this pin
pub fn dac0(&self, pin: Pin<PA02, AlternateB>) -> DacWriteHandle<'_> {
self.inner.ctrla().modify(|_, w| w.enable().set_bit());
self.sync();
DacWriteHandle::new(&self.inner, pin)
}

/// Destroy the DAC peripheral, returning resources.
/// The DAC will be disabled and reset before being released
pub fn release(self) -> pac::Dac {
self.inner.ctrla().modify(|_, w| w.enable().clear_bit());
self.sync();
self.inner.ctrla().write(|w| w.swrst().set_bit());
self.sync();
self.inner
}
}

impl DacWriteHandle<'_> {
pub fn sync(&self) {
while self.reg.status().read().syncbusy().bit_is_set() {
core::hint::spin_loop();
}
}

pub(crate) fn with_dac_disable<R, F: FnOnce(&pac::Dac) -> R>(&self, f: F) -> R {
self.reg.ctrla().write(|w| w.enable().clear_bit());
self.sync();
let ret = f(self.reg);
self.reg.ctrla().write(|w| w.enable().set_bit());
self.sync();
ret
}

pub fn write_val(&mut self, val: u16) {
unsafe {
self.reg.data().write(|w| w.bits(val));
}
}

/// Writes a voltage to the DAC.
#[inline]
pub fn write_voltage(&mut self, mv: u16) {
let raw = Dac::voltage_to_raw(mv);
self.write_val(raw);
}

/// Stop the DAC write handle, releasing the pin
pub fn stop(self) -> Pin<PA02, AlternateB> {
self.with_dac_disable(|dac| {
dac.ctrla().modify(|_, w| w.enable().clear_bit());
});
self.pin
}
}

#[cfg(feature = "dma")]
mod dma {
use pac::dmac::chctrlb::Trigactselect as TriggerAction;
use pac::dmac::chctrlb::Trigsrcselect as TriggerSource;

#[cfg(feature = "async")]
use crate::dmac::ReadyFuture;
use crate::{
dmac::{self, AnyChannel, Buffer, Ready},
sercom::dma::SharedSliceBuffer,
};

use super::*;

struct DacDmaPtr(pub *mut u16);

impl DacDmaPtr {
pub fn new(reg: &pac::Dac) -> Self {
Self(reg.data().as_ptr())
}
}

unsafe impl Buffer for DacDmaPtr {
type Beat = u16;

fn dma_ptr(&mut self) -> *mut Self::Beat {
self.0
}

fn incrementing(&self) -> bool {
false
}

fn buffer_len(&self) -> usize {
1
}
}

impl DacWriteHandle<'_> {
/// Writes a buffer to the DAC using DMA. Each buffer value is DAC
/// RAW output (0-1024). Use [Dac::voltage_to_raw] to convert
/// between target voltage output of the DAC and the value to write
/// to the DAC
///
/// The samples are consumed at the sample rate of the DAC
pub fn write_buffer_blocking<CH>(
&mut self,
buf: &[u16],
channel: &mut CH,
) -> Result<(), dmac::Error>
where
CH: AnyChannel<Status = Ready>,
{
let mut bytes = SharedSliceBuffer::from_slice(buf);
let trigger_action = TriggerAction::Beat;

let mut dest = DacDmaPtr::new(self.reg);

unsafe {
channel.as_mut().transfer(
&mut bytes,
&mut dest,
TriggerSource::DacEmpty,
trigger_action,
None,
)?;
}
while channel.as_mut().xfer_complete() {
core::hint::spin_loop();
}
channel.as_mut().xfer_success()
}

/// Writes a buffer to the DAC using DMA (Async version).
/// Each buffer value is DAC RAW output (0-4096).
/// Use [Dac::voltage_to_raw] to convert between target
/// voltage output of the DAC and the value to write
/// to the DAC
///
/// The samples are consumed at the sample rate of the DAC
#[cfg(feature = "async")]
pub async fn write_buffer<CH>(
&mut self,
buf: &[u16],
channel: &mut CH,
) -> Result<(), dmac::Error>
where
CH: AnyChannel<Status = ReadyFuture>,
{
let bytes = SharedSliceBuffer::from_slice(buf);
let trigger_action = TriggerAction::Beat;
let dest = DacDmaPtr::new(self.reg);

channel
.as_mut()
.transfer_future(bytes, dest, TriggerSource::DacEmpty, trigger_action)
.await
}
}
}
Loading