add ValueArray type and respective write functions
This commit is contained in:
parent
e0747e937f
commit
fe7b72948a
1 changed files with 124 additions and 82 deletions
|
@ -98,6 +98,14 @@ pub enum Value {
|
||||||
Bit12(u16, Alignment),
|
Bit12(u16, Alignment),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum ValueArray<'a> {
|
||||||
|
Bit8(&'a [u8]),
|
||||||
|
Bit12Left(&'a [u16]),
|
||||||
|
Bit12Right(&'a [u16]),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Dac<'d, T: Instance, Tx> {
|
pub struct Dac<'d, T: Instance, Tx> {
|
||||||
channels: u8,
|
channels: u8,
|
||||||
txdma: PeripheralRef<'d, Tx>,
|
txdma: PeripheralRef<'d, Tx>,
|
||||||
|
@ -129,21 +137,19 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> {
|
||||||
T::enable();
|
T::enable();
|
||||||
T::reset();
|
T::reset();
|
||||||
|
|
||||||
unsafe {
|
T::regs().mcr().modify(|reg| {
|
||||||
T::regs().mcr().modify(|reg| {
|
for ch in 0..channels {
|
||||||
for ch in 0..channels {
|
reg.set_mode(ch as usize, 0);
|
||||||
reg.set_mode(ch as usize, 0);
|
reg.set_mode(ch as usize, 0);
|
||||||
reg.set_mode(ch as usize, 0);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
T::regs().cr().modify(|reg| {
|
T::regs().cr().modify(|reg| {
|
||||||
for ch in 0..channels {
|
for ch in 0..channels {
|
||||||
reg.set_en(ch as usize, true);
|
reg.set_en(ch as usize, true);
|
||||||
reg.set_ten(ch as usize, true);
|
reg.set_ten(ch as usize, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
channels,
|
channels,
|
||||||
|
@ -161,13 +167,12 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the enable register of the given channel
|
||||||
fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> {
|
fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> {
|
||||||
self.check_channel_exists(ch)?;
|
self.check_channel_exists(ch)?;
|
||||||
unsafe {
|
T::regs().cr().modify(|reg| {
|
||||||
T::regs().cr().modify(|reg| {
|
reg.set_en(ch.index(), on);
|
||||||
reg.set_en(ch.index(), on);
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,112 +184,149 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> {
|
||||||
self.set_channel_enable(ch, false)
|
self.set_channel_enable(ch, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs all register accesses necessary to select a new trigger for CH1
|
||||||
pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
||||||
self.check_channel_exists(Channel::Ch1)?;
|
self.check_channel_exists(Channel::Ch1)?;
|
||||||
unwrap!(self.disable_channel(Channel::Ch1));
|
unwrap!(self.disable_channel(Channel::Ch1));
|
||||||
unsafe {
|
T::regs().cr().modify(|reg| {
|
||||||
T::regs().cr().modify(|reg| {
|
reg.set_tsel1(trigger.tsel());
|
||||||
reg.set_tsel1(trigger.tsel());
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs all register accesses necessary to select a new trigger for CH2
|
||||||
pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
||||||
self.check_channel_exists(Channel::Ch2)?;
|
self.check_channel_exists(Channel::Ch2)?;
|
||||||
unwrap!(self.disable_channel(Channel::Ch2));
|
unwrap!(self.disable_channel(Channel::Ch2));
|
||||||
unsafe {
|
T::regs().cr().modify(|reg| {
|
||||||
T::regs().cr().modify(|reg| {
|
reg.set_tsel2(trigger.tsel());
|
||||||
reg.set_tsel2(trigger.tsel());
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a software trigger on a given channel
|
||||||
pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> {
|
pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> {
|
||||||
self.check_channel_exists(ch)?;
|
self.check_channel_exists(ch)?;
|
||||||
unsafe {
|
T::regs().swtrigr().write(|reg| {
|
||||||
T::regs().swtrigr().write(|reg| {
|
reg.set_swtrig(ch.index(), true);
|
||||||
reg.set_swtrig(ch.index(), true);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a software trigger on all channels
|
||||||
pub fn trigger_all(&mut self) {
|
pub fn trigger_all(&mut self) {
|
||||||
unsafe {
|
T::regs().swtrigr().write(|reg| {
|
||||||
T::regs().swtrigr().write(|reg| {
|
reg.set_swtrig(Channel::Ch1.index(), true);
|
||||||
reg.set_swtrig(Channel::Ch1.index(), true);
|
reg.set_swtrig(Channel::Ch2.index(), true);
|
||||||
reg.set_swtrig(Channel::Ch2.index(), true);
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> {
|
pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> {
|
||||||
self.check_channel_exists(ch)?;
|
self.check_channel_exists(ch)?;
|
||||||
match value {
|
match value {
|
||||||
Value::Bit8(v) => unsafe {
|
Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)),
|
||||||
T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v));
|
Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)),
|
||||||
},
|
Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)),
|
||||||
Value::Bit12(v, Alignment::Left) => unsafe {
|
|
||||||
T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v));
|
|
||||||
},
|
|
||||||
Value::Bit12(v, Alignment::Right) => unsafe {
|
|
||||||
T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v));
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write `data` to the DAC via DMA.
|
||||||
|
///
|
||||||
|
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||||
|
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||||
|
///
|
||||||
|
/// ## Current limitations
|
||||||
|
/// - Only CH1 Supported
|
||||||
|
///
|
||||||
/// TODO: Allow an array of Value instead of only u16, right-aligned
|
/// TODO: Allow an array of Value instead of only u16, right-aligned
|
||||||
pub async fn write(&mut self, data: &[u16], circular: bool) -> Result<(), Error>
|
pub async fn write_8bit(&mut self, data_ch1: &[u8], circular: bool) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
Tx: Dma<T>,
|
||||||
|
{
|
||||||
|
self.write_inner(ValueArray::Bit8(data_ch1), circular).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_12bit_right_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
Tx: Dma<T>,
|
||||||
|
{
|
||||||
|
self.write_inner(ValueArray::Bit12Right(data_ch1), circular).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_12bit_left_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
Tx: Dma<T>,
|
||||||
|
{
|
||||||
|
self.write_inner(ValueArray::Bit12Left(data_ch1), circular).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_inner(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
Tx: Dma<T>,
|
Tx: Dma<T>,
|
||||||
{
|
{
|
||||||
// TODO: Make this a parameter or get it from the struct or so...
|
// TODO: Make this a parameter or get it from the struct or so...
|
||||||
const CHANNEL: usize = 0;
|
const CHANNEL: usize = 0;
|
||||||
|
|
||||||
//debug!("Starting DAC");
|
// Enable DAC and DMA
|
||||||
unsafe {
|
T::regs().cr().modify(|w| {
|
||||||
T::regs().cr().modify(|w| {
|
w.set_en(CHANNEL, true);
|
||||||
w.set_en(CHANNEL, true);
|
w.set_dmaen(CHANNEL, true);
|
||||||
w.set_dmaen(CHANNEL, true);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let tx_request = self.txdma.request();
|
let tx_request = self.txdma.request();
|
||||||
|
|
||||||
// Use the 12 bit right-aligned register for now. TODO: distinguish values
|
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||||
let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16;
|
let tx_f = match data_ch1 {
|
||||||
|
ValueArray::Bit8(buf) => unsafe {
|
||||||
let tx_f = unsafe {
|
Transfer::new_write(
|
||||||
Transfer::new_write(
|
&mut self.txdma,
|
||||||
&mut self.txdma,
|
tx_request,
|
||||||
tx_request,
|
buf,
|
||||||
data,
|
T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8,
|
||||||
tx_dst,
|
TransferOptions {
|
||||||
TransferOptions {
|
circular,
|
||||||
circular,
|
halt_transfer_ir: false,
|
||||||
halt_transfer_ir: false,
|
},
|
||||||
},
|
)
|
||||||
)
|
},
|
||||||
|
ValueArray::Bit12Left(buf) => unsafe {
|
||||||
|
Transfer::new_write(
|
||||||
|
&mut self.txdma,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16,
|
||||||
|
TransferOptions {
|
||||||
|
circular,
|
||||||
|
halt_transfer_ir: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Right(buf) => unsafe {
|
||||||
|
Transfer::new_write(
|
||||||
|
&mut self.txdma,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16,
|
||||||
|
TransferOptions {
|
||||||
|
circular,
|
||||||
|
halt_transfer_ir: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
//debug!("Awaiting tx_f");
|
|
||||||
|
|
||||||
tx_f.await;
|
tx_f.await;
|
||||||
|
|
||||||
// finish dma
|
// finish dma
|
||||||
unsafe {
|
// TODO: Do we need to check any status registers here?
|
||||||
// TODO: Do we need to check any status registers here?
|
T::regs().cr().modify(|w| {
|
||||||
|
// Disable the DAC peripheral
|
||||||
|
w.set_en(CHANNEL, false);
|
||||||
|
// Disable the DMA. TODO: Is this necessary?
|
||||||
|
w.set_dmaen(CHANNEL, false);
|
||||||
|
});
|
||||||
|
|
||||||
T::regs().cr().modify(|w| {
|
|
||||||
// Disable the dac peripheral
|
|
||||||
w.set_en(CHANNEL, false);
|
|
||||||
// Disable the DMA. TODO: Is this necessary?
|
|
||||||
w.set_dmaen(CHANNEL, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue