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