diff options
| author | Caleb Jamison <[email protected]> | 2023-09-09 18:25:23 -0400 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-09-10 23:01:15 +0200 |
| commit | 2d9f50addc5f509f5549e69f594c382cebe739e6 (patch) | |
| tree | 41861a3120a578dcfa744b6777ec5b592cd63c97 | |
| parent | 18da91e2529b7973d0476e5c239709b3851db14b (diff) | |
I2c slave take 2
refactored to split modules
renamed to match upstream docs
slight improvement to slave error handling
| -rw-r--r-- | embassy-rp/src/i2c/i2c.rs | 155 | ||||
| -rw-r--r-- | embassy-rp/src/i2c_slave.rs | 326 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 1 | ||||
| -rw-r--r-- | tests/rp/src/bin/i2c.rs | 33 |
4 files changed, 497 insertions, 18 deletions
diff --git a/embassy-rp/src/i2c/i2c.rs b/embassy-rp/src/i2c/i2c.rs index 13124dd81..facc7185f 100644 --- a/embassy-rp/src/i2c/i2c.rs +++ b/embassy-rp/src/i2c/i2c.rs | |||
| @@ -11,7 +11,38 @@ use super::{ | |||
| 11 | use crate::gpio::sealed::Pin; | 11 | use crate::gpio::sealed::Pin; |
| 12 | use crate::gpio::AnyPin; | 12 | use crate::gpio::AnyPin; |
| 13 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 13 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 14 | use crate::{pac, Peripheral}; | 14 | use crate::{interrupt, pac, peripherals, Peripheral}; |
| 15 | |||
| 16 | /// I2C error abort reason | ||
| 17 | #[derive(Debug)] | ||
| 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 19 | pub enum AbortReason { | ||
| 20 | /// A bus operation was not acknowledged, e.g. due to the addressed device | ||
| 21 | /// not being available on the bus or the device not being ready to process | ||
| 22 | /// requests at the moment | ||
| 23 | NoAcknowledge, | ||
| 24 | /// The arbitration was lost, e.g. electrical problems with the clock signal | ||
| 25 | ArbitrationLoss, | ||
| 26 | /// Transmit ended with data still in fifo | ||
| 27 | TxNotEmpty(u16), | ||
| 28 | Other(u32), | ||
| 29 | } | ||
| 30 | |||
| 31 | /// I2C error | ||
| 32 | #[derive(Debug)] | ||
| 33 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 34 | pub enum Error { | ||
| 35 | /// I2C abort with error | ||
| 36 | Abort(AbortReason), | ||
| 37 | /// User passed in a read buffer that was 0 length | ||
| 38 | InvalidReadBufferLength, | ||
| 39 | /// User passed in a write buffer that was 0 length | ||
| 40 | InvalidWriteBufferLength, | ||
| 41 | /// Target i2c address is out of range | ||
| 42 | AddressOutOfRange(u16), | ||
| 43 | /// Target i2c address is reserved | ||
| 44 | AddressReserved(u16), | ||
| 45 | } | ||
| 15 | 46 | ||
| 16 | #[non_exhaustive] | 47 | #[non_exhaustive] |
| 17 | #[derive(Copy, Clone)] | 48 | #[derive(Copy, Clone)] |
| @@ -26,6 +57,8 @@ impl Default for Config { | |||
| 26 | } | 57 | } |
| 27 | } | 58 | } |
| 28 | 59 | ||
| 60 | pub const FIFO_SIZE: u8 = 16; | ||
| 61 | |||
| 29 | pub struct I2c<'d, T: Instance, M: Mode> { | 62 | pub struct I2c<'d, T: Instance, M: Mode> { |
| 30 | phantom: PhantomData<(&'d mut T, M)>, | 63 | phantom: PhantomData<(&'d mut T, M)>, |
| 31 | } | 64 | } |
| @@ -696,3 +729,123 @@ mod nightly { | |||
| 696 | } | 729 | } |
| 697 | } | 730 | } |
| 698 | } | 731 | } |
| 732 | <<<<<<< HEAD:embassy-rp/src/i2c/i2c.rs | ||
| 733 | ======= | ||
| 734 | |||
| 735 | pub fn i2c_reserved_addr(addr: u16) -> bool { | ||
| 736 | ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 | ||
| 737 | } | ||
| 738 | |||
| 739 | mod sealed { | ||
| 740 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 741 | |||
| 742 | use crate::interrupt; | ||
| 743 | |||
| 744 | pub trait Instance { | ||
| 745 | const TX_DREQ: u8; | ||
| 746 | const RX_DREQ: u8; | ||
| 747 | |||
| 748 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 749 | |||
| 750 | fn regs() -> crate::pac::i2c::I2c; | ||
| 751 | fn reset() -> crate::pac::resets::regs::Peripherals; | ||
| 752 | fn waker() -> &'static AtomicWaker; | ||
| 753 | } | ||
| 754 | |||
| 755 | pub trait Mode {} | ||
| 756 | |||
| 757 | pub trait SdaPin<T: Instance> {} | ||
| 758 | pub trait SclPin<T: Instance> {} | ||
| 759 | } | ||
| 760 | |||
| 761 | pub trait Mode: sealed::Mode {} | ||
| 762 | |||
| 763 | macro_rules! impl_mode { | ||
| 764 | ($name:ident) => { | ||
| 765 | impl sealed::Mode for $name {} | ||
| 766 | impl Mode for $name {} | ||
| 767 | }; | ||
| 768 | } | ||
| 769 | |||
| 770 | pub struct Blocking; | ||
| 771 | pub struct Async; | ||
| 772 | |||
| 773 | impl_mode!(Blocking); | ||
| 774 | impl_mode!(Async); | ||
| 775 | |||
| 776 | pub trait Instance: sealed::Instance {} | ||
| 777 | |||
| 778 | macro_rules! impl_instance { | ||
| 779 | ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { | ||
| 780 | impl sealed::Instance for peripherals::$type { | ||
| 781 | const TX_DREQ: u8 = $tx_dreq; | ||
| 782 | const RX_DREQ: u8 = $rx_dreq; | ||
| 783 | |||
| 784 | type Interrupt = crate::interrupt::typelevel::$irq; | ||
| 785 | |||
| 786 | #[inline] | ||
| 787 | fn regs() -> pac::i2c::I2c { | ||
| 788 | pac::$type | ||
| 789 | } | ||
| 790 | |||
| 791 | #[inline] | ||
| 792 | fn reset() -> pac::resets::regs::Peripherals { | ||
| 793 | let mut ret = pac::resets::regs::Peripherals::default(); | ||
| 794 | ret.$reset(true); | ||
| 795 | ret | ||
| 796 | } | ||
| 797 | |||
| 798 | #[inline] | ||
| 799 | fn waker() -> &'static AtomicWaker { | ||
| 800 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 801 | |||
| 802 | &WAKER | ||
| 803 | } | ||
| 804 | } | ||
| 805 | impl Instance for peripherals::$type {} | ||
| 806 | }; | ||
| 807 | } | ||
| 808 | |||
| 809 | impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); | ||
| 810 | impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); | ||
| 811 | |||
| 812 | pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} | ||
| 813 | pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} | ||
| 814 | |||
| 815 | macro_rules! impl_pin { | ||
| 816 | ($pin:ident, $instance:ident, $function:ident) => { | ||
| 817 | impl sealed::$function<peripherals::$instance> for peripherals::$pin {} | ||
| 818 | impl $function<peripherals::$instance> for peripherals::$pin {} | ||
| 819 | }; | ||
| 820 | } | ||
| 821 | |||
| 822 | impl_pin!(PIN_0, I2C0, SdaPin); | ||
| 823 | impl_pin!(PIN_1, I2C0, SclPin); | ||
| 824 | impl_pin!(PIN_2, I2C1, SdaPin); | ||
| 825 | impl_pin!(PIN_3, I2C1, SclPin); | ||
| 826 | impl_pin!(PIN_4, I2C0, SdaPin); | ||
| 827 | impl_pin!(PIN_5, I2C0, SclPin); | ||
| 828 | impl_pin!(PIN_6, I2C1, SdaPin); | ||
| 829 | impl_pin!(PIN_7, I2C1, SclPin); | ||
| 830 | impl_pin!(PIN_8, I2C0, SdaPin); | ||
| 831 | impl_pin!(PIN_9, I2C0, SclPin); | ||
| 832 | impl_pin!(PIN_10, I2C1, SdaPin); | ||
| 833 | impl_pin!(PIN_11, I2C1, SclPin); | ||
| 834 | impl_pin!(PIN_12, I2C0, SdaPin); | ||
| 835 | impl_pin!(PIN_13, I2C0, SclPin); | ||
| 836 | impl_pin!(PIN_14, I2C1, SdaPin); | ||
| 837 | impl_pin!(PIN_15, I2C1, SclPin); | ||
| 838 | impl_pin!(PIN_16, I2C0, SdaPin); | ||
| 839 | impl_pin!(PIN_17, I2C0, SclPin); | ||
| 840 | impl_pin!(PIN_18, I2C1, SdaPin); | ||
| 841 | impl_pin!(PIN_19, I2C1, SclPin); | ||
| 842 | impl_pin!(PIN_20, I2C0, SdaPin); | ||
| 843 | impl_pin!(PIN_21, I2C0, SclPin); | ||
| 844 | impl_pin!(PIN_22, I2C1, SdaPin); | ||
| 845 | impl_pin!(PIN_23, I2C1, SclPin); | ||
| 846 | impl_pin!(PIN_24, I2C0, SdaPin); | ||
| 847 | impl_pin!(PIN_25, I2C0, SclPin); | ||
| 848 | impl_pin!(PIN_26, I2C1, SdaPin); | ||
| 849 | impl_pin!(PIN_27, I2C1, SclPin); | ||
| 850 | impl_pin!(PIN_28, I2C0, SdaPin); | ||
| 851 | impl_pin!(PIN_29, I2C0, SclPin); | ||
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs new file mode 100644 index 000000000..a65250116 --- /dev/null +++ b/embassy-rp/src/i2c_slave.rs | |||
| @@ -0,0 +1,326 @@ | |||
| 1 | use core::future; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::task::Poll; | ||
| 4 | |||
| 5 | use embassy_hal_internal::into_ref; | ||
| 6 | use pac::i2c; | ||
| 7 | |||
| 8 | use crate::i2c::{i2c_reserved_addr, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE}; | ||
| 9 | use crate::interrupt::typelevel::{Binding, Interrupt}; | ||
| 10 | use crate::{pac, Peripheral}; | ||
| 11 | |||
| 12 | /// I2C error | ||
| 13 | #[derive(Debug)] | ||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | #[non_exhaustive] | ||
| 16 | pub enum Error { | ||
| 17 | /// I2C abort with error | ||
| 18 | Abort(AbortReason), | ||
| 19 | /// User passed in a response buffer that was 0 length | ||
| 20 | InvalidResponseBufferLength, | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Received command | ||
| 24 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub enum Command { | ||
| 27 | /// General Call | ||
| 28 | GeneralCall(usize), | ||
| 29 | /// Read | ||
| 30 | Read, | ||
| 31 | /// Write+read | ||
| 32 | WriteRead(usize), | ||
| 33 | /// Write | ||
| 34 | Write(usize), | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Possible responses to responding to a read | ||
| 38 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 40 | pub enum ReadStatus { | ||
| 41 | /// Transaction Complete, controller naked our last byte | ||
| 42 | Done, | ||
| 43 | /// Transaction Incomplete, controller trying to read more bytes than were provided | ||
| 44 | NeedMoreBytes, | ||
| 45 | /// Transaction Complere, but controller stopped reading bytes before we ran out | ||
| 46 | LeftoverBytes(u16), | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Slave Configuration | ||
| 50 | #[non_exhaustive] | ||
| 51 | #[derive(Copy, Clone)] | ||
| 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 53 | pub struct Config { | ||
| 54 | /// Target Address | ||
| 55 | pub addr: u16, | ||
| 56 | } | ||
| 57 | |||
| 58 | impl Default for Config { | ||
| 59 | fn default() -> Self { | ||
| 60 | Self { addr: 0x55 } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | pub struct I2cSlave<'d, T: Instance> { | ||
| 65 | phantom: PhantomData<&'d mut T>, | ||
| 66 | } | ||
| 67 | |||
| 68 | impl<'d, T: Instance> I2cSlave<'d, T> { | ||
| 69 | pub fn new( | ||
| 70 | _peri: impl Peripheral<P = T> + 'd, | ||
| 71 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||
| 72 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | ||
| 73 | _irq: impl Binding<T::Interrupt, InterruptHandler<T>>, | ||
| 74 | config: Config, | ||
| 75 | ) -> Self { | ||
| 76 | into_ref!(_peri, scl, sda); | ||
| 77 | |||
| 78 | assert!(!i2c_reserved_addr(config.addr)); | ||
| 79 | assert!(config.addr != 0); | ||
| 80 | |||
| 81 | let p = T::regs(); | ||
| 82 | |||
| 83 | let reset = T::reset(); | ||
| 84 | crate::reset::reset(reset); | ||
| 85 | crate::reset::unreset_wait(reset); | ||
| 86 | |||
| 87 | p.ic_enable().write(|w| w.set_enable(false)); | ||
| 88 | |||
| 89 | p.ic_sar().write(|w| w.set_ic_sar(config.addr)); | ||
| 90 | p.ic_con().modify(|w| { | ||
| 91 | w.set_master_mode(false); | ||
| 92 | w.set_ic_slave_disable(false); | ||
| 93 | w.set_tx_empty_ctrl(true); | ||
| 94 | }); | ||
| 95 | |||
| 96 | // Set FIFO watermarks to 1 to make things simpler. This is encoded | ||
| 97 | // by a register value of 0. Rx watermark should never change, but Tx watermark will be | ||
| 98 | // adjusted in operation. | ||
| 99 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); | ||
| 100 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||
| 101 | |||
| 102 | // Configure SCL & SDA pins | ||
| 103 | scl.gpio().ctrl().write(|w| w.set_funcsel(3)); | ||
| 104 | sda.gpio().ctrl().write(|w| w.set_funcsel(3)); | ||
| 105 | |||
| 106 | scl.pad_ctrl().write(|w| { | ||
| 107 | w.set_schmitt(true); | ||
| 108 | w.set_ie(true); | ||
| 109 | w.set_od(false); | ||
| 110 | w.set_pue(true); | ||
| 111 | w.set_pde(false); | ||
| 112 | }); | ||
| 113 | sda.pad_ctrl().write(|w| { | ||
| 114 | w.set_schmitt(true); | ||
| 115 | w.set_ie(true); | ||
| 116 | w.set_od(false); | ||
| 117 | w.set_pue(true); | ||
| 118 | w.set_pde(false); | ||
| 119 | }); | ||
| 120 | |||
| 121 | // Clear interrupts | ||
| 122 | p.ic_clr_intr().read(); | ||
| 123 | |||
| 124 | // Enable I2C block | ||
| 125 | p.ic_enable().write(|w| w.set_enable(true)); | ||
| 126 | |||
| 127 | // mask everything initially | ||
| 128 | p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); | ||
| 129 | T::Interrupt::unpend(); | ||
| 130 | unsafe { T::Interrupt::enable() }; | ||
| 131 | |||
| 132 | Self { phantom: PhantomData } | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Calls `f` to check if we are ready or not. | ||
| 136 | /// If not, `g` is called once the waker is set (to eg enable the required interrupts). | ||
| 137 | #[inline(always)] | ||
| 138 | async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U | ||
| 139 | where | ||
| 140 | F: FnMut(&mut Self) -> Poll<U>, | ||
| 141 | G: FnMut(&mut Self), | ||
| 142 | { | ||
| 143 | future::poll_fn(|cx| { | ||
| 144 | let r = f(self); | ||
| 145 | |||
| 146 | trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0); | ||
| 147 | |||
| 148 | if r.is_pending() { | ||
| 149 | T::waker().register(cx.waker()); | ||
| 150 | g(self); | ||
| 151 | } | ||
| 152 | |||
| 153 | r | ||
| 154 | }) | ||
| 155 | .await | ||
| 156 | } | ||
| 157 | |||
| 158 | #[inline(always)] | ||
| 159 | fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { | ||
| 160 | let p = T::regs(); | ||
| 161 | let len = p.ic_rxflr().read().rxflr() as usize; | ||
| 162 | let end = offset + len; | ||
| 163 | for i in offset..end { | ||
| 164 | buffer[i] = p.ic_data_cmd().read().dat(); | ||
| 165 | } | ||
| 166 | end | ||
| 167 | } | ||
| 168 | |||
| 169 | #[inline(always)] | ||
| 170 | fn write_to_fifo(&mut self, buffer: &[u8]) { | ||
| 171 | let p = T::regs(); | ||
| 172 | for byte in buffer { | ||
| 173 | p.ic_data_cmd().write(|w| w.set_dat(*byte)); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Wait asynchronously for commands from an I2C master. | ||
| 178 | /// `buffer` is provided in case master does a 'write' and is unused for 'read'. | ||
| 179 | pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { | ||
| 180 | let p = T::regs(); | ||
| 181 | |||
| 182 | p.ic_clr_intr().read(); | ||
| 183 | // set rx fifo watermark to 1 byte | ||
| 184 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||
| 185 | |||
| 186 | let mut len = 0; | ||
| 187 | let ret = self | ||
| 188 | .wait_on( | ||
| 189 | |me| { | ||
| 190 | let stat = p.ic_raw_intr_stat().read(); | ||
| 191 | if p.ic_rxflr().read().rxflr() > 0 { | ||
| 192 | len = me.drain_fifo(buffer, len); | ||
| 193 | // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise | ||
| 194 | p.ic_rx_tl().write(|w| w.set_rx_tl(11)); | ||
| 195 | } | ||
| 196 | |||
| 197 | if stat.restart_det() && stat.rd_req() { | ||
| 198 | Poll::Ready(Ok(Command::WriteRead(len))) | ||
| 199 | } else if stat.gen_call() && stat.stop_det() && len > 0 { | ||
| 200 | Poll::Ready(Ok(Command::GeneralCall(len))) | ||
| 201 | } else if stat.stop_det() { | ||
| 202 | Poll::Ready(Ok(Command::Write(len))) | ||
| 203 | } else if stat.rd_req() { | ||
| 204 | Poll::Ready(Ok(Command::Read)) | ||
| 205 | } else { | ||
| 206 | Poll::Pending | ||
| 207 | } | ||
| 208 | }, | ||
| 209 | |_me| { | ||
| 210 | p.ic_intr_mask().modify(|w| { | ||
| 211 | w.set_m_stop_det(true); | ||
| 212 | w.set_m_restart_det(true); | ||
| 213 | w.set_m_gen_call(true); | ||
| 214 | w.set_m_rd_req(true); | ||
| 215 | w.set_m_rx_full(true); | ||
| 216 | }); | ||
| 217 | }, | ||
| 218 | ) | ||
| 219 | .await; | ||
| 220 | |||
| 221 | p.ic_clr_intr().read(); | ||
| 222 | |||
| 223 | ret | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Respond to an I2C master READ command, asynchronously. | ||
| 227 | pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> { | ||
| 228 | let p = T::regs(); | ||
| 229 | |||
| 230 | if buffer.len() == 0 { | ||
| 231 | return Err(Error::InvalidResponseBufferLength); | ||
| 232 | } | ||
| 233 | |||
| 234 | let mut chunks = buffer.chunks(FIFO_SIZE as usize); | ||
| 235 | |||
| 236 | let ret = self | ||
| 237 | .wait_on( | ||
| 238 | |me| { | ||
| 239 | if let Err(abort_reason) = me.read_and_clear_abort_reason() { | ||
| 240 | if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { | ||
| 241 | return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); | ||
| 242 | } else { | ||
| 243 | return Poll::Ready(Err(abort_reason)); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | if let Some(chunk) = chunks.next() { | ||
| 248 | me.write_to_fifo(chunk); | ||
| 249 | |||
| 250 | Poll::Pending | ||
| 251 | } else { | ||
| 252 | let stat = p.ic_raw_intr_stat().read(); | ||
| 253 | |||
| 254 | if stat.rx_done() && stat.stop_det() { | ||
| 255 | Poll::Ready(Ok(ReadStatus::Done)) | ||
| 256 | } else if stat.rd_req() { | ||
| 257 | Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) | ||
| 258 | } else { | ||
| 259 | Poll::Pending | ||
| 260 | } | ||
| 261 | } | ||
| 262 | }, | ||
| 263 | |_me| { | ||
| 264 | p.ic_intr_mask().modify(|w| { | ||
| 265 | w.set_m_stop_det(true); | ||
| 266 | w.set_m_rx_done(true); | ||
| 267 | w.set_m_tx_empty(true); | ||
| 268 | w.set_m_tx_abrt(true); | ||
| 269 | }) | ||
| 270 | }, | ||
| 271 | ) | ||
| 272 | .await; | ||
| 273 | |||
| 274 | p.ic_clr_intr().read(); | ||
| 275 | |||
| 276 | ret | ||
| 277 | } | ||
| 278 | |||
| 279 | /// Respond to reads with the fill byte until the controller stops asking | ||
| 280 | pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { | ||
| 281 | loop { | ||
| 282 | match self.respond_to_read(&[fill]).await { | ||
| 283 | Ok(ReadStatus::NeedMoreBytes) => (), | ||
| 284 | Ok(_) => break Ok(()), | ||
| 285 | Err(e) => break Err(e), | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | #[inline(always)] | ||
| 291 | fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { | ||
| 292 | let p = T::regs(); | ||
| 293 | let mut abort_reason = p.ic_tx_abrt_source().read(); | ||
| 294 | |||
| 295 | // Mask off fifo flush count | ||
| 296 | let tx_flush_cnt = abort_reason.tx_flush_cnt(); | ||
| 297 | abort_reason.set_tx_flush_cnt(0); | ||
| 298 | |||
| 299 | // Mask off master_dis | ||
| 300 | abort_reason.set_abrt_master_dis(false); | ||
| 301 | |||
| 302 | if abort_reason.0 != 0 { | ||
| 303 | // Note clearing the abort flag also clears the reason, and this | ||
| 304 | // instance of flag is clear-on-read! Note also the | ||
| 305 | // IC_CLR_TX_ABRT register always reads as 0. | ||
| 306 | p.ic_clr_tx_abrt().read(); | ||
| 307 | |||
| 308 | let reason = if abort_reason.abrt_7b_addr_noack() | ||
| 309 | | abort_reason.abrt_10addr1_noack() | ||
| 310 | | abort_reason.abrt_10addr2_noack() | ||
| 311 | { | ||
| 312 | AbortReason::NoAcknowledge | ||
| 313 | } else if abort_reason.arb_lost() { | ||
| 314 | AbortReason::ArbitrationLoss | ||
| 315 | } else if abort_reason.abrt_slvflush_txfifo() { | ||
| 316 | AbortReason::TxNotEmpty(tx_flush_cnt) | ||
| 317 | } else { | ||
| 318 | AbortReason::Other(abort_reason.0) | ||
| 319 | }; | ||
| 320 | |||
| 321 | Err(Error::Abort(reason)) | ||
| 322 | } else { | ||
| 323 | Ok(()) | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 49bd3533e..2a1bca4b9 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -16,6 +16,7 @@ pub mod flash; | |||
| 16 | mod float; | 16 | mod float; |
| 17 | pub mod gpio; | 17 | pub mod gpio; |
| 18 | pub mod i2c; | 18 | pub mod i2c; |
| 19 | pub mod i2c_slave; | ||
| 19 | pub mod multicore; | 20 | pub mod multicore; |
| 20 | pub mod pwm; | 21 | pub mod pwm; |
| 21 | mod reset; | 22 | mod reset; |
diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs index 63dd00233..a6cf48afe 100644 --- a/tests/rp/src/bin/i2c.rs +++ b/tests/rp/src/bin/i2c.rs | |||
| @@ -5,10 +5,9 @@ | |||
| 5 | use defmt::{assert_eq, info, panic, unwrap}; | 5 | use defmt::{assert_eq, info, panic, unwrap}; |
| 6 | use embassy_executor::Executor; | 6 | use embassy_executor::Executor; |
| 7 | use embassy_executor::_export::StaticCell; | 7 | use embassy_executor::_export::StaticCell; |
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::i2c::{self, Async, InterruptHandler}; | ||
| 10 | use embassy_rp::multicore::{spawn_core1, Stack}; | 8 | use embassy_rp::multicore::{spawn_core1, Stack}; |
| 11 | use embassy_rp::peripherals::{I2C0, I2C1}; | 9 | use embassy_rp::peripherals::{I2C0, I2C1}; |
| 10 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; | ||
| 12 | use embedded_hal_1::i2c::Operation; | 11 | use embedded_hal_1::i2c::Operation; |
| 13 | use embedded_hal_async::i2c::I2c; | 12 | use embedded_hal_async::i2c::I2c; |
| 14 | use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; |
| @@ -20,14 +19,14 @@ static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | |||
| 20 | use crate::i2c::AbortReason; | 19 | use crate::i2c::AbortReason; |
| 21 | 20 | ||
| 22 | bind_interrupts!(struct Irqs { | 21 | bind_interrupts!(struct Irqs { |
| 23 | I2C0_IRQ => InterruptHandler<I2C0>; | 22 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; |
| 24 | I2C1_IRQ => InterruptHandler<I2C1>; | 23 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; |
| 25 | }); | 24 | }); |
| 26 | 25 | ||
| 27 | const DEV_ADDR: u8 = 0x42; | 26 | const DEV_ADDR: u8 = 0x42; |
| 28 | 27 | ||
| 29 | #[embassy_executor::task] | 28 | #[embassy_executor::task] |
| 30 | async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { | 29 | async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { |
| 31 | info!("Device start"); | 30 | info!("Device start"); |
| 32 | 31 | ||
| 33 | let mut count = 0xD0; | 32 | let mut count = 0xD0; |
| @@ -35,33 +34,33 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { | |||
| 35 | loop { | 34 | loop { |
| 36 | let mut buf = [0u8; 128]; | 35 | let mut buf = [0u8; 128]; |
| 37 | match dev.listen(&mut buf).await { | 36 | match dev.listen(&mut buf).await { |
| 38 | Ok(i2c::Command::GeneralCall(len)) => { | 37 | Ok(i2c_slave::Command::GeneralCall(len)) => { |
| 39 | assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed"); | 38 | assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed"); |
| 40 | info!("General Call - OK"); | 39 | info!("General Call - OK"); |
| 41 | } | 40 | } |
| 42 | Ok(i2c::Command::Read) => { | 41 | Ok(i2c_slave::Command::Read) => { |
| 43 | loop { | 42 | loop { |
| 44 | //info!("Responding to read, count {}", count); | 43 | //info!("Responding to read, count {}", count); |
| 45 | let a = dev.respond_to_read(&[count]).await; | 44 | let a = dev.respond_to_read(&[count]).await; |
| 46 | //info!("x {}", a); | 45 | //info!("x {}", a); |
| 47 | match a { | 46 | match a { |
| 48 | Ok(x) => match x { | 47 | Ok(x) => match x { |
| 49 | i2c::ReadStatus::Done => break, | 48 | i2c_slave::ReadStatus::Done => break, |
| 50 | i2c::ReadStatus::NeedMoreBytes => count += 1, | 49 | i2c_slave::ReadStatus::NeedMoreBytes => count += 1, |
| 51 | i2c::ReadStatus::LeftoverBytes(x) => { | 50 | i2c_slave::ReadStatus::LeftoverBytes(x) => { |
| 52 | info!("tried to write {} extra bytes", x); | 51 | info!("tried to write {} extra bytes", x); |
| 53 | break; | 52 | break; |
| 54 | } | 53 | } |
| 55 | }, | 54 | }, |
| 56 | Err(e) => match e { | 55 | Err(e) => match e { |
| 57 | embassy_rp::i2c::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), | 56 | embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), |
| 58 | _ => panic!("{}", e), | 57 | _ => panic!("{}", e), |
| 59 | }, | 58 | }, |
| 60 | } | 59 | } |
| 61 | } | 60 | } |
| 62 | count += 1; | 61 | count += 1; |
| 63 | } | 62 | } |
| 64 | Ok(i2c::Command::Write(len)) => match len { | 63 | Ok(i2c_slave::Command::Write(len)) => match len { |
| 65 | 1 => { | 64 | 1 => { |
| 66 | assert_eq!(buf[..len], [0xAA], "recieving a single byte failed"); | 65 | assert_eq!(buf[..len], [0xAA], "recieving a single byte failed"); |
| 67 | info!("Single Byte Write - OK") | 66 | info!("Single Byte Write - OK") |
| @@ -83,7 +82,7 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { | |||
| 83 | } | 82 | } |
| 84 | _ => panic!("Invalid write length {}", len), | 83 | _ => panic!("Invalid write length {}", len), |
| 85 | }, | 84 | }, |
| 86 | Ok(i2c::Command::WriteRead(len)) => { | 85 | Ok(i2c_slave::Command::WriteRead(len)) => { |
| 87 | info!("device recieved write read: {:x}", buf[..len]); | 86 | info!("device recieved write read: {:x}", buf[..len]); |
| 88 | match buf[0] { | 87 | match buf[0] { |
| 89 | 0xC2 => { | 88 | 0xC2 => { |
| @@ -101,7 +100,7 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { | |||
| 101 | } | 100 | } |
| 102 | } | 101 | } |
| 103 | Err(e) => match e { | 102 | Err(e) => match e { |
| 104 | embassy_rp::i2c::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), | 103 | embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), |
| 105 | _ => panic!("{}", e), | 104 | _ => panic!("{}", e), |
| 106 | }, | 105 | }, |
| 107 | } | 106 | } |
| @@ -109,7 +108,7 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { | |||
| 109 | } | 108 | } |
| 110 | 109 | ||
| 111 | #[embassy_executor::task] | 110 | #[embassy_executor::task] |
| 112 | async fn controller_task(mut con: i2c::I2c<'static, I2C0, Async>) { | 111 | async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { |
| 113 | info!("Device start"); | 112 | info!("Device start"); |
| 114 | 113 | ||
| 115 | { | 114 | { |
| @@ -194,9 +193,9 @@ fn main() -> ! { | |||
| 194 | 193 | ||
| 195 | let d_sda = p.PIN_19; | 194 | let d_sda = p.PIN_19; |
| 196 | let d_scl = p.PIN_18; | 195 | let d_scl = p.PIN_18; |
| 197 | let mut config = i2c::DeviceConfig::default(); | 196 | let mut config = i2c_slave::Config::default(); |
| 198 | config.addr = DEV_ADDR as u16; | 197 | config.addr = DEV_ADDR as u16; |
| 199 | let device = i2c::I2cDevice::new(p.I2C1, d_sda, d_scl, Irqs, config); | 198 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); |
| 200 | 199 | ||
| 201 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | 200 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { |
| 202 | let executor1 = EXECUTOR1.init(Executor::new()); | 201 | let executor1 = EXECUTOR1.init(Executor::new()); |
