diff options
| author | Timo Kröger <[email protected]> | 2023-01-05 18:45:58 +0100 |
|---|---|---|
| committer | Timo Kröger <[email protected]> | 2023-01-05 18:45:58 +0100 |
| commit | 1096a9746c0843e4a26fcec3fca7f9d6d9d57f01 (patch) | |
| tree | 93fe51a391cb60198b76f4e1b3cc405e7bc0d948 | |
| parent | 840a75674b632485d5b45cfece27870ded0edfcd (diff) | |
rp: Improve BufferedUart interrupt handling
* Only clear interrupt flags that have fired (so that we do not lose any error flags)
* Enable RX interrupt when a read is requested, disable it when the RX buffer is full
* Rework TX interrupt handling: its "edge" triggered by a FIFO threshold
| -rw-r--r-- | embassy-rp/src/uart/buffered.rs | 79 |
1 files changed, 58 insertions, 21 deletions
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6465a20c6..b9fc33eae 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs | |||
| @@ -7,6 +7,7 @@ use embassy_hal_common::atomic_ring_buffer::RingBuffer; | |||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 8 | ||
| 9 | use super::*; | 9 | use super::*; |
| 10 | use crate::RegExt; | ||
| 10 | 11 | ||
| 11 | pub struct State { | 12 | pub struct State { |
| 12 | tx_waker: AtomicWaker, | 13 | tx_waker: AtomicWaker, |
| @@ -57,6 +58,19 @@ fn init<'d, T: Instance + 'd>( | |||
| 57 | let len = rx_buffer.len(); | 58 | let len = rx_buffer.len(); |
| 58 | unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; | 59 | unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; |
| 59 | 60 | ||
| 61 | // From the datasheet: | ||
| 62 | // "The transmit interrupt is based on a transition through a level, rather | ||
| 63 | // than on the level itself. When the interrupt and the UART is enabled | ||
| 64 | // before any data is written to the transmit FIFO the interrupt is not set. | ||
| 65 | // The interrupt is only set, after written data leaves the single location | ||
| 66 | // of the transmit FIFO and it becomes empty." | ||
| 67 | // | ||
| 68 | // This means we can leave the interrupt enabled the whole time as long as | ||
| 69 | // we clear it after it happens. The downside is that the we manually have | ||
| 70 | // to pend the ISR when we want data transmission to start. | ||
| 71 | let regs = T::regs(); | ||
| 72 | unsafe { regs.uartimsc().write_set(|w| w.set_txim(true)) }; | ||
| 73 | |||
| 60 | irq.set_handler(on_interrupt::<T>); | 74 | irq.set_handler(on_interrupt::<T>); |
| 61 | irq.unpend(); | 75 | irq.unpend(); |
| 62 | irq.enable(); | 76 | irq.enable(); |
| @@ -159,8 +173,6 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { | |||
| 159 | 173 | ||
| 160 | fn read<'a>(buf: &'a mut [u8]) -> impl Future<Output = Result<usize, Error>> + 'a { | 174 | fn read<'a>(buf: &'a mut [u8]) -> impl Future<Output = Result<usize, Error>> + 'a { |
| 161 | poll_fn(move |cx| { | 175 | poll_fn(move |cx| { |
| 162 | unsafe { T::Interrupt::steal() }.pend(); | ||
| 163 | |||
| 164 | let state = T::state(); | 176 | let state = T::state(); |
| 165 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 177 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 166 | let n = rx_reader.pop(|data| { | 178 | let n = rx_reader.pop(|data| { |
| @@ -173,14 +185,22 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { | |||
| 173 | return Poll::Pending; | 185 | return Poll::Pending; |
| 174 | } | 186 | } |
| 175 | 187 | ||
| 188 | // (Re-)Enable the interrupt to receive more data in case it was | ||
| 189 | // disabled because the buffer was full. | ||
| 190 | let regs = T::regs(); | ||
| 191 | unsafe { | ||
| 192 | regs.uartimsc().write_set(|w| { | ||
| 193 | w.set_rxim(true); | ||
| 194 | w.set_rtim(true); | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | |||
| 176 | Poll::Ready(Ok(n)) | 198 | Poll::Ready(Ok(n)) |
| 177 | }) | 199 | }) |
| 178 | } | 200 | } |
| 179 | 201 | ||
| 180 | fn fill_buf<'a>() -> impl Future<Output = Result<&'a [u8], Error>> { | 202 | fn fill_buf<'a>() -> impl Future<Output = Result<&'a [u8], Error>> { |
| 181 | poll_fn(move |cx| { | 203 | poll_fn(move |cx| { |
| 182 | unsafe { T::Interrupt::steal() }.pend(); | ||
| 183 | |||
| 184 | let state = T::state(); | 204 | let state = T::state(); |
| 185 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 205 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 186 | let (p, n) = rx_reader.pop_buf(); | 206 | let (p, n) = rx_reader.pop_buf(); |
| @@ -198,6 +218,16 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { | |||
| 198 | let state = T::state(); | 218 | let state = T::state(); |
| 199 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 219 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 200 | rx_reader.pop_done(amt); | 220 | rx_reader.pop_done(amt); |
| 221 | |||
| 222 | // (Re-)Enable the interrupt to receive more data in case it was | ||
| 223 | // disabled because the buffer was full. | ||
| 224 | let regs = T::regs(); | ||
| 225 | unsafe { | ||
| 226 | regs.uartimsc().write_set(|w| { | ||
| 227 | w.set_rxim(true); | ||
| 228 | w.set_rtim(true); | ||
| 229 | }); | ||
| 230 | } | ||
| 201 | } | 231 | } |
| 202 | } | 232 | } |
| 203 | 233 | ||
| @@ -250,6 +280,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { | |||
| 250 | return Poll::Pending; | 280 | return Poll::Pending; |
| 251 | } | 281 | } |
| 252 | 282 | ||
| 283 | // The TX interrupt only triggers when the there was data in the | ||
| 284 | // FIFO and the number of bytes drops below a threshold. When the | ||
| 285 | // FIFO was empty we have to manually pend the interrupt to shovel | ||
| 286 | // TX data from the buffer into the FIFO. | ||
| 253 | unsafe { T::Interrupt::steal() }.pend(); | 287 | unsafe { T::Interrupt::steal() }.pend(); |
| 254 | Poll::Ready(Ok(n)) | 288 | Poll::Ready(Ok(n)) |
| 255 | }) | 289 | }) |
| @@ -299,14 +333,22 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { | |||
| 299 | } | 333 | } |
| 300 | 334 | ||
| 301 | pub(crate) unsafe fn on_interrupt<T: Instance>(_: *mut ()) { | 335 | pub(crate) unsafe fn on_interrupt<T: Instance>(_: *mut ()) { |
| 302 | trace!("on_interrupt"); | ||
| 303 | |||
| 304 | let r = T::regs(); | 336 | let r = T::regs(); |
| 305 | let s = T::state(); | 337 | let s = T::state(); |
| 306 | 338 | ||
| 307 | unsafe { | 339 | unsafe { |
| 340 | // Clear TX and error interrupt flags | ||
| 341 | // RX interrupt flags are cleared by reading from the FIFO. | ||
| 308 | let ris = r.uartris().read(); | 342 | let ris = r.uartris().read(); |
| 309 | let mut mis = r.uartimsc().read(); | 343 | r.uarticr().write(|w| { |
| 344 | w.set_txic(ris.txris()); | ||
| 345 | w.set_feic(ris.feris()); | ||
| 346 | w.set_peic(ris.peris()); | ||
| 347 | w.set_beic(ris.beris()); | ||
| 348 | w.set_oeic(ris.oeris()); | ||
| 349 | }); | ||
| 350 | |||
| 351 | trace!("on_interrupt ris={=u32:#X}", ris.0); | ||
| 310 | 352 | ||
| 311 | // Errors | 353 | // Errors |
| 312 | if ris.feris() { | 354 | if ris.feris() { |
| @@ -321,13 +363,6 @@ pub(crate) unsafe fn on_interrupt<T: Instance>(_: *mut ()) { | |||
| 321 | if ris.oeris() { | 363 | if ris.oeris() { |
| 322 | warn!("Overrun error"); | 364 | warn!("Overrun error"); |
| 323 | } | 365 | } |
| 324 | // Clear any error flags | ||
| 325 | r.uarticr().write(|w| { | ||
| 326 | w.set_feic(true); | ||
| 327 | w.set_peic(true); | ||
| 328 | w.set_beic(true); | ||
| 329 | w.set_oeic(true); | ||
| 330 | }); | ||
| 331 | 366 | ||
| 332 | // RX | 367 | // RX |
| 333 | let mut rx_writer = s.rx_buf.writer(); | 368 | let mut rx_writer = s.rx_buf.writer(); |
| @@ -345,8 +380,12 @@ pub(crate) unsafe fn on_interrupt<T: Instance>(_: *mut ()) { | |||
| 345 | s.rx_waker.wake(); | 380 | s.rx_waker.wake(); |
| 346 | } | 381 | } |
| 347 | // Disable any further RX interrupts when the buffer becomes full. | 382 | // Disable any further RX interrupts when the buffer becomes full. |
| 348 | mis.set_rxim(!s.rx_buf.is_full()); | 383 | if s.rx_buf.is_full() { |
| 349 | mis.set_rtim(!s.rx_buf.is_full()); | 384 | r.uartimsc().write_clear(|w| { |
| 385 | w.set_rxim(true); | ||
| 386 | w.set_rtim(true); | ||
| 387 | }); | ||
| 388 | } | ||
| 350 | 389 | ||
| 351 | // TX | 390 | // TX |
| 352 | let mut tx_reader = s.tx_buf.reader(); | 391 | let mut tx_reader = s.tx_buf.reader(); |
| @@ -363,11 +402,9 @@ pub(crate) unsafe fn on_interrupt<T: Instance>(_: *mut ()) { | |||
| 363 | tx_reader.pop_done(n_written); | 402 | tx_reader.pop_done(n_written); |
| 364 | s.tx_waker.wake(); | 403 | s.tx_waker.wake(); |
| 365 | } | 404 | } |
| 366 | // Disable the TX interrupt when we do not have more data to send. | 405 | // The TX interrupt only triggers once when the FIFO threshold is |
| 367 | mis.set_txim(!s.tx_buf.is_empty()); | 406 | // crossed. No need to disable it when the buffer becomes empty |
| 368 | 407 | // as it does re-trigger anymore once we have cleared it. | |
| 369 | // Update interrupt mask. | ||
| 370 | r.uartimsc().write_value(mis); | ||
| 371 | } | 408 | } |
| 372 | } | 409 | } |
| 373 | 410 | ||
