Skip to content

Commit 96fcca4

Browse files
committed
RMT: add poll() method to blocking transactions
this allows properly interleaving several such transactions (as the HIL loopback tests do, in fact) or other work
1 parent 834fe07 commit 96fcca4

File tree

3 files changed

+112
-66
lines changed

3 files changed

+112
-66
lines changed

esp-hal/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- RMT `TxChannelCreator` and `RxChannelCreator` now have a `DriverMode` generic parameter; `TxChannelCreatorAsync` and `RxChannelCreatorAsync` have been removed. (#3505)
2424
- RMT `ChannelCreator` methods have been renamed from `configure` to `configure_tx` and `configure_rx` to avoid trait disambiguation issues. (#3505)
2525
- The RMT `Error` type has been marked `non_exhaustive` (#3701)
26+
- Blocking RMT transactions can now be `poll`ed without blocking, returning whether they have completed. (#3716)
2627

2728
### Fixed
2829

esp-hal/src/rmt.rs

Lines changed: 94 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,10 @@ where
998998
}
999999

10001000
/// An in-progress transaction for a single shot TX transaction.
1001+
///
1002+
/// If the data size exceeds the size of the internal buffer, `.poll()` or
1003+
/// `.wait()` needs to be called before the entire buffer has been sent to avoid
1004+
/// underruns.
10011005
pub struct SingleShotTxTransaction<'a, Raw>
10021006
where
10031007
Raw: TxChannelInternal,
@@ -1017,15 +1021,61 @@ impl<Raw> SingleShotTxTransaction<'_, Raw>
10171021
where
10181022
Raw: TxChannelInternal,
10191023
{
1020-
/// Wait for the transaction to complete
1021-
pub fn wait(mut self) -> Result<Channel<Blocking, Raw>, (Error, Channel<Blocking, Raw>)> {
1024+
fn poll_internal(&mut self) -> Option<Event> {
10221025
let raw = self.channel.raw;
1023-
let memsize = raw.memsize().codes();
10241026

1027+
let status = raw.get_tx_status();
1028+
if status == Some(Event::Threshold) {
1029+
raw.reset_tx_threshold_set();
1030+
1031+
if !self.remaining_data.is_empty() {
1032+
// re-fill TX RAM
1033+
let memsize = raw.memsize().codes();
1034+
let ptr = unsafe { raw.channel_ram_start().add(self.ram_index) };
1035+
let count = self.remaining_data.len().min(memsize / 2);
1036+
let (chunk, remaining) = self.remaining_data.split_at(count);
1037+
for (idx, entry) in chunk.iter().enumerate() {
1038+
unsafe {
1039+
ptr.add(idx).write_volatile(*entry);
1040+
}
1041+
}
1042+
1043+
// If count == memsize / 2 codes were written, update ram_index as
1044+
// - 0 -> memsize / 2
1045+
// - memsize / 2 -> 0
1046+
// Otherwise, for count < memsize / 2, the new position is invalid but the new
1047+
// slice is empty and we won't use ram_index again.
1048+
self.ram_index = memsize / 2 - self.ram_index;
1049+
self.remaining_data = remaining;
1050+
debug_assert!(
1051+
self.ram_index == 0
1052+
|| self.ram_index == memsize / 2
1053+
|| self.remaining_data.is_empty()
1054+
);
1055+
}
1056+
}
1057+
1058+
status
1059+
}
1060+
1061+
/// Check transmission status and write new data to the hardware if
1062+
/// necessary.
1063+
///
1064+
/// Returns whether transmission has ended (whether successfully or with an
1065+
/// error). In that case, a subsequent call to `wait()` returns immediately.
1066+
pub fn poll(&mut self) -> bool {
1067+
match self.poll_internal() {
1068+
Some(Event::Error | Event::End) => true,
1069+
Some(Event::Threshold) | None => false,
1070+
}
1071+
}
1072+
1073+
/// Wait for the transaction to complete
1074+
pub fn wait(mut self) -> Result<Channel<Blocking, Raw>, (Error, Channel<Blocking, Raw>)> {
1075+
// Not sure that all the error cases below can happen. However, it's best to
1076+
// handle them to be sure that we don't lock up here in case they can happen.
10251077
loop {
1026-
// Not sure that all the error cases below can happen. However, it's best to
1027-
// handle them to be sure that we don't lock up here in case they can happen.
1028-
match raw.get_tx_status() {
1078+
match self.poll_internal() {
10291079
Some(Event::Error) => break Err((Error::TransmissionError, self.channel)),
10301080
Some(Event::End) => {
10311081
if !self.remaining_data.is_empty() {
@@ -1036,34 +1086,6 @@ where
10361086
break Ok(self.channel);
10371087
}
10381088
}
1039-
Some(Event::Threshold) => {
1040-
raw.reset_tx_threshold_set();
1041-
1042-
if !self.remaining_data.is_empty() {
1043-
// re-fill TX RAM
1044-
let ptr = unsafe { raw.channel_ram_start().add(self.ram_index) };
1045-
let count = self.remaining_data.len().min(memsize / 2);
1046-
let (chunk, remaining) = self.remaining_data.split_at(count);
1047-
for (idx, entry) in chunk.iter().enumerate() {
1048-
unsafe {
1049-
ptr.add(idx).write_volatile(*entry);
1050-
}
1051-
}
1052-
1053-
// If count == memsize / 2 codes were written, update ram_index as
1054-
// - 0 -> memsize / 2
1055-
// - memsize / 2 -> 0
1056-
// Otherwise, for count < memsize / 2, the new position is invalid but the new
1057-
// slice is empty and we won't use ram_index again.
1058-
self.ram_index = memsize / 2 - self.ram_index;
1059-
self.remaining_data = remaining;
1060-
debug_assert!(
1061-
self.ram_index == 0
1062-
|| self.ram_index == memsize / 2
1063-
|| self.remaining_data.is_empty()
1064-
);
1065-
}
1066-
}
10671089
_ => continue,
10681090
}
10691091
}
@@ -1298,32 +1320,53 @@ pub struct RxTransaction<'a, Raw: RxChannelInternal> {
12981320
}
12991321

13001322
impl<Raw: RxChannelInternal> RxTransaction<'_, Raw> {
1301-
/// Wait for the transaction to complete
1302-
pub fn wait(self) -> Result<Channel<Blocking, Raw>, (Error, Channel<Blocking, Raw>)> {
1323+
fn poll_internal(&mut self) -> Option<Event> {
13031324
let raw = self.channel.raw;
13041325

1305-
loop {
1306-
if raw.is_error() {
1307-
return Err((Error::ReceiverError, self.channel));
1308-
}
1326+
let status = raw.get_rx_status();
1327+
if status == Some(Event::End) {
1328+
// Do not clear the interrupt flags here: Subsequent calls of wait() must
1329+
// be able to observe them if this is currently called via poll()
1330+
raw.stop_rx();
1331+
raw.update();
13091332

1310-
if raw.is_rx_done() {
1311-
break;
1333+
let ptr = raw.channel_ram_start();
1334+
// SAFETY: RxChannel.receive() verifies that the length of self.data does not
1335+
// exceed the channel RAM size.
1336+
for (idx, entry) in self.data.iter_mut().enumerate() {
1337+
*entry = unsafe { ptr.add(idx).read_volatile() };
13121338
}
13131339
}
13141340

1315-
raw.stop_rx();
1316-
raw.clear_rx_interrupts();
1317-
raw.update();
1341+
status
1342+
}
13181343

1319-
let ptr = raw.channel_ram_start();
1320-
// SAFETY: RxChannel.receive() verifies that the length of self.data does not
1321-
// exceed the channel RAM size.
1322-
for (idx, entry) in self.data.iter_mut().enumerate() {
1323-
*entry = unsafe { ptr.add(idx).read_volatile() };
1344+
/// Check receive status
1345+
///
1346+
/// Returns whether reception has ended (whether successfully or with an
1347+
/// error). In that case, a subsequent call to `wait()` returns immediately.
1348+
pub fn poll(&mut self) -> bool {
1349+
match self.poll_internal() {
1350+
Some(Event::Error | Event::End) => true,
1351+
Some(Event::Threshold) | None => false,
13241352
}
1353+
}
13251354

1326-
Ok(self.channel)
1355+
/// Wait for the transaction to complete
1356+
pub fn wait(mut self) -> Result<Channel<Blocking, Raw>, (Error, Channel<Blocking, Raw>)> {
1357+
let raw = self.channel.raw;
1358+
1359+
let result = loop {
1360+
match self.poll_internal() {
1361+
Some(Event::Error) => break Err((Error::ReceiverError, self.channel)),
1362+
Some(Event::End) => break Ok(self.channel),
1363+
_ => continue,
1364+
}
1365+
};
1366+
1367+
raw.clear_rx_interrupts();
1368+
1369+
result
13271370
}
13281371
}
13291372

hil-test/tests/rmt.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn generate_tx_data<const TX_LEN: usize>(write_end_marker: bool) -> [u32; TX_LEN
7979

8080
// Run a test where some data is sent from one channel and looped back to
8181
// another one for receive, and verify that the data matches.
82-
fn do_rmt_loopback<const TX_LEN: usize>(tx_memsize: u8, rx_memsize: u8, wait_tx_first: bool) {
82+
fn do_rmt_loopback<const TX_LEN: usize>(tx_memsize: u8, rx_memsize: u8) {
8383
use esp_hal::rmt::{RxChannel, TxChannel};
8484

8585
let peripherals = esp_hal::init(esp_hal::Config::default());
@@ -96,17 +96,20 @@ fn do_rmt_loopback<const TX_LEN: usize>(tx_memsize: u8, rx_memsize: u8, wait_tx_
9696
let tx_data: [_; TX_LEN] = generate_tx_data(true);
9797
let mut rcv_data: [u32; TX_LEN] = [PulseCode::empty(); TX_LEN];
9898

99-
let rx_transaction = rx_channel.receive(&mut rcv_data).unwrap();
100-
let tx_transaction = tx_channel.transmit(&tx_data).unwrap();
99+
let mut rx_transaction = rx_channel.receive(&mut rcv_data).unwrap();
100+
let mut tx_transaction = tx_channel.transmit(&tx_data).unwrap();
101101

102-
if wait_tx_first {
103-
tx_transaction.wait().unwrap();
104-
rx_transaction.wait().unwrap();
105-
} else {
106-
rx_transaction.wait().unwrap();
107-
tx_transaction.wait().unwrap();
102+
loop {
103+
let tx_done = tx_transaction.poll();
104+
let rx_done = rx_transaction.poll();
105+
if tx_done && rx_done {
106+
break;
107+
}
108108
}
109109

110+
tx_transaction.wait().unwrap();
111+
rx_transaction.wait().unwrap();
112+
110113
// the last two pulse-codes are the ones which wait for the timeout so
111114
// they can't be equal
112115
assert_eq!(&tx_data[..TX_LEN - 2], &rcv_data[..TX_LEN - 2]);
@@ -180,7 +183,7 @@ mod tests {
180183
#[test]
181184
fn rmt_loopback_simple() {
182185
// 20 codes fit a single RAM block
183-
do_rmt_loopback::<20>(1, 1, false);
186+
do_rmt_loopback::<20>(1, 1);
184187
}
185188

186189
#[test]
@@ -191,7 +194,7 @@ mod tests {
191194
#[test]
192195
fn rmt_loopback_extended_ram() {
193196
// 80 codes require two RAM blocks
194-
do_rmt_loopback::<80>(2, 2, false);
197+
do_rmt_loopback::<80>(2, 2);
195198
}
196199

197200
// FIXME: This test currently fails on esp32 with an rmt::Error::ReceiverError,
@@ -204,9 +207,8 @@ mod tests {
204207
#[test]
205208
fn rmt_loopback_tx_wrap() {
206209
// 80 codes require two RAM blocks; thus a tx channel with only 1 block requires
207-
// wrapping. We need to .wait() on the tx transaction first to handle
208-
// this.
209-
do_rmt_loopback::<80>(1, 2, true);
210+
// wrapping.
211+
do_rmt_loopback::<80>(1, 2);
210212
}
211213

212214
// FIXME: This test can't work right now, because wrapping rx is not
@@ -216,7 +218,7 @@ mod tests {
216218
// fn rmt_loopback_rx_wrap() {
217219
// // 80 codes require two RAM blocks; thus an rx channel with only 1 block
218220
// // requires wrapping
219-
// do_rmt_loopback<80>(2, 1, false);
221+
// do_rmt_loopback<80>(2, 1);
220222
// }
221223

222224
#[test]

0 commit comments

Comments
 (0)