add ValueArray type and respective write functions

This commit is contained in:
JuliDi 2023-06-19 13:42:25 +02:00
parent e0747e937f
commit fe7b72948a
No known key found for this signature in database
GPG Key ID: E1E90AE563D09D63

View File

@ -98,6 +98,14 @@ pub enum Value {
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> {
channels: u8,
txdma: PeripheralRef<'d, Tx>,
@ -129,21 +137,19 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> {
T::enable();
T::reset();
unsafe {
T::regs().mcr().modify(|reg| {
for ch in 0..channels {
reg.set_mode(ch as usize, 0);
reg.set_mode(ch as usize, 0);
}
});
T::regs().mcr().modify(|reg| {
for ch in 0..channels {
reg.set_mode(ch as usize, 0);
reg.set_mode(ch as usize, 0);
}
});
T::regs().cr().modify(|reg| {
for ch in 0..channels {
reg.set_en(ch as usize, true);
reg.set_ten(ch as usize, true);
}
});
}
T::regs().cr().modify(|reg| {
for ch in 0..channels {
reg.set_en(ch as usize, true);
reg.set_ten(ch as usize, true);
}
});
Self {
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> {
self.check_channel_exists(ch)?;
unsafe {
T::regs().cr().modify(|reg| {
reg.set_en(ch.index(), on);
})
}
T::regs().cr().modify(|reg| {
reg.set_en(ch.index(), on);
});
Ok(())
}
@ -179,112 +184,149 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> {
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> {
self.check_channel_exists(Channel::Ch1)?;
unwrap!(self.disable_channel(Channel::Ch1));
unsafe {
T::regs().cr().modify(|reg| {
reg.set_tsel1(trigger.tsel());
})
}
T::regs().cr().modify(|reg| {
reg.set_tsel1(trigger.tsel());
});
Ok(())
}
/// Performs all register accesses necessary to select a new trigger for CH2
pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
self.check_channel_exists(Channel::Ch2)?;
unwrap!(self.disable_channel(Channel::Ch2));
unsafe {
T::regs().cr().modify(|reg| {
reg.set_tsel2(trigger.tsel());
})
}
T::regs().cr().modify(|reg| {
reg.set_tsel2(trigger.tsel());
});
Ok(())
}
/// Perform a software trigger on a given channel
pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> {
self.check_channel_exists(ch)?;
unsafe {
T::regs().swtrigr().write(|reg| {
reg.set_swtrig(ch.index(), true);
});
}
T::regs().swtrigr().write(|reg| {
reg.set_swtrig(ch.index(), true);
});
Ok(())
}
/// Perform a software trigger on all channels
pub fn trigger_all(&mut self) {
unsafe {
T::regs().swtrigr().write(|reg| {
reg.set_swtrig(Channel::Ch1.index(), true);
reg.set_swtrig(Channel::Ch2.index(), true);
})
}
T::regs().swtrigr().write(|reg| {
reg.set_swtrig(Channel::Ch1.index(), true);
reg.set_swtrig(Channel::Ch2.index(), true);
});
}
pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> {
self.check_channel_exists(ch)?;
match value {
Value::Bit8(v) => unsafe {
T::regs().dhr8r(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));
},
Value::Bit8(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)),
}
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
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
Tx: Dma<T>,
{
// TODO: Make this a parameter or get it from the struct or so...
const CHANNEL: usize = 0;
//debug!("Starting DAC");
unsafe {
T::regs().cr().modify(|w| {
w.set_en(CHANNEL, true);
w.set_dmaen(CHANNEL, true);
});
}
// Enable DAC and DMA
T::regs().cr().modify(|w| {
w.set_en(CHANNEL, true);
w.set_dmaen(CHANNEL, true);
});
let tx_request = self.txdma.request();
// Use the 12 bit right-aligned register for now. TODO: distinguish values
let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16;
let tx_f = unsafe {
Transfer::new_write(
&mut self.txdma,
tx_request,
data,
tx_dst,
TransferOptions {
circular,
halt_transfer_ir: false,
},
)
// Initiate the correct type of DMA transfer depending on what data is passed
let tx_f = match data_ch1 {
ValueArray::Bit8(buf) => unsafe {
Transfer::new_write(
&mut self.txdma,
tx_request,
buf,
T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8,
TransferOptions {
circular,
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;
// 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(())
}
}