aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-02-15 17:43:20 -0500
committerCaleb Jamison <[email protected]>2024-02-15 17:56:50 -0500
commitbd0b450ca4ff36b2b1fe0b3b422cd478f9201ad0 (patch)
tree9555d33c741592a04ecab056ba0283c14cc38bb8 /embassy-rp/src
parent5220453d85b1e0f279e94dc1627b7d2434132920 (diff)
Improve rp2040 i2c slave
This commit takes the fixes and error reporting improvements from jcdickinson's work and applies them without overlaying a software state machine on top of the hardware state machine. Also allows configuration of response to 'general call' writes.
Diffstat (limited to 'embassy-rp/src')
-rw-r--r--embassy-rp/src/i2c_slave.rs236
1 files changed, 147 insertions, 89 deletions
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs
index 721b7a1f6..d3cc20f39 100644
--- a/embassy-rp/src/i2c_slave.rs
+++ b/embassy-rp/src/i2c_slave.rs
@@ -4,6 +4,7 @@ use core::marker::PhantomData;
4use core::task::Poll; 4use core::task::Poll;
5 5
6use embassy_hal_internal::into_ref; 6use embassy_hal_internal::into_ref;
7use embassy_time::{block_for, Duration};
7use pac::i2c; 8use pac::i2c;
8 9
9use crate::i2c::{ 10use crate::i2c::{
@@ -21,6 +22,16 @@ pub enum Error {
21 Abort(AbortReason), 22 Abort(AbortReason),
22 /// User passed in a response buffer that was 0 length 23 /// User passed in a response buffer that was 0 length
23 InvalidResponseBufferLength, 24 InvalidResponseBufferLength,
25 /// The response buffer length was too short to contain the message
26 ///
27 /// The length parameter will always be the length of the buffer, and is
28 /// provided as a convenience for matching alongside `Command::Write`.
29 PartialWrite(usize),
30 /// The response buffer length was too short to contain the message
31 ///
32 /// The length parameter will always be the length of the buffer, and is
33 /// provided as a convenience for matching alongside `Command::GeneralCall`.
34 PartialGeneralCall(usize),
24} 35}
25 36
26/// Received command 37/// Received command
@@ -56,17 +67,23 @@ pub enum ReadStatus {
56pub struct Config { 67pub struct Config {
57 /// Target Address 68 /// Target Address
58 pub addr: u16, 69 pub addr: u16,
70 /// Control if the peripheral should ack to and report general calls.
71 pub general_call: bool,
59} 72}
60 73
61impl Default for Config { 74impl Default for Config {
62 fn default() -> Self { 75 fn default() -> Self {
63 Self { addr: 0x55 } 76 Self {
77 addr: 0x55,
78 general_call: true,
79 }
64 } 80 }
65} 81}
66 82
67/// I2CSlave driver. 83/// I2CSlave driver.
68pub struct I2cSlave<'d, T: Instance> { 84pub struct I2cSlave<'d, T: Instance> {
69 phantom: PhantomData<&'d mut T>, 85 phantom: PhantomData<&'d mut T>,
86 pending_byte: Option<u8>,
70} 87}
71 88
72impl<'d, T: Instance> I2cSlave<'d, T> { 89impl<'d, T: Instance> I2cSlave<'d, T> {
@@ -96,7 +113,19 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
96 w.set_master_mode(false); 113 w.set_master_mode(false);
97 w.set_ic_slave_disable(false); 114 w.set_ic_slave_disable(false);
98 w.set_tx_empty_ctrl(true); 115 w.set_tx_empty_ctrl(true);
116 w.set_rx_fifo_full_hld_ctrl(true);
117
118 // This typically makes no sense for a slave, but it is used to
119 // tune spike suppression, according to the datasheet.
120 w.set_speed(pac::i2c::vals::Speed::FAST);
121
122 // Generate stop interrupts for general calls
123 // This also causes stop interrupts for other devices on the bus but those will not be
124 // propagated up to the application.
125 w.set_stop_det_ifaddressed(!config.general_call);
99 }); 126 });
127 p.ic_ack_general_call()
128 .write(|w| w.set_ack_gen_call(config.general_call));
100 129
101 // Set FIFO watermarks to 1 to make things simpler. This is encoded 130 // Set FIFO watermarks to 1 to make things simpler. This is encoded
102 // by a register value of 0. Rx watermark should never change, but Tx watermark will be 131 // by a register value of 0. Rx watermark should never change, but Tx watermark will be
@@ -119,7 +148,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
119 T::Interrupt::unpend(); 148 T::Interrupt::unpend();
120 unsafe { T::Interrupt::enable() }; 149 unsafe { T::Interrupt::enable() };
121 150
122 Self { phantom: PhantomData } 151 Self {
152 phantom: PhantomData,
153 pending_byte: None,
154 }
123 } 155 }
124 156
125 /// Calls `f` to check if we are ready or not. 157 /// Calls `f` to check if we are ready or not.
@@ -133,8 +165,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
133 future::poll_fn(|cx| { 165 future::poll_fn(|cx| {
134 let r = f(self); 166 let r = f(self);
135 167
136 trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0);
137
138 if r.is_pending() { 168 if r.is_pending() {
139 T::waker().register(cx.waker()); 169 T::waker().register(cx.waker());
140 g(self); 170 g(self);
@@ -146,14 +176,36 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
146 } 176 }
147 177
148 #[inline(always)] 178 #[inline(always)]
149 fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { 179 fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
150 let p = T::regs(); 180 let p = T::regs();
151 let len = p.ic_rxflr().read().rxflr() as usize; 181
152 let end = offset + len; 182 for b in &mut buffer[*offset..] {
153 for i in offset..end { 183 if let Some(pending) = self.pending_byte.take() {
154 buffer[i] = p.ic_data_cmd().read().dat(); 184 *b = pending;
185 *offset += 1;
186 continue;
187 }
188
189 let status = p.ic_status().read();
190 if !status.rfne() {
191 break;
192 }
193
194 let dat = p.ic_data_cmd().read();
195 if *offset != 0 && dat.first_data_byte() {
196 // The RP2040 state machine will keep placing bytes into the
197 // FIFO, even if they are part of a subsequent write transaction.
198 //
199 // Unfortunately merely reading ic_data_cmd will consume that
200 // byte, the first byte of the next transaction, so we need
201 // to store it elsewhere
202 self.pending_byte = Some(dat.dat());
203 break;
204 }
205
206 *b = dat.dat();
207 *offset += 1;
155 } 208 }
156 end
157 } 209 }
158 210
159 #[inline(always)] 211 #[inline(always)]
@@ -165,52 +217,62 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
165 } 217 }
166 218
167 /// Wait asynchronously for commands from an I2C master. 219 /// Wait asynchronously for commands from an I2C master.
168 /// `buffer` is provided in case master does a 'write' and is unused for 'read'. 220 /// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'.
169 pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { 221 pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
170 let p = T::regs(); 222 let p = T::regs();
171 223
172 p.ic_clr_intr().read();
173 // set rx fifo watermark to 1 byte 224 // set rx fifo watermark to 1 byte
174 p.ic_rx_tl().write(|w| w.set_rx_tl(0)); 225 p.ic_rx_tl().write(|w| w.set_rx_tl(0));
175 226
176 let mut len = 0; 227 let mut len = 0;
177 let ret = self 228 self.wait_on(
178 .wait_on( 229 |me| {
179 |me| { 230 let stat = p.ic_raw_intr_stat().read();
180 let stat = p.ic_raw_intr_stat().read(); 231
181 if p.ic_rxflr().read().rxflr() > 0 { 232 if p.ic_rxflr().read().rxflr() > 0 {
182 len = me.drain_fifo(buffer, len); 233 me.drain_fifo(buffer, &mut len);
183 // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise 234 // we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise
184 p.ic_rx_tl().write(|w| w.set_rx_tl(11)); 235 p.ic_rx_tl().write(|w| w.set_rx_tl(11));
185 } 236 }
186 237
187 if stat.restart_det() && stat.rd_req() { 238 if buffer.len() == len {
188 Poll::Ready(Ok(Command::WriteRead(len))) 239 if stat.gen_call() {
189 } else if stat.gen_call() && stat.stop_det() && len > 0 { 240 return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));
190 Poll::Ready(Ok(Command::GeneralCall(len)))
191 } else if stat.stop_det() {
192 Poll::Ready(Ok(Command::Write(len)))
193 } else if stat.rd_req() {
194 Poll::Ready(Ok(Command::Read))
195 } else { 241 } else {
196 Poll::Pending 242 return Poll::Ready(Err(Error::PartialWrite(buffer.len())));
197 } 243 }
198 }, 244 }
199 |_me| { 245
200 p.ic_intr_mask().modify(|w| { 246 if stat.restart_det() && stat.rd_req() {
201 w.set_m_stop_det(true); 247 p.ic_clr_restart_det().read();
202 w.set_m_restart_det(true); 248 Poll::Ready(Ok(Command::WriteRead(len)))
203 w.set_m_gen_call(true); 249 } else if stat.gen_call() && stat.stop_det() && len > 0 {
204 w.set_m_rd_req(true); 250 p.ic_clr_gen_call().read();
205 w.set_m_rx_full(true); 251 p.ic_clr_stop_det().read();
206 }); 252 Poll::Ready(Ok(Command::GeneralCall(len)))
207 }, 253 } else if stat.stop_det() && len > 0 {
208 ) 254 p.ic_clr_stop_det().read();
209 .await; 255 Poll::Ready(Ok(Command::Write(len)))
210 256 } else if stat.rd_req() {
211 p.ic_clr_intr().read(); 257 p.ic_clr_stop_det().read();
212 258 p.ic_clr_restart_det().read();
213 ret 259 p.ic_clr_gen_call().read();
260 Poll::Ready(Ok(Command::Read))
261 } else {
262 Poll::Pending
263 }
264 },
265 |_me| {
266 p.ic_intr_mask().modify(|w| {
267 w.set_m_stop_det(true);
268 w.set_m_restart_det(true);
269 w.set_m_gen_call(true);
270 w.set_m_rd_req(true);
271 w.set_m_rx_full(true);
272 });
273 },
274 )
275 .await
214 } 276 }
215 277
216 /// Respond to an I2C master READ command, asynchronously. 278 /// Respond to an I2C master READ command, asynchronously.
@@ -223,47 +285,47 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
223 285
224 let mut chunks = buffer.chunks(FIFO_SIZE as usize); 286 let mut chunks = buffer.chunks(FIFO_SIZE as usize);
225 287
226 let ret = self 288 self.wait_on(
227 .wait_on( 289 |me| {
228 |me| { 290 if let Err(abort_reason) = me.read_and_clear_abort_reason() {
229 if let Err(abort_reason) = me.read_and_clear_abort_reason() { 291 if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
230 if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { 292 p.ic_clr_intr().read();
231 return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); 293 return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
232 } else { 294 } else {
233 return Poll::Ready(Err(abort_reason)); 295 return Poll::Ready(Err(abort_reason));
234 }
235 } 296 }
297 }
236 298
237 if let Some(chunk) = chunks.next() { 299 if let Some(chunk) = chunks.next() {
238 me.write_to_fifo(chunk); 300 me.write_to_fifo(chunk);
239 301
240 Poll::Pending 302 p.ic_clr_rd_req().read();
241 } else {
242 let stat = p.ic_raw_intr_stat().read();
243
244 if stat.rx_done() && stat.stop_det() {
245 Poll::Ready(Ok(ReadStatus::Done))
246 } else if stat.rd_req() {
247 Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
248 } else {
249 Poll::Pending
250 }
251 }
252 },
253 |_me| {
254 p.ic_intr_mask().modify(|w| {
255 w.set_m_stop_det(true);
256 w.set_m_rx_done(true);
257 w.set_m_tx_empty(true);
258 w.set_m_tx_abrt(true);
259 })
260 },
261 )
262 .await;
263 303
264 p.ic_clr_intr().read(); 304 Poll::Pending
305 } else {
306 let stat = p.ic_raw_intr_stat().read();
265 307
266 ret 308 if stat.rx_done() && stat.stop_det() {
309 p.ic_clr_rx_done().read();
310 p.ic_clr_stop_det().read();
311 Poll::Ready(Ok(ReadStatus::Done))
312 } else if stat.rd_req() && stat.tx_empty() {
313 Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
314 } else {
315 Poll::Pending
316 }
317 }
318 },
319 |_me| {
320 p.ic_intr_mask().modify(|w| {
321 w.set_m_stop_det(true);
322 w.set_m_rx_done(true);
323 w.set_m_tx_empty(true);
324 w.set_m_tx_abrt(true);
325 })
326 },
327 )
328 .await
267 } 329 }
268 330
269 /// Respond to reads with the fill byte until the controller stops asking 331 /// Respond to reads with the fill byte until the controller stops asking
@@ -294,10 +356,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
294 let p = T::regs(); 356 let p = T::regs();
295 let mut abort_reason = p.ic_tx_abrt_source().read(); 357 let mut abort_reason = p.ic_tx_abrt_source().read();
296 358
297 // Mask off fifo flush count
298 let tx_flush_cnt = abort_reason.tx_flush_cnt();
299 abort_reason.set_tx_flush_cnt(0);
300
301 // Mask off master_dis 359 // Mask off master_dis
302 abort_reason.set_abrt_master_dis(false); 360 abort_reason.set_abrt_master_dis(false);
303 361
@@ -314,8 +372,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
314 AbortReason::NoAcknowledge 372 AbortReason::NoAcknowledge
315 } else if abort_reason.arb_lost() { 373 } else if abort_reason.arb_lost() {
316 AbortReason::ArbitrationLoss 374 AbortReason::ArbitrationLoss
317 } else if abort_reason.abrt_slvflush_txfifo() { 375 } else if abort_reason.tx_flush_cnt() > 0 {
318 AbortReason::TxNotEmpty(tx_flush_cnt) 376 AbortReason::TxNotEmpty(abort_reason.tx_flush_cnt())
319 } else { 377 } else {
320 AbortReason::Other(abort_reason.0) 378 AbortReason::Other(abort_reason.0)
321 }; 379 };