diff --git a/README.md b/README.md index fba6854..9c88b31 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,28 @@ A platform agnostic library for the Texas Instruments DAC8568. ## features - Support for Texas Instruments DAC8568 +- Limited subset of DAC8568 features supported - Full no-std support -- Implemented with embedded-hal (https://docs.rs/embedded-hal/0.2.3/embedded_hal) +- Implemented with `embedded-hal` (https://docs.rs/embedded-hal/0.2.3/embedded_hal) - Blocking and non-blocking support ## example -Note: Quick example based on the `stm32h7xx-hal`. \ No newline at end of file +Note: Quick example based on the `stm32h7xx-hal`. + +```rust +// Initialise NSS for SPI communications +let spi = ...; +let nss = nss.into_push_pull_output(); +// Initialize the dac instance +let mut dac = dac8568::Dac::new(nss); +dac.enable(); +// Get a "write" message to set the voltage of a given channel +let message = dac8568::Message::get_write_message(dac8568::Channel::A, voltage); +// Now transfer the data either as a blocking call +dac.write_blocking(spi, message).unwrap(); +// or prepare the data for a DMA transfer +dac.prepare_transfer(message, |payload| { + // begin DMA transfer with bytes payload +}); +``` \ No newline at end of file diff --git a/documentation/dac8568.pdf b/documentation/dac8568.pdf new file mode 100644 index 0000000..02a62eb Binary files /dev/null and b/documentation/dac8568.pdf differ diff --git a/documentation/dac8568_ssop16.png b/documentation/dac8568_ssop16.png new file mode 100644 index 0000000..1083cd7 Binary files /dev/null and b/documentation/dac8568_ssop16.png differ diff --git a/documentation/oscilloscope_capture.png b/documentation/oscilloscope_capture.png new file mode 100644 index 0000000..42350a3 Binary files /dev/null and b/documentation/oscilloscope_capture.png differ diff --git a/src/lib.rs b/src/lib.rs index 5f8b372..8bb09a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ use embedded_hal::digital::v2::OutputPin; -pub enum ChannelSelect { +pub enum Channel { A = 0, B = 1, C = 2, @@ -21,9 +21,25 @@ pub enum ChannelSelect { BROADCAST = 9, } +impl Channel { + pub fn from_index(index: u8) -> Channel { + match index { + 0 => Channel::A, + 1 => Channel::B, + 2 => Channel::C, + 3 => Channel::D, + 4 => Channel::E, + 5 => Channel::F, + 6 => Channel::G, + 7 => Channel::H, + _ => panic!("Unsupported index for dac8568 channel select"), + } + } +} + pub enum ControlType { WriteToInputRegister = 0, - UpdateRegister = 1, + UpdateRegister = 1, WriteToChannelAndUpdateAllRegisters = 2, WriteToChannelAndUpdateSingleRegister = 3, PowerDownComm = 4, @@ -33,106 +49,71 @@ pub enum ControlType { } pub enum SetupMode { - Static = 8, - Flex = 9 + Static = 8, + Flex = 9, } pub enum ClearCodeFeature { - ClearToZeroScale = 0, - ClearToMidScale = 1, - ClearToFullScale = 2, - IgnoreClearPin = 3, + ClearToZeroScale = 0, + ClearToMidScale = 1, + ClearToFullScale = 2, + IgnoreClearPin = 3, } pub enum InternalRefCommFeature { - PowerDownIntRefStatic = 0, - PowerUpIntRefStatic = 1, -// PowerUpIntRefFlex = 0, -// PowerUpIntRefAlwaysFlex = 0, -// PowerDownIntRefFlex = 0, -// SwitchFromFlexToStatic = 0 -} + PowerDownIntRefStatic = 0, + PowerUpIntRefStatic = 1, + // PowerUpIntRefFlex = 0, + // PowerUpIntRefAlwaysFlex = 0, + // PowerDownIntRefFlex = 0, + // SwitchFromFlexToStatic = 0 +} /// TODO pub enum Register { - A = 1, - B = 2, - C = 4, - D = 8, - E = 16, - F = 32, - G = 64, - H = 128 + A = 1, + B = 2, + C = 4, + D = 8, + E = 16, + F = 32, + G = 64, + H = 128, } /// TODO pub enum PowerModes { - PowerUp = 0, - PowerDown1KToGround = 16, - PowerDown100KToGround = 32, - PowerDownHighZToGround = 48 + PowerUp = 0, + PowerDown1KToGround = 16, + PowerDown100KToGround = 32, + PowerDownHighZToGround = 48, } /// TODO pub enum InternalRefCommData { - Default = 0, - PowerUpIntRefFlex = 32768, - PowerUpIntRefAlwaysFlex = 40960, - PowerDownIntRefFlex = 49152, + Default = 0, + PowerUpIntRefFlex = 32768, + PowerUpIntRefAlwaysFlex = 40960, + PowerDownIntRefFlex = 49152, } /// - /// The Message that is eventually serialized and transmitted to the DAC +/// The inputshiftregister (SR) of the DAC7568,DAC8168,and DAC8568 +/// is 32 bits wide(as shown in Table1, Table2, and Table3, respectively), +/// and consists of four Prefix bits (DB31 to DB28), +/// four control bits (DB27 to DB24), 16 databits (DB23 to DB4), +/// and four additional feature bits. The 16 databits comprise the 16-, 14-, or 12-bit input code pub struct Message { - feature: u8, // 4 bits - /// Todo, only DAC8568 is supported. DAC7568 = 12, DAC8168 = 14 - data: u16, // data - address: u8, // 4 bits + prefix: u8, // 4 bits control: u8, // 4 bits - prefix: u8, // 4 bits + address: u8, // 4 bits + data: u16, // 16 bits + feature: u8, // 4 bits } impl Message { - fn get_payload(&self) -> [u8; 4] { - [self.prefix, self.control, self.address, (self.data << 8) as u8, (self.data << 0) as u8, self.feature] - } -} - -/// DAC8568 -pub struct Dac { - nss: NSS, - ldac: LDAC, - clear: CLR, - active: bool, -} - -/// DAC Related errors -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum DacError { - /// Unable to write to bus - BusWriteError, -} - - -impl Dac -where - NSS: OutputPin, - LDAC: OutputPin, - CLR: OutputPin, -{ - /// Initialize a new instance of dac8568 - pub fn new(nss: NSS, ldac: LDAC, clear: CLR) -> Self { - Self { - nss, - ldac, - clear, - active: false, - } - } - pub fn get_power_message(mode: PowerModes, channel: u8) -> Message { Message { prefix: 0, @@ -143,7 +124,11 @@ where } } - pub fn get_enable_message(control: ControlType, data: InternalRefCommData, feature: InternalRefCommFeature) -> Message { + pub fn get_enable_message( + control: ControlType, + data: InternalRefCommData, + feature: InternalRefCommFeature, + ) -> Message { Message { prefix: 0, control: control as u8, @@ -153,7 +138,7 @@ where } } - pub fn get_write_message(channel: ChannelSelect, value: u16) -> Message { + pub fn get_write_message(channel: Channel, value: u16) -> Message { Message { prefix: 0, feature: 0, @@ -163,14 +148,48 @@ where } } + fn get_payload(&self) -> [u8; 4] { + let mut payload: u32 = 0x00; + payload = payload | ((self.prefix as u32) << 28); + payload = payload | ((self.control as u32) << 24); + payload = payload | ((self.address as u32) << 20); + payload = payload | ((self.data as u32) << 4); + payload = payload | ((self.feature as u32) << 0); + payload.to_be_bytes() + } +} + +/// DAC8568 +pub struct Dac { + nss: NSS, + active: bool, +} + +/// DAC Related errors +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum DacError { + /// Unable to write to bus + BusWriteError, +} + +impl Dac +where + NSS: OutputPin, +{ + /// Initialize a new instance of dac8568 + pub fn new(nss: NSS) -> Self { + Self { nss, active: false } + } + + pub fn enable(&mut self) { + self.active = true; + } + /// For asynchronous communication methods (e.g. Interrupt or DMA), this function /// prepares the DAC for the transfer, generates the command and passes it back to the initiator /// via the callback parameter - pub fn prepare_transfer ()>( - &mut self, - message: Message, - mut callback: F, - ) { + pub fn prepare_transfer ()>(&mut self, message: Message, mut callback: F) { if !self.active { return; } @@ -179,13 +198,13 @@ where self.nss.set_low().unwrap_or_default(); callback(command); self.nss.set_high().unwrap_or_default(); - } + } /// Write to the DAC via a blocking call on the specified SPI interface pub fn write_blocking( &mut self, spi: &mut dyn embedded_hal::blocking::spi::Write, - message: Message + message: Message, ) -> Result<(), DacError> { if !self.active { return Ok(());