#![macro_use]
use core::future::{Future, poll_fn};
use core::marker::PhantomData;
use core::sync::atomic::Ordering::SeqCst;
use core::sync::atomic::compiler_fence;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
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::typelevel::Interrupt;
use crate::pac::gpio::vals as gpiovals;
use crate::pac::twis::vals;
use crate::util::slice_in_ram_or;
use crate::{gpio, interrupt, pac};
#[non_exhaustive]
pub struct Config {
pub address0: u8,
pub address1: Option<u8>,
pub orc: u8,
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 {
address0: 0x55,
address1: None,
orc: 0x00,
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))]
enum Status {
Read,
Write,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
TxBufferTooLong,
RxBufferTooLong,
DataNack,
Bus,
BufferNotInRAM,
Overflow,
OverRead,
Timeout,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Command {
Read,
WriteRead(usize),
Write(usize),
}
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_read().read() != 0 || r.events_write().read() != 0 {
s.waker.wake();
r.intenclr().write(|w| {
w.set_read(true);
w.set_write(true);
});
}
if r.events_stopped().read() != 0 {
s.waker.wake();
r.intenclr().write(|w| w.set_stopped(true));
}
if r.events_error().read() != 0 {
s.waker.wake();
r.intenclr().write(|w| w.set_error(true));
}
}
}
pub struct Twis<'d> {
r: pac::twis::Twis,
state: &'static State,
_p: PhantomData<&'d ()>,
}
impl<'d> Twis<'d> {
pub fn new<T: Instance>(
_twis: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sda: Peri<'d, impl GpioPin>,
scl: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
let r = T::regs();
sda.conf().write(|w| {
w.set_dir(gpiovals::Dir::INPUT);
w.set_input(gpiovals::Input::CONNECT);
#[cfg(not(feature = "_nrf54l"))]
w.set_drive(match config.sda_high_drive {
true => gpiovals::Drive::H0D1,
false => gpiovals::Drive::S0D1,
});
#[cfg(feature = "_nrf54l")]
{
w.set_drive0(match config.sda_high_drive {
true => gpiovals::Drive::H,
false => gpiovals::Drive::S,
});
w.set_drive1(gpiovals::Drive::D);
}
if config.sda_pullup {
w.set_pull(gpiovals::Pull::PULLUP);
}
});
scl.conf().write(|w| {
w.set_dir(gpiovals::Dir::INPUT);
w.set_input(gpiovals::Input::CONNECT);
#[cfg(not(feature = "_nrf54l"))]
w.set_drive(match config.scl_high_drive {
true => gpiovals::Drive::H0D1,
false => gpiovals::Drive::S0D1,
});
#[cfg(feature = "_nrf54l")]
{
w.set_drive0(match config.scl_high_drive {
true => gpiovals::Drive::H,
false => gpiovals::Drive::S,
});
w.set_drive1(gpiovals::Drive::D);
}
if config.sda_pullup {
w.set_pull(gpiovals::Pull::PULLUP);
}
});
r.psel().sda().write_value(sda.psel_bits());
r.psel().scl().write_value(scl.psel_bits());
r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));
r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);
r.address(0).write(|w| w.set_address(config.address0));
r.config().write(|w| w.set_address0(true));
if let Some(address1) = config.address1 {
r.address(1).write(|w| w.set_address(address1));
r.config().modify(|w| w.set_address1(true));
}
r.orc().write(|w| w.set_orc(config.orc));
r.shorts().write(|w| w.set_read_suspend(true));
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self {
r: T::regs(),
state: T::state(),
_p: PhantomData,
}
}
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::TxBufferTooLong);
}
let r = self.r;
r.dma().tx().ptr().write_value(buffer.as_ptr() as u32);
r.dma().tx().maxcnt().write(|w|
w.set_maxcnt(buffer.len() as _));
Ok(())
}
unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::RxBufferTooLong);
}
let r = self.r;
r.dma().rx().ptr().write_value(buffer.as_mut_ptr() as u32);
r.dma().rx().maxcnt().write(|w|
w.set_maxcnt(buffer.len() as _));
Ok(())
}
fn clear_errorsrc(&mut self) {
let r = self.r;
r.errorsrc().write(|w| {
w.set_overflow(true);
w.set_overread(true);
w.set_dnack(true);
});
}
pub fn address_match(&self) -> u8 {
let r = self.r;
r.address(r.match_().read().0 as usize).read().address()
}
pub fn address_match_index(&self) -> usize {
self.r.match_().read().0 as _
}
fn blocking_listen_wait(&mut self) -> Result<Status, Error> {
let r = self.r;
loop {
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
while r.events_stopped().read() == 0 {}
return Err(Error::Overflow);
}
if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
return Err(Error::Bus);
}
if r.events_read().read() != 0 {
r.events_read().write_value(0);
return Ok(Status::Read);
}
if r.events_write().read() != 0 {
r.events_write().write_value(0);
return Ok(Status::Write);
}
}
}
fn blocking_listen_wait_end(&mut self, status: Status) -> Result<Command, Error> {
let r = self.r;
loop {
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
return Err(Error::Overflow);
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
return match status {
Status::Read => Ok(Command::Read),
Status::Write => {
let n = r.dma().rx().amount().read().0 as usize;
Ok(Command::Write(n))
}
};
} else if r.events_read().read() != 0 {
r.events_read().write_value(0);
let n = r.dma().rx().amount().read().0 as usize;
return Ok(Command::WriteRead(n));
}
}
}
fn blocking_wait(&mut self) -> Result<usize, Error> {
let r = self.r;
loop {
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
let errorsrc = r.errorsrc().read();
if errorsrc.overread() {
return Err(Error::OverRead);
} else if errorsrc.dnack() {
return Err(Error::DataNack);
} else {
return Err(Error::Bus);
}
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
let n = r.dma().tx().amount().read().0 as usize;
return Ok(n);
}
}
}
#[cfg(feature = "time")]
fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<usize, Error> {
let r = self.r;
let deadline = Instant::now() + timeout;
loop {
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
let errorsrc = r.errorsrc().read();
if errorsrc.overread() {
return Err(Error::OverRead);
} else if errorsrc.dnack() {
return Err(Error::DataNack);
} else {
return Err(Error::Bus);
}
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
let n = r.dma().tx().amount().read().0 as usize;
return Ok(n);
} else if Instant::now() > deadline {
r.tasks_stop().write_value(1);
return Err(Error::Timeout);
}
}
}
#[cfg(feature = "time")]
fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result<Status, Error> {
let r = self.r;
let deadline = Instant::now() + timeout;
loop {
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
while r.events_stopped().read() == 0 {}
return Err(Error::Overflow);
}
if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
return Err(Error::Bus);
}
if r.events_read().read() != 0 {
r.events_read().write_value(0);
return Ok(Status::Read);
}
if r.events_write().read() != 0 {
r.events_write().write_value(0);
return Ok(Status::Write);
}
if Instant::now() > deadline {
r.tasks_stop().write_value(1);
return Err(Error::Timeout);
}
}
}
#[cfg(feature = "time")]
fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result<Command, Error> {
let r = self.r;
let deadline = Instant::now() + timeout;
loop {
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
return Err(Error::Overflow);
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
return match status {
Status::Read => Ok(Command::Read),
Status::Write => {
let n = r.dma().rx().amount().read().0 as usize;
Ok(Command::Write(n))
}
};
} else if r.events_read().read() != 0 {
r.events_read().write_value(0);
let n = r.dma().rx().amount().read().0 as usize;
return Ok(Command::WriteRead(n));
} else if Instant::now() > deadline {
r.tasks_stop().write_value(1);
return Err(Error::Timeout);
}
}
}
fn async_wait(&mut self) -> impl Future<Output = Result<usize, Error>> {
let r = self.r;
let s = self.state;
poll_fn(move |cx| {
s.waker.register(cx.waker());
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
let errorsrc = r.errorsrc().read();
if errorsrc.overread() {
return Poll::Ready(Err(Error::OverRead));
} else if errorsrc.dnack() {
return Poll::Ready(Err(Error::DataNack));
} else {
return Poll::Ready(Err(Error::Bus));
}
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
let n = r.dma().tx().amount().read().0 as usize;
return Poll::Ready(Ok(n));
}
Poll::Pending
})
}
fn async_listen_wait(&mut self) -> impl Future<Output = Result<Status, Error>> {
let r = self.r;
let s = self.state;
poll_fn(move |cx| {
s.waker.register(cx.waker());
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
return Poll::Ready(Err(Error::Overflow));
} else if r.events_read().read() != 0 {
r.events_read().write_value(0);
return Poll::Ready(Ok(Status::Read));
} else if r.events_write().read() != 0 {
r.events_write().write_value(0);
return Poll::Ready(Ok(Status::Write));
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
return Poll::Ready(Err(Error::Bus));
}
Poll::Pending
})
}
fn async_listen_wait_end(&mut self, status: Status) -> impl Future<Output = Result<Command, Error>> {
let r = self.r;
let s = self.state;
poll_fn(move |cx| {
s.waker.register(cx.waker());
if r.events_error().read() != 0 {
r.events_error().write_value(0);
r.tasks_stop().write_value(1);
return Poll::Ready(Err(Error::Overflow));
} else if r.events_stopped().read() != 0 {
r.events_stopped().write_value(0);
return match status {
Status::Read => Poll::Ready(Ok(Command::Read)),
Status::Write => {
let n = r.dma().rx().amount().read().0 as usize;
Poll::Ready(Ok(Command::Write(n)))
}
};
} else if r.events_read().read() != 0 {
r.events_read().write_value(0);
let n = r.dma().rx().amount().read().0 as usize;
return Poll::Ready(Ok(Command::WriteRead(n)));
}
Poll::Pending
})
}
fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> {
let r = self.r;
compiler_fence(SeqCst);
unsafe { self.set_tx_buffer(buffer)? };
r.events_stopped().write_value(0);
r.events_error().write_value(0);
self.clear_errorsrc();
if inten {
r.intenset().write(|w| {
w.set_stopped(true);
w.set_error(true);
});
} else {
r.intenclr().write(|w| {
w.set_stopped(true);
w.set_error(true);
});
}
r.tasks_preparetx().write_value(1);
r.tasks_resume().write_value(1);
Ok(())
}
fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
match self.setup_respond_from_ram(wr_buffer, inten) {
Ok(_) => Ok(()),
Err(Error::BufferNotInRAM) => {
trace!("Copying TWIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_respond_from_ram(tx_ram_buf, inten)
}
Err(error) => Err(error),
}
}
fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> {
let r = self.r;
compiler_fence(SeqCst);
unsafe { self.set_rx_buffer(buffer)? };
r.events_read().write_value(0);
r.events_write().write_value(0);
r.events_stopped().write_value(0);
r.events_error().write_value(0);
self.clear_errorsrc();
if inten {
r.intenset().write(|w| {
w.set_stopped(true);
w.set_error(true);
w.set_read(true);
w.set_write(true);
});
} else {
r.intenclr().write(|w| {
w.set_stopped(true);
w.set_error(true);
w.set_read(true);
w.set_write(true);
});
}
r.tasks_preparerx().write_value(1);
Ok(())
}
fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> {
let r = self.r;
compiler_fence(SeqCst);
r.events_read().write_value(0);
r.events_write().write_value(0);
r.events_stopped().write_value(0);
r.events_error().write_value(0);
self.clear_errorsrc();
if inten {
r.intenset().write(|w| {
w.set_stopped(true);
w.set_error(true);
w.set_read(true);
});
} else {
r.intenclr().write(|w| {
w.set_stopped(true);
w.set_error(true);
w.set_read(true);
});
}
Ok(())
}
pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
self.setup_listen(buffer, false)?;
let status = self.blocking_listen_wait()?;
if status == Status::Write {
self.setup_listen_end(false)?;
let command = self.blocking_listen_wait_end(status)?;
return Ok(command);
}
Ok(Command::Read)
}
pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond(buffer, false)?;
self.blocking_wait()
}
pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, false)?;
self.blocking_wait()
}
#[cfg(feature = "time")]
pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result<Command, Error> {
self.setup_listen(buffer, false)?;
let status = self.blocking_listen_wait_timeout(timeout)?;
if status == Status::Write {
self.setup_listen_end(false)?;
let command = self.blocking_listen_wait_end_timeout(status, timeout)?;
return Ok(command);
}
Ok(Command::Read)
}
#[cfg(feature = "time")]
pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<usize, Error> {
self.setup_respond(buffer, false)?;
self.blocking_wait_timeout(timeout)
}
#[cfg(feature = "time")]
pub fn blocking_respond_to_read_from_ram_timeout(
&mut self,
buffer: &[u8],
timeout: Duration,
) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, false)?;
self.blocking_wait_timeout(timeout)
}
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
self.setup_listen(buffer, true)?;
let status = self.async_listen_wait().await?;
if status == Status::Write {
self.setup_listen_end(true)?;
let command = self.async_listen_wait_end(status).await?;
return Ok(command);
}
Ok(Command::Read)
}
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond(buffer, true)?;
self.async_wait().await
}
pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, true)?;
self.async_wait().await
}
}
impl<'a> Drop for Twis<'a> {
fn drop(&mut self) {
trace!("twis drop");
let r = self.r;
r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));
gpio::deconfigure_pin(r.psel().sda().read());
gpio::deconfigure_pin(r.psel().scl().read());
trace!("twis drop: done");
}
}
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> pac::twis::Twis;
fn state() -> &'static State;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + 'static {
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_twis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::twis::SealedInstance for peripherals::$type {
fn regs() -> pac::twis::Twis {
pac::$pac_type
}
fn state() -> &'static crate::twis::State {
static STATE: crate::twis::State = crate::twis::State::new();
&STATE
}
}
impl crate::twis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}