Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for buffered uart #314

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

huseinmsft
Copy link
Contributor

Implement a circular buffer for UART DMA for RX mode to allow continuous reading or data on UART RX

/// Transfer is intended to be done continuously, such as for a circular buffer
pub is_continuous: bool,

/// Transfer is software triggerred
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: typo, triggered*

Comment on lines +737 to +738
T::info().regs.fiford().as_ptr() as *const u8 as *const u32,
buffer as *mut [u8] as *mut u32,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here describing why you're casting twice like this? If I'm following, this looks like you've got potentially a reference to some bytes (slice?) for which you're getting a pointer (instead of the ref). And then you're converting that into a u32 pointer presumably by grouping the bytes?

// sleep until next polling epoch, or if we detect a UART transfer error event
let res = select(
// use embassy_time to enable polling the bus for more data
embassy_time::Timer::after_micros(buffer_config.polling_rate),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: our timer implementation is still based on the RTC so we can only sleep in increments of milliseconds. This will basically just be after_millis(1)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're able to get the baudrate of the UART up to 4 mbps we'll likely need to dedicate some time to configuring the time_driver with a different, faster clock

.await;

match res {
Either::First(()) | Either::Second(Ok(())) => (),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need an on_drop() function in the event that we return an error from the second future?

@@ -218,10 +219,22 @@ impl<'a> UartTx<'a, Blocking> {
}
}

struct BufferConfig {
#[cfg(feature = "time")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should consider adding GH tasks/issues to track:

  1. Using a different/more flexible/dedicated hardware timer for background polling of the UART DMA buffer
  2. Refactoring the circular buffer to use one of the off-the-shelf embassy solutions if possible

Comment on lines -130 to -143
unsafe {
DESCRIPTORS.list[channel].reserved = 0;
if dir == Direction::MemoryToPeripheral {
DESCRIPTORS.list[channel].dst_data_end_addr = dstbase as u32;
} else {
DESCRIPTORS.list[channel].dst_data_end_addr = dstbase as u32 + (xfercount * xferwidth) as u32;
}
if dir == Direction::PeripheralToMemory {
DESCRIPTORS.list[channel].src_data_end_addr = srcbase as u32;
} else {
DESCRIPTORS.list[channel].src_data_end_addr = srcbase as u32 + (xfercount * xferwidth) as u32;
}
DESCRIPTORS.list[channel].nxt_desc_link_addr = 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be part of its own commit. Why is this needed?

Comment on lines +150 to +161
if options.is_continuous {
w.reload().enabled();
w.clrtrig().clear_bit();
} else {
w.reload().disabled();
w.clrtrig().set_bit();
}
if options.is_sw_trig {
w.swtrig().set_bit();
} else {
w.swtrig().clear_bit();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about using variant()?

w.reload().variant(options.is_continuous).
    clrtrig().variant(!options.is_continuous).
    swtrig().variant(options.is_sw_trig)

@@ -49,6 +49,7 @@ pub struct UartTx<'a, M: Mode> {
/// Uart RX driver.
pub struct UartRx<'a, M: Mode> {
info: Info,
_buffer_config: Option<BufferConfig>,
Copy link
Contributor

@felipebalbi felipebalbi Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, you should produce new structs that contain a UartRx. In fact, adding a new directory uart and moving the current driver to uart/mod.rs would allow you to introduce all your new code within uart/buffered.rs. Then, you can have a new struct BufferedUartRx, struct BufferedUartTx, and BufferedUart all isolated from the regular, unbuffered uart. Additionally, you would not need a BufferConfig. A regular Config would suffice, because you would already have a separate buffered module.

Comment on lines +764 to +775
{
if self._buffer_config.is_some() {
self.read_buffered(buf).await
} else {
self.read_unbuffered(buf).await
}
}

#[cfg(not(feature = "time"))]
{
self.read_unbuffered(buf).await
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you had your separate buffered.rs submodule, this wouldn't be necessary. You would have your isolated read() implementation.

@jerrysxie jerrysxie added the enhancement New feature or request label Feb 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants