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

feat: uart tx software break #2872

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Conversation

zpg6
Copy link

@zpg6 zpg6 commented Dec 30, 2024

Thank you for your contribution!

We appreciate the time and effort you've put into this pull request.
To help us review it efficiently, please ensure you've gone through the following checklist:

Submission Checklist 📝

  • I have updated existing examples or added new ones (if applicable).
  • I have used cargo xtask fmt-packages command to ensure that all changed code is formatted correctly.
  • My changes were added to the CHANGELOG.md in the proper section.
  • I have added necessary changes to user code to the Migration Guide.
  • My changes are in accordance to the esp-rs API guidelines

Extra:

Pull Request Details 📖

Description

Closes #2752

Adding ability to send a software break on the UART TX line. This is achieved by inverting the TX line, waiting some time, and then reverting the line back. The duration to leave it inverted (the break duration) is kept in bit time - the time it takes to send one bit at the current baud rate.

New Functions:

  • send_break_async(bits: u32) - async delay during the break
  • send_break(bits: u32) - busy waits during the break

Open Items

  • How to retrieve the current baud rate for break duration calculation
  • How to perform async delay within the Uart (can I use embassy-time after(..).await?)

Testing

Tested with the added examples/uart_send_break and examples/uart_send_break_async on my pocket oscilloscope and visually with the helper LED toggle also in each example.

Since I also have open PR #2858 for detecting breaks - these can eventually be tested together in HIL.

@zpg6
Copy link
Author

zpg6 commented Dec 30, 2024

Paused awaiting feedback on open items:


Baud Rate Retrieval

How best to retrieve the current baud rate for break duration calculation

Some Options

  1. Pull from registers and back-calculate (unsure of the edge cases of this)
  2. Keep stored somewhere
  3. Input it to the send_break function.
  4. Make send_break time-based not bit-time-based. No longer requiring baud rate calculation, and user specifies a time duration to break for.

Async Delay Time-Keeping

How to perform async delay within the Uart driver. Not sure if there is much precedent for this.

Some Options

  1. I can use embassy-time after(..).await
  2. Don't offer with async delay?
  3. ... not sure

@MabezDev
Copy link
Member

MabezDev commented Jan 3, 2025

Keep stored somewhere

I think storing the currently set value (essentially caching it) is the best option there.

How to perform async delay within the Uart driver. Not sure if there is much precedent for this.

Why would you need to do this? Can this not be done outside the driver? In other words, can't we just delay after sending the break?

@zpg6
Copy link
Author

zpg6 commented Jan 3, 2025

Why would you need to do this? Can this not be done outside the driver? In other words, can't we just delay after sending the break?

With my method, the break is for the duration you choose.

  1. Invert the TX line
  2. Wait (either async or blocking)
  3. Put back the TX line

The duration you wait may depend on people's use cases. But a default duration might be what would minimally trigger the break interrupt on ESP. I think just one bit time past the stop bit - so for 8N1 it would be min13 bits?

Anyways, 13bits at 19200 is almost a millisecond wasted to busy wait if not for an async offering.

/// the break is just is busy-waiting.
pub fn send_break(&mut self, bits: u32) {
// Invert the TX line
self.register_block()
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is quite right, we should be using the tx_brk register bit and the txbrk_conf (I think it's idle_conf on the esp32/esp32s2).

See the esp-idf impl here: https://github.com/espressif/esp-idf/blob/b5ac4fbdf9e9fb320bb0a98ee4fbaa18f8566f37/components/hal/esp32s3/include/hal/uart_ll.h#L535-L543

You can then wait for an UART_INTR_TX_BRK_DONE interrupt to fire to implement this in an async way, and just wait on reading the status registers for the blocking one. This completely avoids the delay issue you mentioned below.

Copy link
Author

Choose a reason for hiding this comment

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

I didn't know about this, let me test with it a bit

Copy link
Author

@zpg6 zpg6 Jan 18, 2025

Choose a reason for hiding this comment

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

@MabezDev Ah I do know about this. Coming from IDF land, there is a function uart_write_bytes_with_break that will send a break AFTER the data is transmitted. For some use cases like mine, automotive LIN, I want to have a break at the START of a data transmission.

As I understand, when the txd_brk is set, the ESP internally doesn't act upon it until the next non-zero-length transmission finishes and then sends a break. There is no way to alter this behavior I'm aware of.

My solution circumvents this, and works such that a break sent on TX can be detected reliably by the ESPs own break detection on the RX. (Assuming the minimum bit duration is met as mentioned above.)

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/uart.html#transmit-data

The function uart_write_bytes_with_break() is similar to uart_write_bytes() but adds a serial break signal at the end of the transmission. A 'serial break signal' means holding the TX line low for a period longer than one data frame.

Copy link
Author

Choose a reason for hiding this comment

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

I'm going off of ESP32 technical reference manual:
https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf

  • UART_TX_DONE_INT: Triggered when the transmitter has sent out all FIFO data.
  • UART_TX_BRK_IDLE_DONE_INT: Triggered when the transmitter’s idle state has been kept to a minimum
    after sending the last data.
  • UART_TX_BRK_DONE_INT: Triggered when the transmitter completes sending NULL characters, after all
    data in transmit-FIFO are sent.

These descriptions are a bit tough to understand, but I think they confirm the behavior "break gets sent after completion of non-zero-length transmission".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UART: transmitting BRK is not supported
2 participants