diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 4a27ee8d..f7e6a6f6 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -39,12 +39,15 @@ impl Default for Config { } } +const TX_FIFO_SIZE: u8 = 16; +const RX_FIFO_SIZE: u8 = 16; + pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Master> { - pub fn new_master( +impl<'d, T: Instance> I2c<'d, T, Blocking> { + pub fn new_blocking( _peri: impl Peripheral
+ 'd, scl: impl Peripheral
> + 'd, sda: impl Peripheral
> + 'd,
@@ -60,9 +63,10 @@ impl<'d, T: Instance> I2c<'d, T, Master> {
unsafe {
p.ic_enable().write(|w| w.set_enable(false));
- // select controller mode & speed
+ // Select controller mode & speed
p.ic_con().write(|w| {
- // Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
+ // Always use "fast" mode (<= 400 kHz, works fine for standard
+ // mode too)
w.set_speed(i2c::vals::Speed::FAST);
w.set_master_mode(true);
w.set_ic_slave_disable(true);
@@ -70,7 +74,8 @@ impl<'d, T: Instance> I2c<'d, T, Master> {
w.set_tx_empty_ctrl(true);
});
- // Clear FIFO threshold
+ // Set FIFO watermarks to 1 to make things simpler. This is encoded
+ // by a register value of 0.
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
@@ -89,8 +94,9 @@ impl<'d, T: Instance> I2c<'d, T, Master> {
// Configure baudrate
- // There are some subtleties to I2C timing which we are completely ignoring here
- // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
+ // There are some subtleties to I2C timing which we are completely
+ // ignoring here See:
+ // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
let clk_base = crate::clocks::clk_sys_freq();
let period = (clk_base + config.frequency / 2) / config.frequency;
@@ -104,21 +110,21 @@ impl<'d, T: Instance> I2c<'d, T, Master> {
assert!(lcnt >= 8);
// Per I2C-bus specification a device in standard or fast mode must
- // internally provide a hold time of at least 300ns for the SDA signal to
- // bridge the undefined region of the falling edge of SCL. A smaller hold
- // time of 120ns is used for fast mode plus.
+ // internally provide a hold time of at least 300ns for the SDA
+ // signal to bridge the undefined region of the falling edge of SCL.
+ // A smaller hold time of 120ns is used for fast mode plus.
let sda_tx_hold_count = if config.frequency < 1_000_000 {
- // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns)
- // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
- // Add 1 to avoid division truncation.
+ // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s /
+ // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't
+ // fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 10_000_000) + 1
} else {
// fast mode plus requires a clk_base > 32MHz
assert!(clk_base >= 32_000_000);
- // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns)
- // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
- // Add 1 to avoid division truncation.
+ // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
+ // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
+ // fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 25_000_000) + 1
};
assert!(sda_tx_hold_count <= lcnt - 2);
@@ -138,6 +144,266 @@ impl<'d, T: Instance> I2c<'d, T, Master> {
}
}
+impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
+ /// Number of bytes currently in the RX FIFO
+ #[inline]
+ pub fn rx_fifo_used(&self) -> u8 {
+ unsafe { T::regs().ic_rxflr().read().rxflr() }
+ }
+
+ /// Remaining capacity in the RX FIFO
+ #[inline]
+ pub fn rx_fifo_free(&self) -> u8 {
+ RX_FIFO_SIZE - self.rx_fifo_used()
+ }
+
+ /// RX FIFO is empty
+ #[inline]
+ pub fn rx_fifo_empty(&self) -> bool {
+ self.rx_fifo_used() == 0
+ }
+
+ /// Number of bytes currently in the TX FIFO
+ #[inline]
+ pub fn tx_fifo_used(&self) -> u8 {
+ unsafe { T::regs().ic_txflr().read().txflr() }
+ }
+
+ /// Remaining capacity in the TX FIFO
+ #[inline]
+ pub fn tx_fifo_free(&self) -> u8 {
+ TX_FIFO_SIZE - self.tx_fifo_used()
+ }
+
+ /// TX FIFO is at capacity
+ #[inline]
+ pub fn tx_fifo_full(&self) -> bool {
+ self.tx_fifo_free() == 0
+ }
+
+ fn setup(addr: u16) -> Result<(), Error> {
+ if addr >= 0x80 {
+ return Err(Error::AddressOutOfRange(addr));
+ }
+
+ if i2c_reserved_addr(addr) {
+ return Err(Error::AddressReserved(addr));
+ }
+
+ let p = T::regs();
+ unsafe {
+ p.ic_enable().write(|w| w.set_enable(false));
+ p.ic_tar().write(|w| w.set_ic_tar(addr));
+ p.ic_enable().write(|w| w.set_enable(true));
+ }
+ Ok(())
+ }
+
+ fn read_and_clear_abort_reason(&mut self) -> Option