aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias <[email protected]>2022-08-19 11:51:42 +0200
committerDario Nieuwenhuis <[email protected]>2022-09-27 22:08:49 +0200
commit820e6462b6a48a6a59f082bccd5d7a3035937b2f (patch)
treebebf1fe09d24fa069aec38acc9aeddb143aa01d8
parent5c882cf4faffe68dd376b90b6ce7504fd5d259c5 (diff)
Add preliminary I2C implementation for RP2040
-rw-r--r--embassy-rp/src/clocks.rs2
-rw-r--r--embassy-rp/src/i2c.rs221
-rw-r--r--embassy-rp/src/lib.rs4
-rw-r--r--embassy-rp/src/uart/mod.rs5
4 files changed, 231 insertions, 1 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 1c446f389..875c129c0 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -114,7 +114,7 @@ pub unsafe fn init() {
114 reset::unreset_wait(peris); 114 reset::unreset_wait(peris);
115} 115}
116 116
117pub(crate) fn _clk_sys_freq() -> u32 { 117pub(crate) fn clk_sys_freq() -> u32 {
118 125_000_000 118 125_000_000
119} 119}
120 120
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
new file mode 100644
index 000000000..4a27ee8df
--- /dev/null
+++ b/embassy-rp/src/i2c.rs
@@ -0,0 +1,221 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use pac::i2c;
5
6use crate::{pac, peripherals, Peripheral};
7
8/// I2C error
9#[derive(Debug)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub enum Error {
12 /// I2C abort with error
13 Abort(u32),
14 /// User passed in a read buffer that was 0 length
15 InvalidReadBufferLength,
16 /// User passed in a write buffer that was 0 length
17 InvalidWriteBufferLength,
18 /// Target i2c address is out of range
19 AddressOutOfRange(u16),
20 /// Target i2c address is reserved
21 AddressReserved(u16),
22}
23
24#[non_exhaustive]
25#[derive(Copy, Clone)]
26pub struct Config {
27 pub frequency: u32,
28 pub sda_pullup: bool,
29 pub scl_pullup: bool,
30}
31
32impl Default for Config {
33 fn default() -> Self {
34 Self {
35 frequency: 100_000,
36 sda_pullup: false,
37 scl_pullup: false,
38 }
39 }
40}
41
42pub struct I2c<'d, T: Instance, M: Mode> {
43 phantom: PhantomData<(&'d mut T, M)>,
44}
45
46impl<'d, T: Instance> I2c<'d, T, Master> {
47 pub fn new_master(
48 _peri: impl Peripheral<P = T> + 'd,
49 scl: impl Peripheral<P = impl SclPin<T>> + 'd,
50 sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
51 config: Config,
52 ) -> Self {
53 into_ref!(_peri, scl, sda);
54
55 assert!(config.frequency <= 1_000_000);
56 assert!(config.frequency > 0);
57
58 let p = T::regs();
59
60 unsafe {
61 p.ic_enable().write(|w| w.set_enable(false));
62
63 // select controller mode & speed
64 p.ic_con().write(|w| {
65 // Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
66 w.set_speed(i2c::vals::Speed::FAST);
67 w.set_master_mode(true);
68 w.set_ic_slave_disable(true);
69 w.set_ic_restart_en(true);
70 w.set_tx_empty_ctrl(true);
71 });
72
73 // Clear FIFO threshold
74 p.ic_tx_tl().write(|w| w.set_tx_tl(0));
75 p.ic_rx_tl().write(|w| w.set_rx_tl(0));
76
77 // Configure SCL & SDA pins
78 scl.io().ctrl().write(|w| w.set_funcsel(3));
79 sda.io().ctrl().write(|w| w.set_funcsel(3));
80
81 scl.pad_ctrl().write(|w| {
82 w.set_schmitt(true);
83 w.set_pue(config.scl_pullup);
84 });
85 sda.pad_ctrl().write(|w| {
86 w.set_schmitt(true);
87 w.set_pue(config.sda_pullup);
88 });
89
90 // Configure baudrate
91
92 // There are some subtleties to I2C timing which we are completely ignoring here
93 // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
94 let clk_base = crate::clocks::clk_sys_freq();
95
96 let period = (clk_base + config.frequency / 2) / config.frequency;
97 let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low
98 let hcnt = period - lcnt; // and 2/5 (40%) of the period high
99
100 // Check for out-of-range divisors:
101 assert!(hcnt <= 0xffff);
102 assert!(lcnt <= 0xffff);
103 assert!(hcnt >= 8);
104 assert!(lcnt >= 8);
105
106 // Per I2C-bus specification a device in standard or fast mode must
107 // internally provide a hold time of at least 300ns for the SDA signal to
108 // bridge the undefined region of the falling edge of SCL. A smaller hold
109 // time of 120ns is used for fast mode plus.
110 let sda_tx_hold_count = if config.frequency < 1_000_000 {
111 // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns)
112 // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
113 // Add 1 to avoid division truncation.
114 ((clk_base * 3) / 10_000_000) + 1
115 } else {
116 // fast mode plus requires a clk_base > 32MHz
117 assert!(clk_base >= 32_000_000);
118
119 // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns)
120 // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
121 // Add 1 to avoid division truncation.
122 ((clk_base * 3) / 25_000_000) + 1
123 };
124 assert!(sda_tx_hold_count <= lcnt - 2);
125
126 p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));
127 p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
128 p.ic_fs_spklen()
129 .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 }));
130 p.ic_sda_hold()
131 .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
132
133 // Enable I2C block
134 p.ic_enable().write(|w| w.set_enable(true));
135 }
136
137 Self { phantom: PhantomData }
138 }
139}
140
141mod sealed {
142 pub trait Instance {}
143 pub trait Mode {}
144
145 pub trait SdaPin<T: Instance> {}
146 pub trait SclPin<T: Instance> {}
147}
148
149pub trait Mode: sealed::Mode {}
150
151macro_rules! impl_mode {
152 ($name:ident) => {
153 impl sealed::Mode for $name {}
154 impl Mode for $name {}
155 };
156}
157
158pub struct Master;
159pub struct Slave;
160
161impl_mode!(Master);
162impl_mode!(Slave);
163
164pub trait Instance: sealed::Instance {
165 fn regs() -> pac::i2c::I2c;
166}
167
168macro_rules! impl_instance {
169 ($type:ident, $irq:ident) => {
170 impl sealed::Instance for peripherals::$type {}
171 impl Instance for peripherals::$type {
172 fn regs() -> pac::i2c::I2c {
173 pac::$type
174 }
175 }
176 };
177}
178
179impl_instance!(I2C0, I2c0);
180impl_instance!(I2C1, I2c1);
181
182pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
183pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
184
185macro_rules! impl_pin {
186 ($pin:ident, $instance:ident, $function:ident) => {
187 impl sealed::$function<peripherals::$instance> for peripherals::$pin {}
188 impl $function<peripherals::$instance> for peripherals::$pin {}
189 };
190}
191
192impl_pin!(PIN_0, I2C0, SdaPin);
193impl_pin!(PIN_1, I2C0, SclPin);
194impl_pin!(PIN_2, I2C1, SdaPin);
195impl_pin!(PIN_3, I2C1, SclPin);
196impl_pin!(PIN_4, I2C0, SdaPin);
197impl_pin!(PIN_5, I2C0, SclPin);
198impl_pin!(PIN_6, I2C1, SdaPin);
199impl_pin!(PIN_7, I2C1, SclPin);
200impl_pin!(PIN_8, I2C0, SdaPin);
201impl_pin!(PIN_9, I2C0, SclPin);
202impl_pin!(PIN_10, I2C1, SdaPin);
203impl_pin!(PIN_11, I2C1, SclPin);
204impl_pin!(PIN_12, I2C0, SdaPin);
205impl_pin!(PIN_13, I2C0, SclPin);
206impl_pin!(PIN_14, I2C1, SdaPin);
207impl_pin!(PIN_15, I2C1, SclPin);
208impl_pin!(PIN_16, I2C0, SdaPin);
209impl_pin!(PIN_17, I2C0, SclPin);
210impl_pin!(PIN_18, I2C1, SdaPin);
211impl_pin!(PIN_19, I2C1, SclPin);
212impl_pin!(PIN_20, I2C0, SdaPin);
213impl_pin!(PIN_21, I2C0, SclPin);
214impl_pin!(PIN_22, I2C1, SdaPin);
215impl_pin!(PIN_23, I2C1, SclPin);
216impl_pin!(PIN_24, I2C0, SdaPin);
217impl_pin!(PIN_25, I2C0, SclPin);
218impl_pin!(PIN_26, I2C1, SdaPin);
219impl_pin!(PIN_27, I2C1, SclPin);
220impl_pin!(PIN_28, I2C0, SdaPin);
221impl_pin!(PIN_29, I2C0, SclPin);
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 9ac98d226..e784399d4 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -8,6 +8,7 @@ mod intrinsics;
8 8
9pub mod dma; 9pub mod dma;
10pub mod gpio; 10pub mod gpio;
11pub mod i2c;
11pub mod interrupt; 12pub mod interrupt;
12pub mod rom_data; 13pub mod rom_data;
13pub mod rtc; 14pub mod rtc;
@@ -75,6 +76,9 @@ embassy_hal_common::peripherals! {
75 SPI0, 76 SPI0,
76 SPI1, 77 SPI1,
77 78
79 I2C0,
80 I2C1,
81
78 DMA_CH0, 82 DMA_CH0,
79 DMA_CH1, 83 DMA_CH1,
80 DMA_CH2, 84 DMA_CH2,
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index d9285ee51..567c79db3 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -428,9 +428,11 @@ mod eh02 {
428 428
429 impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, T, M> { 429 impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, T, M> {
430 type Error = Error; 430 type Error = Error;
431
431 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 432 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
432 self.blocking_write(buffer) 433 self.blocking_write(buffer)
433 } 434 }
435
434 fn bflush(&mut self) -> Result<(), Self::Error> { 436 fn bflush(&mut self) -> Result<(), Self::Error> {
435 self.blocking_flush() 437 self.blocking_flush()
436 } 438 }
@@ -438,6 +440,7 @@ mod eh02 {
438 440
439 impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, T, M> { 441 impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, T, M> {
440 type Error = Error; 442 type Error = Error;
443
441 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> { 444 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
442 embedded_hal_02::serial::Read::read(&mut self.rx) 445 embedded_hal_02::serial::Read::read(&mut self.rx)
443 } 446 }
@@ -445,9 +448,11 @@ mod eh02 {
445 448
446 impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, T, M> { 449 impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, T, M> {
447 type Error = Error; 450 type Error = Error;
451
448 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 452 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
449 self.blocking_write(buffer) 453 self.blocking_write(buffer)
450 } 454 }
455
451 fn bflush(&mut self) -> Result<(), Self::Error> { 456 fn bflush(&mut self) -> Result<(), Self::Error> {
452 self.blocking_flush() 457 self.blocking_flush()
453 } 458 }