diff --git a/src/initialize.rs b/src/initialize.rs
index 7be9aaead7b3d0c0bc566c3a5b467ad064fa7106..7c2abcd59767ac16178878df6e31fceb3d0bd8b1 100644
--- a/src/initialize.rs
+++ b/src/initialize.rs
@@ -62,7 +62,7 @@ pub fn initialize(heap_memory: &'static mut [MaybeUninit<u8>], debug: bool) {
if debug {
let _ = send_bytes(b"RTC driver initialized\n");
}
- twi::initialize(nrf51_peripherals.TWI0, gpio.p0_04, gpio.p0_02);
+ twi::initialize(nrf51_peripherals.TWI0, gpio.p0_04, gpio.p0_02, &mut cortex_m_peripherals.NVIC);
if debug {
let _ = send_bytes(b"TWI initialized\n");
}
diff --git a/src/twi.rs b/src/twi.rs
index 62ced56c8f7ae96d4e773847cf72fe93d58356cf..25ed4983e308c66d2a7e0d3e490d9c864d093051 100644
--- a/src/twi.rs
+++ b/src/twi.rs
@@ -1,10 +1,13 @@
+use core::sync::atomic::{AtomicBool, Ordering};
+use cortex_m::peripheral::NVIC;
use crate::mutex::Mutex;
use crate::once_cell::OnceCell;
use nrf51_hal::gpio::p0::{P0_02, P0_04};
use nrf51_hal::gpio::{Disconnected, Pin};
use nrf51_hal::twi::Error;
use nrf51_pac::twi0::frequency::FREQUENCY_A;
-use nrf51_pac::{GPIO, TWI0};
+use nrf51_pac::{GPIO, Interrupt, TWI0};
+use nrf51_pac::interrupt;
const FREQ: FREQUENCY_A = FREQUENCY_A::K400;
@@ -12,143 +15,31 @@ pub(crate) static TWI: Mutex<OnceCell<TwiWrapper>> = Mutex::new(OnceCell::uninit
pub struct TwiWrapper {
twi: TWI0,
-}
-
-impl TwiWrapper {
- fn send_byte(&self, byte: u8) -> Result<(), Error> {
- // Clear sent event.
- self.twi.events_txdsent.write(|w| unsafe { w.bits(0) });
-
- // Copy data into the send buffer.
- self.twi.txd.write(|w| unsafe { w.bits(u32::from(byte)) });
-
- // Wait until transmission was confirmed.
- while self.twi.events_txdsent.read().bits() == 0 {
- // Bail out if we get an error instead.
- if self.twi.events_error.read().bits() != 0 {
- self.twi.events_error.write(|w| unsafe { w.bits(0) });
- return Err(Error::Transmit);
- }
- }
-
- // Clear sent event.
- self.twi.events_txdsent.write(|w| unsafe { w.bits(0) });
-
- Ok(())
- }
-
- fn recv_byte(&self) -> Result<u8, Error> {
- // Wait until something ended up in the buffer.
- while self.twi.events_rxdready.read().bits() == 0 {
- // Bail out if it's an error instead of data.
- if self.twi.events_error.read().bits() != 0 {
- self.twi.events_error.write(|w| unsafe { w.bits(0) });
- return Err(Error::Receive);
- }
- }
-
- // Read out data.
- let out = self.twi.rxd.read().bits() as u8;
-
- // Clear reception event.
- self.twi.events_rxdready.write(|w| unsafe { w.bits(0) });
-
- Ok(out)
- }
-
- fn send_stop(&self) -> Result<(), Error> {
- // Clear stopped event.
- self.twi.events_stopped.write(|w| unsafe { w.bits(0) });
-
- // Start stop condition.
- self.twi.tasks_stop.write(|w| unsafe { w.bits(1) });
-
- // Wait until stop was sent.
- while self.twi.events_stopped.read().bits() == 0 {
- // Bail out if we get an error instead.
- if self.twi.events_error.read().bits() != 0 {
- self.twi.events_error.write(|w| unsafe { w.bits(0) });
- return Err(Error::Transmit);
- }
- }
-
- Ok(())
- }
+ sent: AtomicBool,
+ rec: AtomicBool,
}
impl embedded_hal::blocking::i2c::Write for TwiWrapper {
type Error = Error;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
- cortex_m::interrupt::free(|_| {
- // Make sure all previously used shortcuts are disabled.
- self.twi
- .shorts
- .write(|w| w.bb_stop().disabled().bb_suspend().disabled());
-
- // Set Slave I2C address.
- self.twi
- .address
- .write(|w| unsafe { w.address().bits(addr.into()) });
-
- // Start data transmission.
- self.twi.tasks_starttx.write(|w| unsafe { w.bits(1) });
-
- // Clock out all bytes.
- for byte in bytes {
- self.send_byte(*byte)?;
- }
-
- // Send stop.
- self.send_stop()?;
- Ok(())
- })
- }
-}
-
-impl embedded_hal::blocking::i2c::Read for TwiWrapper {
- type Error = Error;
-
- fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> {
- cortex_m::interrupt::free(|_| {
- // Make sure all previously used shortcuts are disabled.
- self.twi
- .shorts
- .write(|w| w.bb_stop().disabled().bb_suspend().disabled());
-
- // Set Slave I2C address.
- self.twi
- .address
- .write(|w| unsafe { w.address().bits(addr.into()) });
-
- // Read into buffer.
- if let Some((last, before)) = bytes.split_last_mut() {
- // If we want to read multiple bytes we need to use the suspend mode.
- if !before.is_empty() {
- self.twi.shorts.write(|w| w.bb_suspend().enabled());
- } else {
- self.twi.shorts.write(|w| w.bb_stop().enabled());
- }
-
- // Clear reception event.
- self.twi.events_rxdready.write(|w| unsafe { w.bits(0) });
-
- // Start data reception.
- self.twi.tasks_startrx.write(|w| unsafe { w.bits(1) });
-
- for byte in &mut before.into_iter() {
- self.twi.tasks_resume.write(|w| unsafe { w.bits(1) });
- *byte = self.recv_byte()?;
- }
+ // Clear sent/rec in case some event has been received in the meantime
+ self.sent.store(false, Ordering::SeqCst);
+
+ // Setup for task
+ self.twi.address.write(|w| unsafe { w.address().bits(addr) });
+ self.twi.shorts.write(|w| w.bb_stop().disabled().bb_suspend().disabled());
+
+ // Write data
+ self.twi.tasks_starttx.write(|w| unsafe { w.bits(1) });
+ for byte in bytes {
+ self.twi.txd.write(|w| w.txd().variant(*byte));
+ while !self.sent.load(Ordering::SeqCst) {}
+ self.sent.store(false, Ordering::SeqCst);
+ }
+ self.twi.tasks_stop.write(|w| unsafe { w.bits(1) });
- self.twi.shorts.write(|w| w.bb_stop().enabled());
- self.twi.tasks_resume.write(|w| unsafe { w.bits(1) });
- *last = self.recv_byte()?;
- } else {
- self.send_stop()?;
- }
- Ok(())
- })
+ Ok(())
}
}
@@ -161,57 +52,44 @@ impl embedded_hal::blocking::i2c::WriteRead for TwiWrapper {
bytes: &'w [u8],
buffer: &'w mut [u8],
) -> Result<(), Error> {
- cortex_m::interrupt::free(|_| {
- // Make sure all previously used shortcuts are disabled.
- self.twi
- .shorts
- .write(|w| w.bb_stop().disabled().bb_suspend().disabled());
-
- // Set Slave I2C address.
- self.twi
- .address
- .write(|w| unsafe { w.address().bits(addr.into()) });
-
- // Start data transmission.
- self.twi.tasks_starttx.write(|w| unsafe { w.bits(1) });
+ // Clear sent/rec in case some event has been received in the meantime
+ self.sent.store(false, Ordering::SeqCst);
+ self.rec.store(false, Ordering::SeqCst);
+
+ // Setup for task
+ self.twi.address.write(|w| unsafe { w.address().bits(addr) });
+ self.twi.shorts.write(|w| w.bb_stop().disabled().bb_suspend().disabled());
+ self.twi.tasks_starttx.write(|w| unsafe { w.bits(1) });
+
+ // Write data
+ self.twi.tasks_starttx.write(|w| unsafe { w.bits(1) });
+ for byte in bytes {
+ self.twi.txd.write(|w| w.txd().variant(*byte));
+ while !self.sent.load(Ordering::SeqCst) {}
+ self.sent.store(false, Ordering::SeqCst);
+ }
- // Send out all bytes in the outgoing buffer.
- for byte in bytes {
- self.send_byte(*byte)?;
+ // Read data
+ self.twi.shorts.write(|w| w.bb_suspend().set_bit());
+ self.twi.tasks_startrx.write(|w| unsafe { w.bits(1) });
+ let last = buffer.len() - 1;
+ for (i, byte) in buffer.iter_mut().enumerate() {
+ // After the last byte, stop tasks
+ if i == last {
+ self.twi.shorts.write(|w| w.bb_suspend().clear_bit().bb_stop().set_bit());
}
- // Turn around to read data.
- if let Some((last, before)) = buffer.split_last_mut() {
- // If we want to read multiple bytes we need to use the suspend mode.
- if !before.is_empty() {
- self.twi.shorts.write(|w| w.bb_suspend().enabled());
- } else {
- self.twi.shorts.write(|w| w.bb_stop().enabled());
- }
-
- // Clear reception event.
- self.twi.events_rxdready.write(|w| unsafe { w.bits(0) });
-
- // Start data reception.
- self.twi.tasks_startrx.write(|w| unsafe { w.bits(1) });
-
- for byte in &mut before.into_iter() {
- self.twi.tasks_resume.write(|w| unsafe { w.bits(1) });
- *byte = self.recv_byte()?;
- }
+ while !self.rec.load(Ordering::SeqCst) {}
+ self.rec.store(false, Ordering::SeqCst);
+ *byte = self.twi.rxd.read().bits() as u8;
+ self.twi.tasks_resume.write(|w| unsafe { w.bits(1) });
+ }
- self.twi.shorts.write(|w| w.bb_stop().enabled());
- self.twi.tasks_resume.write(|w| unsafe { w.bits(1) });
- *last = self.recv_byte()?;
- } else {
- self.send_stop()?;
- }
- Ok(())
- })
+ Ok(())
}
}
-pub(crate) fn initialize(twi: TWI0, scl_pin: P0_04<Disconnected>, sda_pin: P0_02<Disconnected>) {
+pub(crate) fn initialize(twi: TWI0, scl_pin: P0_04<Disconnected>, sda_pin: P0_02<Disconnected>, nvic: &mut NVIC) {
let scl_pin = Pin::from(scl_pin.into_floating_input());
let sda_pin = Pin::from(sda_pin.into_floating_input());
@@ -244,13 +122,51 @@ pub(crate) fn initialize(twi: TWI0, scl_pin: P0_04<Disconnected>, sda_pin: P0_02
twi.pselsda
.write(|w| unsafe { w.bits(sda_pin.pin().into()) });
+ // Clear interrupts
+ twi.events_rxdready.reset();
+ twi.events_txdsent.reset();
+
// Set frequency.
twi.frequency.write(|w| w.frequency().variant(FREQ));
+ // Set which interrupts we want to receive
+ twi.intenset.write(|w| w.txdsent().set_bit().rxdready().set_bit().error().set_bit());
twi.enable.write(|w| w.enable().enabled());
// Initialize oncecell
TWI.modify(|t| t.initialize(TwiWrapper {
- twi
+ twi,
+ rec: false.into(),
+ sent: false.into(),
}));
+
+ // Setup NVIC
+ NVIC::unpend(Interrupt::SPI0_TWI0);
+ // Safety: We are not using priority-based critical sections.
+ unsafe {
+ nvic.set_priority(Interrupt::SPI0_TWI0, 3); // Same as C template
+ NVIC::unmask(Interrupt::SPI0_TWI0);
+ }
}
+
+#[interrupt]
+unsafe fn SPI0_TWI0() {
+ // Safety: interrupts are already turned off here, since we are inside an interrupt
+ // We might be accessing the hardware while the interrupted code also wants to, this is fine since we're only touching the EVENT registers which are not touched by the other code
+ let twi = unsafe { TWI.no_critical_section_lock_mut() };
+
+ if twi.twi.events_rxdready.read().bits() != 0 {
+ twi.twi.events_rxdready.reset();
+ twi.rec.store(true, Ordering::SeqCst);
+ }
+ if twi.twi.events_txdsent.read().bits() != 0 {
+ twi.twi.events_txdsent.reset();
+ twi.sent.store(true, Ordering::SeqCst);
+ }
+
+ // Errors are silently ignored
+ if twi.twi.events_error.read().bits() != 0 {
+ twi.twi.errorsrc.write(|w| w.anack().clear_bit().overrun().clear_bit()); // Clear error source
+ twi.twi.events_error.reset();
+ }
+}
\ No newline at end of file