#![macro_use]
//! HAL interface to the TWIM peripheral.
//!
//! See product specification:
//!
//! - nRF52832: Section 33
//! - nRF52840: Section 6.31
use core::future::{poll_fn, Future};
use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::util::{slice_in_ram, slice_in_ram_or};
use crate::{gpio, pac, Peripheral};
#[derive(Clone, Copy)]
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,
pub sda_high_drive: bool,
pub sda_pullup: bool,
pub scl_high_drive: bool,
pub scl_pullup: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
frequency: Frequency::K100,
scl_high_drive: false,
sda_pullup: false,
sda_high_drive: false,
scl_pullup: false,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
TxBufferTooLong,
RxBufferTooLong,
Transmit,
Receive,
DMABufferNotInDataMemory,
AddressNack,
DataNack,
Overrun,
Timeout,
}
/// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
pub struct Twim<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Twim<'d, T> {
pub fn new(
twim: impl Peripheral
+ 'd,
irq: impl Peripheral
+ 'd,
sda: impl Peripheral
+ 'd,
scl: impl Peripheral
+ 'd,
config: Config,
) -> Self {
into_ref!(twim, irq, sda, scl);
let r = T::regs();
// Configure pins
sda.conf().write(|w| {
w.dir().input();
w.input().connect();
if config.sda_high_drive {
w.drive().h0d1();
} else {
w.drive().s0d1();
}
if config.sda_pullup {
w.pull().pullup();
}
w
});
scl.conf().write(|w| {
w.dir().input();
w.input().connect();
if config.scl_high_drive {
w.drive().h0d1();
} else {
w.drive().s0d1();
}
if config.scl_pullup {
w.pull().pullup();
}
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 { _p: twim }
}
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() > 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() > 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 check_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(())
}
fn check_rx(&self, len: usize) -> Result<(), Error> {
let r = T::regs();
if r.rxd.amount.read().bits() != len as u32 {
Err(Error::Receive)
} else {
Ok(())
}
}
fn check_tx(&self, len: usize) -> Result<(), Error> {
let r = T::regs();
if r.txd.amount.read().bits() != len as u32 {
Err(Error::Transmit)
} else {
Ok(())
}
}
/// Wait for stop or error
fn blocking_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) });
}
}
}
/// Wait for stop or error
#[cfg(feature = "time")]
fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
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) });
}
if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
Ok(())
}
/// Wait for stop or error
fn async_wait(&mut self) -> impl Future