#![macro_use] //! HAL interface to the TWIM peripheral. //! //! See product specification: //! //! - nRF52832: Section 33 //! - nRF52840: Section 6.31 use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::util::{AtomicWaker, Unborrow}; use embassy_extras::unborrow; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; use crate::pac; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{fmt::*, gpio}; pub enum Frequency { #[doc = "26738688: 100 kbps"] K100 = 26738688, #[doc = "67108864: 250 kbps"] K250 = 67108864, #[doc = "104857600: 400 kbps"] K400 = 104857600, } #[non_exhaustive] pub struct Config { pub frequency: Frequency, } impl Default for Config { fn default() -> Self { Self { frequency: Frequency::K100, } } } /// Interface to a TWIM instance. pub struct Twim<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } impl<'d, T: Instance> Twim<'d, T> { pub fn new( _twim: impl Unborrow + 'd, irq: impl Unborrow + 'd, sda: impl Unborrow + 'd, scl: impl Unborrow + 'd, config: Config, ) -> Self { unborrow!(irq, sda, scl); let r = T::regs(); // Configure pins sda.conf().write(|w| { w.dir().input(); w.input().connect(); w.pull().pullup(); w.drive().s0d1(); w }); scl.conf().write(|w| { w.dir().input(); w.input().connect(); w.pull().pullup(); w.drive().s0d1(); w }); // Select pins. r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) }); r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) }); // Enable TWIM instance. r.enable.write(|w| w.enable().enabled()); // Configure frequency. r.frequency .write(|w| unsafe { w.frequency().bits(config.frequency as u32) }); // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); Self { phantom: PhantomData, } } fn on_interrupt(_: *mut ()) { let r = T::regs(); let s = T::state(); if r.events_stopped.read().bits() != 0 { s.end_waker.wake(); r.intenclr.write(|w| w.stopped().clear()); } if r.events_error.read().bits() != 0 { s.end_waker.wake(); r.intenclr.write(|w| w.error().clear()); } } /// Set TX buffer, checking that it is in RAM and has suitable length. unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() == 0 { return Err(Error::TxBufferZeroLength); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); } let r = T::regs(); r.txd.ptr.write(|w| // We're giving the register a pointer to the stack. Since we're // waiting for the I2C transaction to end before this stack pointer // becomes invalid, there's nothing wrong here. // // The PTR field is a full 32 bits wide and accepts the full range // of values. w.ptr().bits(buffer.as_ptr() as u32)); r.txd.maxcnt.write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to `u8` is also fine. // // The MAXCNT field is 8 bits wide and accepts the full range of // values. w.maxcnt().bits(buffer.len() as _)); Ok(()) } /// Set RX buffer, checking that it has suitable length. unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> { // NOTE: RAM slice check is not necessary, as a mutable // slice can only be built from data located in RAM. if buffer.len() == 0 { return Err(Error::RxBufferZeroLength); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::RxBufferTooLong); } let r = T::regs(); r.rxd.ptr.write(|w| // We're giving the register a pointer to the stack. Since we're // waiting for the I2C transaction to end before this stack pointer // becomes invalid, there's nothing wrong here. // // The PTR field is a full 32 bits wide and accepts the full range // of values. w.ptr().bits(buffer.as_mut_ptr() as u32)); r.rxd.maxcnt.write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to the type of maxcnt // is also fine. // // Note that that nrf52840 maxcnt is a wider // type than a u8, so we use a `_` cast rather than a `u8` cast. // The MAXCNT field is thus at least 8 bits wide and accepts the // full range of values that fit in a `u8`. w.maxcnt().bits(buffer.len() as _)); Ok(()) } fn clear_errorsrc(&mut self) { let r = T::regs(); r.errorsrc .write(|w| w.anack().bit(true).dnack().bit(true).overrun().bit(true)); } /// Get Error instance, if any occurred. fn read_errorsrc(&self) -> Result<(), Error> { let r = T::regs(); let err = r.errorsrc.read(); if err.anack().is_received() { return Err(Error::AddressNack); } if err.dnack().is_received() { return Err(Error::DataNack); } if err.overrun().is_received() { return Err(Error::DataNack); } Ok(()) } /// Wait for stop or error fn wait(&mut self) { let r = T::regs(); loop { if r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); break; } if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); } } } /// Write to an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { let r = T::regs(); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // before any DMA action has started. compiler_fence(SeqCst); r.address.write(|w| unsafe { w.address().bits(address) }); // Set up the DMA write. unsafe { self.set_tx_buffer(buffer)? }; // Clear events r.events_stopped.reset(); r.events_error.reset(); r.events_lasttx.reset(); self.clear_errorsrc(); // Start write operation. r.shorts.write(|w| w.lasttx_stop().enabled()); r.tasks_starttx.write(|w| // `1` is a valid value to write to task registers. unsafe { w.bits(1) }); self.wait(); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // after all possible DMA actions have completed. compiler_fence(SeqCst); self.read_errorsrc()?; if r.txd.amount.read().bits() != buffer.len() as u32 { return Err(Error::Transmit); } Ok(()) } /// Read from an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { let r = T::regs(); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // before any DMA action has started. compiler_fence(SeqCst); r.address.write(|w| unsafe { w.address().bits(address) }); // Set up the DMA read. unsafe { self.set_rx_buffer(buffer)? }; // Clear events r.events_stopped.reset(); r.events_error.reset(); self.clear_errorsrc(); // Start read operation. r.shorts.write(|w| w.lastrx_stop().enabled()); r.tasks_startrx.write(|w| // `1` is a valid value to write to task registers. unsafe { w.bits(1) }); self.wait(); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // after all possible DMA actions have completed. compiler_fence(SeqCst); self.read_errorsrc()?; if r.rxd.amount.read().bits() != buffer.len() as u32 { return Err(Error::Receive); } Ok(()) } /// Write data to an I2C slave, then read data from the slave without /// triggering a stop condition between the two. /// /// The buffers must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn write_then_read( &mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8], ) -> Result<(), Error> { let r = T::regs(); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // before any DMA action has started. compiler_fence(SeqCst); r.address.write(|w| unsafe { w.address().bits(address) }); // Set up DMA buffers. unsafe { self.set_tx_buffer(wr_buffer)?; self.set_rx_buffer(rd_buffer)?; } // Clear events r.events_stopped.reset(); r.events_error.reset(); self.clear_errorsrc(); // Start write+read operation. r.shorts.write(|w| { w.lasttx_startrx().enabled(); w.lastrx_stop().enabled(); w }); // `1` is a valid value to write to task registers. r.tasks_starttx.write(|w| unsafe { w.bits(1) }); self.wait(); // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // after all possible DMA actions have completed. compiler_fence(SeqCst); self.read_errorsrc()?; let bad_write = r.txd.amount.read().bits() != wr_buffer.len() as u32; let bad_read = r.rxd.amount.read().bits() != rd_buffer.len() as u32; if bad_write { return Err(Error::Transmit); } if bad_read { return Err(Error::Receive); } Ok(()) } /// Copy data into RAM and write to an I2C slave. /// /// The write buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 1024 bytes on the nRF52840. pub fn copy_write(&mut self, address: u8, wr_buffer: &[u8]) -> Result<(), Error> { if wr_buffer.len() > FORCE_COPY_BUFFER_SIZE { return Err(Error::TxBufferTooLong); } // Copy to RAM let wr_ram_buffer = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; wr_ram_buffer.copy_from_slice(wr_buffer); self.write(address, wr_ram_buffer) } /// Copy data into RAM and write to an I2C slave, then read data from the slave without /// triggering a stop condition between the two. /// /// The write buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 1024 bytes on the nRF52840. /// /// The read buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn copy_write_then_read( &mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8], ) -> Result<(), Error> { if wr_buffer.len() > FORCE_COPY_BUFFER_SIZE { return Err(Error::TxBufferTooLong); } // Copy to RAM let wr_ram_buffer = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; wr_ram_buffer.copy_from_slice(wr_buffer); self.write_then_read(address, wr_ram_buffer, rd_buffer) } } impl<'a, T: Instance> Drop for Twim<'a, T> { fn drop(&mut self) { info!("twim drop"); // TODO when implementing async here, check for abort // disable! let r = T::regs(); r.enable.write(|w| w.enable().disabled()); gpio::deconfigure_pin(r.psel.sda.read().bits()); gpio::deconfigure_pin(r.psel.scl.read().bits()); info!("twim drop: done"); } } impl<'a, T: Instance> embedded_hal::blocking::i2c::Write for Twim<'a, T> { type Error = Error; fn write<'w>(&mut self, addr: u8, bytes: &'w [u8]) -> Result<(), Error> { if slice_in_ram(bytes) { self.write(addr, bytes) } else { let buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..]; for chunk in bytes.chunks(FORCE_COPY_BUFFER_SIZE) { buf[..chunk.len()].copy_from_slice(chunk); self.write(addr, &buf[..chunk.len()])?; } Ok(()) } } } impl<'a, T: Instance> embedded_hal::blocking::i2c::Read for Twim<'a, T> { type Error = Error; fn read<'w>(&mut self, addr: u8, bytes: &'w mut [u8]) -> Result<(), Error> { self.read(addr, bytes) } } impl<'a, T: Instance> embedded_hal::blocking::i2c::WriteRead for Twim<'a, T> { type Error = Error; fn write_read<'w>( &mut self, addr: u8, bytes: &'w [u8], buffer: &'w mut [u8], ) -> Result<(), Error> { if slice_in_ram(bytes) { self.write_then_read(addr, bytes, buffer) } else { self.copy_write_then_read(addr, bytes, buffer) } } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Error { TxBufferTooLong, RxBufferTooLong, TxBufferZeroLength, RxBufferZeroLength, Transmit, Receive, DMABufferNotInDataMemory, AddressNack, DataNack, Overrun, } pub(crate) mod sealed { use super::*; pub struct State { pub end_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { end_waker: AtomicWaker::new(), } } } pub trait Instance { fn regs() -> &'static pac::twim0::RegisterBlock; fn state() -> &'static State; } } pub trait Instance: Unborrow + sealed::Instance + 'static { type Interrupt: Interrupt; } macro_rules! impl_twim { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::twim::sealed::Instance for peripherals::$type { fn regs() -> &'static pac::twim0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } fn state() -> &'static crate::twim::sealed::State { static STATE: crate::twim::sealed::State = crate::twim::sealed::State::new(); &STATE } } impl crate::twim::Instance for peripherals::$type { type Interrupt = crate::interrupt::$irq; } }; }