#![no_std] use core::{marker::PhantomData, ops::Range}; pub type LPSPI = ral_registers::Instance; #[repr(C)] #[allow(non_snake_case)] pub struct RegisterBlock { pub VERID: u32, pub PARAM: u32, reserved_0: [u8; 8], pub CR: u32, pub SR: u32, pub IER: u32, pub DER: u32, pub CFGR0: u32, pub CFGR1: u32, reserved_1: [u8; 8], pub DMR0: u32, pub DMR1: u32, reserved_2: [u8; 8], pub CCR: u32, reserved_3: [u8; 20], pub FCR: u32, pub FSR: u32, pub TCR: u32, pub TDR: u32, reserved_4: [u8; 8], pub RSR: u32, pub RDR: u32, } ral_registers::register! { #[doc = "Version ID Register"] pub VERID RO [ #[doc = "Module Identification Number"] FEATURE start(0) width(16) RO { #[doc = "Standard feature set supporting a 32-bit shift register."] FEATURE_4 = 0x4, } #[doc = "Minor Version Number"] MINOR start(16) width(8) RO {} #[doc = "Major Version Number"] MAJOR start(24) width(8) RO {} ] } ral_registers::register! { #[doc = "Parameter Register"] pub PARAM RO [ #[doc = "Transmit FIFO Size"] TXFIFO start(0) width(8) RO {} #[doc = "Receive FIFO Size"] RXFIFO start(8) width(8) RO {} #[doc = "PCS Number"] PCSNUM start(16) width(8) RO {} ] } ral_registers::register! { #[doc = "Control Register"] pub CR RW [ #[doc = "Module Enable"] MEN start(0) width(1) RW {} #[doc = "Software Reset"] RST start(1) width(1) RW {} #[doc = "Doze mode enable"] DOZEN start(2) width(1) RW {} #[doc = "Debug Enable"] DBGEN start(3) width(1) RW {} #[doc = "Reset Transmit FIFO"] RTF start(8) width(1) WO {} #[doc = "Reset Receive FIFO"] RRF start(9) width(1) WO {} ] } ral_registers::register! { #[doc = "Status Register"] pub SR RW [ #[doc = "Transmit Data Flag"] TDF start(0) width(1) RO {} #[doc = "Receive Data Flag"] RDF start(1) width(1) RO {} #[doc = "Word Complete Flag"] WCF start(8) width(1) RW {} #[doc = "Frame Complete Flag"] FCF start(9) width(1) RW {} #[doc = "Transfer Complete Flag"] TCF start(10) width(1) RW {} #[doc = "Transmit Error Flag"] TEF start(11) width(1) RW {} #[doc = "Receive Error Flag"] REF start(12) width(1) RW {} #[doc = "Data Match Flag"] DMF start(13) width(1) RW {} #[doc = "Module Busy Flag"] MBF start(24) width(1) RO {} ] } ral_registers::register! { #[doc = "Interrupt Enable Register"] pub IER RW [ #[doc = "Transmit Data Interrupt Enable"] TDIE start(0) width(1) RW {} #[doc = "Receive Data Interrupt Enable"] RDIE start(1) width(1) RW {} #[doc = "Word Complete Interrupt Enable"] WCIE start(8) width(1) RW {} #[doc = "Frame Complete Interrupt Enable"] FCIE start(9) width(1) RW {} #[doc = "Transfer Complete Interrupt Enable"] TCIE start(10) width(1) RW {} #[doc = "Transmit Error Interrupt Enable"] TEIE start(11) width(1) RW {} #[doc = "Receive Error Interrupt Enable"] REIE start(12) width(1) RW {} #[doc = "Data Match Interrupt Enable"] DMIE start(13) width(1) RW {} ] } ral_registers::register! { #[doc = "DMA Enable Register"] pub DER RW [ #[doc = "Transmit Data DMA Enable"] TDDE start(0) width(1) RW {} #[doc = "Receive Data DMA Enable"] RDDE start(1) width(1) RW {} ] } ral_registers::register! { #[doc = "Configuration Register 0"] pub CFGR0 RW [ #[doc = "Host Request Enable"] HREN start(0) width(1) RW {} #[doc = "Host Request Polarity"] HRPOL start(1) width(1) RW { #[doc = "Active low"] ACTIVE_LOW = 0, #[doc = "Active high"] ACTIVE_HIGH = 0x1, } #[doc = "Host Request Select"] HRSEL start(2) width(1) RW { #[doc = "Host request input is the LPSPI_HREQ pin"] LPSPI_HREQ = 0, #[doc = "Host request input is the input trigger"] INPUT_TRIGGER = 0x1, } #[doc = "Circular FIFO Enable"] CIRFIFO start(8) width(1) RW {} #[doc = "Receive Data Match Only"] RDMO start(9) width(1) RW { #[doc = "Received data is stored in the receive FIFO as in normal operations"] STORE = 0, #[doc = "Received data is discarded unless the Data Match Flag (DMF) is set"] DISCARD = 0x1, } ] } ral_registers::register! { #[doc = "Configuration Register 1"] pub CFGR1 RW [ #[doc = "Controller Mode"] CONTROLLER start(0) width(1) RW { #[doc = "Peripheral mode"] PERIPHERAL = 0, #[doc = "Controller mode"] CONTROLLER = 0x1, } #[doc = "Sample Point"] SAMPLE start(1) width(1) RW { #[doc = "Input data is sampled on SCK edge"] ON_EDGE = 0, #[doc = "Input data is sampled on delayed SCK edge"] DELAYED_EDGE = 0x1, } #[doc = "Automatic PCS"] AUTOPCS start(2) width(1) RW {} #[doc = "No Stall"] NOSTALL start(3) width(1) RW { #[doc = "Transfers will stall when the transmit FIFO is empty or the receive FIFO is full"] STALL = 0, #[doc = "Transfers will not stall, allowing transmit FIFO underruns or receive FIFO overruns to occur"] NOSTALL = 0x1, } #[doc = "Peripheral Chip Select Polarity"] PCSPOL start(8) width(4) RW {} #[doc = "Match Configuration"] #[doc = ""] #[doc = "`+` is boolean OR. `*` is boolean AND`. The `*_SINGLE` variants never match unless you set the MATCH registers to the same value."] MATCFG start(16) width(3) RW { #[doc = "Match is disabled"] DISABLE = 0, #[doc = "010b - Match is enabled, if 1st data word equals MATCH0 OR MATCH1, i.e., (1st data word = MATCH0 + MATCH1)"] FIRST_WORD_EITHER = 0x2, #[doc = "011b - Match is enabled, if any data word equals MATCH0 OR MATCH1, i.e., (any data word = MATCH0 + MATCH1)"] ANY_WORD_EITHER = 0x3, #[doc = "100b - Match is enabled, if 1st data word equals MATCH0 AND 2nd data word equals MATCH1, i.e., [(1st data word = MATCH0) * (2nd data word = MATCH1)]"] FIRST_WORD_BOTH = 0x4, #[doc = "101b - Match is enabled, if any data word equals MATCH0 AND the next data word equals MATCH1, i.e., [(any data word = MATCH0) * (next data word = MATCH1)]"] ANY_WORD_BOTH = 0x5, #[doc = "110b - Match is enabled, if (1st data word AND MATCH1) equals (MATCH0 AND MATCH1), i.e., [(1st data word * MATCH1) = (MATCH0 * MATCH1)]"] FIRST_WORD_SINGLE = 0x6, #[doc = "111b - Match is enabled, if (any data word AND MATCH1) equals (MATCH0 AND MATCH1), i.e., [(any data word * MATCH1) = (MATCH0 * MATCH1)]"] ANY_WORD_SINGLE = 0x7, } #[doc = "Pin Configuration"] PINCFG start(24) width(2) RW { #[doc = "SIN is used for input data and SOUT is used for output data"] FULL_DUPLEX = 0, #[doc = "SIN is used for both input and output data"] HALF_DUPLEX_SIN = 0x1, #[doc = "SOUT is used for both input and output data"] HALF_DUPLEX_SOUT = 0x2, #[doc = "SOUT is used for input data and SIN is used for output data"] INVERSE = 0x3, } #[doc = "Output Config"] OUTCFG start(26) width(1) RW { #[doc = "Output data retains last value when chip select is negated"] HOLD = 0, #[doc = "Output data is tristated when chip select is negated"] TRISTATE = 0x1, } #[doc = "Peripheral Chip Select Configuration"] PCSCFG start(27) width(1) RW {} ] } ral_registers::register! { #[doc = "Data Match Register 0"] pub DMR0 RW [] } ral_registers::register! { #[doc = "Data Match Register 1"] pub DMR1 RW [] } ral_registers::register! { #[doc = "Clock Configuration Register"] pub CCR RW [ #[doc = "SCK Divider"] SCKDIV start(0) width(8) RW {} #[doc = "Delay Between Transfers"] DBT start(8) width(8) RW {} #[doc = "PCS-to-SCK Delay"] PCSSCK start(16) width(8) RW {} #[doc = "SCK-to-PCS Delay"] SCKPCS start(24) width(8) RW {} ] } ral_registers::register! { #[doc = "FIFO Control Register"] pub FCR RW [ #[doc = "Transmit FIFO Watermark"] TXWATER start(0) width(4) RW {} #[doc = "Receive FIFO Watermark"] RXWATER start(16) width(4) RW {} ] } ral_registers::register! { #[doc = "FIFO Status Register"] pub FSR RO [ #[doc = "Transmit FIFO Count"] TXCOUNT start(0) width(5) RO {} #[doc = "Receive FIFO Count"] RXCOUNT start(16) width(5) RO {} ] } ral_registers::register! { #[doc = "Transmit Command Register"] pub TCR RW [ #[doc = "Frame Size"] FRAMESZ start(0) width(12) RW {} #[doc = "Transfer Width"] WIDTH start(16) width(2) RW { #[doc = "1 bit transfer"] SINGLE = 0, #[doc = "2 bit transfer"] DUAL = 0x1, #[doc = "4 bit transfer"] QUAD = 0x2, } #[doc = "Transmit Data Mask"] TXMSK start(18) width(1) RW {} #[doc = "Receive Data Mask"] RXMSK start(19) width(1) RW {} #[doc = "Continuing Command"] CONTC start(20) width(1) RW {} #[doc = "Continuous Transfer"] CONT start(21) width(1) RW {} #[doc = "Byte Swap"] BYSW start(22) width(1) RW {} #[doc = "LSB First"] LSBF start(23) width(1) RW { #[doc = "Data is transferred MSB first"] MSB = 0, #[doc = "Data is transferred LSB first"] LSB = 0x1, } #[doc = "Peripheral Chip Select"] PCS start(24) width(2) RW { #[doc = "Transfer using LPSPI_PCS[0]"] PCS_0 = 0, #[doc = "Transfer using LPSPI_PCS[1]"] PCS_1 = 0x1, #[doc = "Transfer using LPSPI_PCS[2]"] PCS_2 = 0x2, #[doc = "Transfer using LPSPI_PCS[3]"] PCS_3 = 0x3, } #[doc = "Prescaler Value"] PRESCALE start(27) width(3) RW { #[doc = "Divide by 1"] DIVIDE_BY_1= 0, #[doc = "Divide by 2"] DIVIDE_BY_2= 0x1, #[doc = "Divide by 4"] DIVIDE_BY_4= 0x2, #[doc = "Divide by 8"] DIVIDE_BY_8= 0x3, #[doc = "Divide by 16"] DIVIDE_BY_16= 0x4, #[doc = "Divide by 32"] DIVIDE_BY_32= 0x5, #[doc = "Divide by 64"] DIVIDE_BY_64= 0x6, #[doc = "Divide by 128"] DIVIDE_BY_128= 0x7, } #[doc = "Clock Phase"] CPHA start(30) width(1) RW { #[doc = "Data is captured on the leading edge of SCK and changed on the following edge of SCK"] CAPTURE_LEADING_SETUP_FOLLOWING = 0, #[doc = "Data is changed on the leading edge of SCK and captured on the following edge of SCK"] SETUP_LEADING_CAPTURE_FOLLOWING = 0x1, } #[doc = "Clock Polarity"] CPOL start(31) width(1) RW { #[doc = "The inactive state value of SCK is low"] INACTIVE_LOW = 0, #[doc = "The inactive state value of SCK is high"] INACTIVE_HIGH = 0x1, } ] } ral_registers::register! { #[doc = "Transmit Data Register"] pub TDR WO [] } ral_registers::register! { #[doc = "Receive Status Register"] pub RSR RO [ #[doc = "Start Of Frame"] SOF start(0) width(1) RO { #[doc = "Subsequent data word received after LPSPI_PCS assertion"] SUBSEQUENT = 0, #[doc = "First data word received after LPSPI_PCS assertion"] FIRST = 0x1, } #[doc = "RX FIFO Empty"] RXEMPTY start(1) width(1) RO {} ] } ral_registers::register! { #[doc = "Receive Data Register"] pub RDR RO [] } /// A command for the next LPSPI transfer. #[derive(Debug, Clone, Copy)] #[repr(transparent)] pub struct TransmitCommand(u32); impl TransmitCommand { /// Create a transmit command for a single frame. /// /// `frame_size_bits` describes the number of bits to transmit within /// a single transmission. `frame_size_bits` must be at least 8 bits /// large, but no larger than 4096 bits. The device's native word is /// 32 bits large. If there are more than 32 bits to transfer, there /// must be more than one bit remaining. /// /// If the frame size is valid, this returns the command for further /// configuration. Otherwise, this returns `None`. pub const fn for_frame(frame_size_bits: usize) -> Option { const MIN_FRAME_SIZE: usize = 8; const MAX_FRAME_SIZE: usize = 1 << 12; const WORD_SIZE_BITS: usize = 32; if MIN_FRAME_SIZE <= frame_size_bits && frame_size_bits <= MAX_FRAME_SIZE && frame_size_bits % WORD_SIZE_BITS != 1 { Some(Self(frame_size_bits as u32 - 1)) } else { None } } /// Construct a command for the given bytes. pub const fn for_u8s(bytes: &[u8]) -> Option { Self::for_frame(size_of_val(bytes) * 8) } /// Construct a command for the given `u16`s. pub const fn for_u16s(halves: &[u16]) -> Option { Self::for_frame(size_of_val(halves) * 8) } /// Construct a command for the given `u32`s. pub const fn for_u32s(words: &[u32]) -> Option { Self::for_frame(size_of_val(words) * 8) } /// Set the transfer width. /// /// By default, the width is 1 bit. pub const fn with_width(mut self, width: TransferWidth) -> Self { self.0 &= !(0b11 << 16); self.0 |= (width as u32) << 16; self } /// Mask or unmask the transmit data. /// /// By default, transmit data is not masked. pub const fn with_transmit_mask(mut self, mask: bool) -> Self { self.0 &= !(1 << 18); self.0 |= (mask as u32) << 18; self } /// Mask or unmask the receive data. /// /// By default, receive data is not masked. pub const fn with_receive_mask(mut self, mask: bool) -> Self { self.0 &= !(1 << 19); self.0 |= (mask as u32) << 19; self } pub const fn with_continuing(mut self, continuing: bool) -> Self { self.0 &= !(1 << 20); self.0 |= (continuing as u32) << 20; self } pub const fn with_continuous(mut self, continuous: bool) -> Self { self.0 &= !(1 << 21); self.0 |= (continuous as u32) << 21; self } pub const fn with_byte_swap(mut self, swap: bool) -> Self { self.0 &= !(1 << 22); self.0 |= (swap as u32) << 22; self } pub const fn with_lsb_first(mut self, lsb: bool) -> Self { self.0 &= !(1 << 23); self.0 |= (lsb as u32) << 23; self } pub const fn with_chip_select(mut self, pcs: Pcs) -> Self { self.0 &= !(0b11 << 24); self.0 |= (pcs as u32) << 24; self } pub const fn with_prescale(mut self, prescale: Prescale) -> Self { self.0 &= !(0b111 << 27); self.0 |= (prescale as u32) << 27; self } pub const fn with_clock_phase(mut self, phase: ClockPhase) -> Self { self.0 &= !(1 << 30); self.0 |= (phase as u32) << 30; self } pub const fn with_clock_polarity(mut self, polarity: ClockPolarity) -> Self { self.0 &= !(1 << 31); self.0 |= (polarity as u32) << 31; self } /// Returns the raw command value. pub const fn get(self) -> u32 { self.0 } } /// Write the transmit command. /// /// This doesn't check the TX FIFO for space to receive /// the command. #[inline] pub fn write_transmit_command(lpspi: LPSPI, cmd: TransmitCommand) { ral_registers::write_reg!(self, lpspi, TCR, cmd.0); } /// The resting state of clock. /// /// Updates when PCS negates. #[derive(Default, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum ClockPolarity { /// Idle the clock low. #[default] InactiveLow, /// Idle the clock high. InactiveHigh, } /// Describes the clock edge that captures and changes data. #[derive(Default, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum ClockPhase { /// Captured on leading, changed on following. #[default] Captured, /// Changed on leading, captured on following. Changed, } /// Divider applied to all properties in the clock /// configuration register. #[derive(Default, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum Prescale { #[default] Div1, Div2, Div4, Div8, Div16, Div32, Div64, Div128, } /// The chip select used for the transfer. /// /// You're responsible for muxing this pin and making /// sure it's supported for your peripheral instance. #[derive(Default, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum Pcs { #[default] Pcs0, Pcs1, Pcs2, Pcs3, } /// Describes the data transfer width. /// /// Nominal SPI is [`Bit1`]. Dual and quad SPI can /// use additional bits. #[derive(Default, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum TransferWidth { /// One bit for data transfer. #[default] Bit1, /// Two bits for data transfer. Bit2, /// Four bits for data transfer. Bit4, } /// Provides words for LPSPI transmits. pub trait TxWordSource { /// Returns how many words are available in this /// source. fn words_available(&self) -> usize; /// Produces the next word, or an unspecified /// value if there is no next word. fn next_word(&mut self) -> u32; /// Returns `true` if this source can provide /// another word. fn has_word(&self) -> bool { self.words_available() != 0 } } /// A destination for LPSPI receives. pub trait RxWordSink { /// Returns how many words remain unoccupied /// in this sink. fn words_available(&self) -> usize; /// Store the next word, or do nothing if there /// is no more space. fn next_word(&mut self, word: u32); /// Returns `true` if this sink can accept another /// word. fn has_word(&self) -> bool { self.words_available() != 0 } } /// Produces LPSPI words for transmit. /// /// The LPSPI word size is 32 bits. Stuffing smaller /// 8 or 16 bit primitives into that 32 bit word requires /// the CPU. This type helps with that stuffing. /// /// Use [`next_word`](Self::next_word) to access the next /// available word from your slice. You can write this /// word directly to the LPSPI TX FIFO. /// /// To perform a bidirectional I/O with a single buffer, /// use [`bidirectional_u8s`], [`bidirectional_u16s`], or /// [`bidirectional_u32s`]. pub struct TxWords<'a, W> { ptr: *const W, end: *const W, _buffer: PhantomData<&'a W>, } impl<'a, W> TxWords<'a, W> { fn new(ptr: *const W, end: *const W) -> Self { Self { ptr, end, _buffer: PhantomData, } } fn bytes_available(&self) -> usize { // Safety: pointers remain in range of the same allocation. // Module inspection shows that ptr never crosses beyond // end. unsafe { self.end.byte_offset_from_unsigned(self.ptr) } } fn words_available(&self) -> usize { self.bytes_available().div_ceil(size_of::()) } fn has_word(&self) -> bool { self.ptr != self.end } } impl<'a> TxWords<'a, u8> { /// Transmit words from a slice of bytes. pub fn from_u8s(bytes: &'a [u8]) -> Self { let Range { start, end } = bytes.as_ptr_range(); Self::new(start, end) } } impl TxWordSource for TxWords<'_, u8> { fn words_available(&self) -> usize { TxWords::words_available(self) } fn has_word(&self) -> bool { TxWords::has_word(self) } fn next_word(&mut self) -> u32 { // Safety: access through pointers guarded by the // number of bytes available in that pointer. Reads // are aligned for a u8 and valid. unsafe { let size = self.bytes_available().min(size_of::()); let mut word = 0_u32; if size == 4 { word |= u32::from(self.ptr.add(0).read()) << 24; word |= u32::from(self.ptr.add(1).read()) << 16; word |= u32::from(self.ptr.add(2).read()) << 8; word |= u32::from(self.ptr.add(3).read()); } else if size == 3 { word |= u32::from(self.ptr.add(0).read()) << 16; word |= u32::from(self.ptr.add(1).read()) << 8; word |= u32::from(self.ptr.add(2).read()); } else if size == 2 { word |= u32::from(self.ptr.add(0).read()) << 8; word |= u32::from(self.ptr.add(1).read()); } else if size == 1 { word |= u32::from(self.ptr.read()); } self.ptr = self.ptr.add(size); word } } } impl<'a> TxWords<'a, u32> { /// Transmit words from a slice of words. pub fn from_u32s(words: &'a [u32]) -> Self { let Range { start, end } = words.as_ptr_range(); Self::new(start, end) } } impl TxWordSource for TxWords<'_, u32> { fn words_available(&self) -> usize { TxWords::words_available(self) } fn has_word(&self) -> bool { TxWords::has_word(self) } fn next_word(&mut self) -> u32 { // Safety: bounds check prevents out of bounds // read. Module inspection shows that ptr never // extends beyond end. Pointer is aligned and // valid for reads given the public API required // to construct this type. unsafe { if self.ptr == self.end { return 0; } let word = self.ptr.read(); self.ptr = self.ptr.add(1); word } } } /// Place received LPSPI words into a buffer. /// /// The LPSPI word size is 32 bits. Separating that word /// into smaller 8 or 16 bit primitives requires the CPU. /// /// After reading the LPSPI RX FIFO, use [`set_next_word`](Self::next_word) /// to store it into the user's buffer. /// /// To perform a bidirectional I/O with a single buffer, /// use [`bidirectional_u8s`], [`bidirectional_u16s`], or /// [`bidirectional_u32s`]. pub struct RxWords<'a, W> { ptr: *mut W, end: *mut W, _buffer: PhantomData<&'a W>, } impl<'a, W> RxWords<'a, W> { fn new(ptr: *mut W, end: *mut W) -> Self { Self { ptr, end, _buffer: PhantomData, } } fn bytes_available(&self) -> usize { // Safety: pointers remain in range of the same allocation. // Module inspection shows that ptr never crosses beyond // end. unsafe { self.end.byte_offset_from_unsigned(self.ptr) } } fn words_available(&self) -> usize { self.bytes_available().div_ceil(size_of::()) } fn has_word(&self) -> bool { self.ptr != self.end } } impl<'a> RxWords<'a, u8> { /// Read data into a byte slice. pub fn from_u8s(bytes: &'a mut [u8]) -> Self { let Range { start, end } = bytes.as_mut_ptr_range(); Self::new(start, end) } } impl RxWordSink for RxWords<'_, u8> { fn words_available(&self) -> usize { RxWords::words_available(self) } fn has_word(&self) -> bool { RxWords::has_word(self) } fn next_word(&mut self, word: u32) { // Safety: access through pointer guarded by the number // of bytes available through that pointer. Similarly, // pointer offset guaded by the available bytes in the // pointer. Access of u8s, u16s, and u32s are all valid // through u8 access. unsafe { let size = self.bytes_available().min(size_of_val(&word)); if size == 4 { self.ptr.add(0).write((word >> 24) as u8); self.ptr.add(1).write((word >> 16) as u8); self.ptr.add(2).write((word >> 8) as u8); self.ptr.add(3).write(word as u8); } else if size == 3 { self.ptr.add(0).write((word >> 16) as u8); self.ptr.add(1).write((word >> 8) as u8); self.ptr.add(2).write(word as u8); } else if size == 2 { self.ptr.add(0).write((word >> 8) as u8); self.ptr.add(1).write(word as u8); } else if size == 1 { self.ptr.write(word as u8); } self.ptr = self.ptr.add(size); } } } impl<'a> RxWords<'a, u32> { /// Read data into a word slice. pub fn from_u32s(words: &'a mut [u32]) -> Self { let Range { start, end } = words.as_mut_ptr_range(); Self::new(start, end) } } impl RxWordSink for RxWords<'_, u32> { fn words_available(&self) -> usize { RxWords::words_available(self) } fn has_word(&self) -> bool { RxWords::has_word(self) } fn next_word(&mut self, word: u32) { // Safety: pointer remains in bound of allocation, // aligned for u32 access, and valid for writes. unsafe { if self.ptr != self.end { self.ptr.write(word); self.ptr = self.ptr.add(1); } } } } /// Transmit and receive data from this byte slice. pub fn bidirectional_u8s<'a>(bytes: &'a mut [u8]) -> (TxWords<'a, u8>, RxWords<'a, u8>) { let Range { start, end } = bytes.as_mut_ptr_range(); (TxWords::new(start, end), RxWords::new(start, end)) } /// Transmit and receive data from this word slice. pub fn bidirectional_u32s<'a>(words: &'a mut [u32]) -> (TxWords<'a, u32>, RxWords<'a, u32>) { let Range { start, end } = words.as_mut_ptr_range(); (TxWords::new(start, end), RxWords::new(start, end)) }