UART break at first

Bencium
Posts: 1
Joined: Fri May 20, 2022 5:29 pm

Re: UART break at first

Postby Bencium » Fri May 20, 2022 8:48 pm

Hi sorry to bump this thread.

I have joined the forum as am interested in implementing some Lin communication on an ESP to control some ambient light lin devices on my car and I have found this thread to very interesting.
Phatpaul, I was wondering if you are sharing your LIn driver including the headers. It seems to me something very complex for my skills level and it would be a great starting point to have a working driver as an start point.

Will it work as well in an ESP8266?

Thanks a lot in advance
Best regards

phatpaul
Posts: 115
Joined: Fri Aug 24, 2018 1:14 pm

Re: UART break at first

Postby phatpaul » Fri Jun 03, 2022 6:15 pm

Well, a recent update to esp-idf has broken my LIN break method. In 3.3.5 it works, then in 3.3.6, the break is not long enough.

From the release notes:
https://github.com/espressif/esp-idf/re ... tag/v3.3.6

Perhaps something in one of these changes broke my LIN solution:

Freemodbus: Fixed rs485 rts de-assert fail when tx delayed (#6728)
Freemodbus: Added the MB_PORT_TASK_AFFINITY kconfig option for modbus tasks (#6700)
UART: Fixed uart module reset issue (#1202)
UART: Added config to support placing UART ISR in IRAM
UART: Fixed a typo in the error message of invalid rx_thresh_xoff

@Bencium I can share my example project, but I'll need a little time to prepare it. Do you need LIN master or slave for your application? If the lights you want to control are already commanded by a master (i.e. part of your car) then I'm not sure how you can override that.

FlorianR
Posts: 29
Joined: Fri Mar 12, 2021 2:00 pm

Re: UART break at first

Postby FlorianR » Fri Jun 03, 2022 7:33 pm

Hi I had the same problem and "fixed" it. After lots of trial and error I added a second wait_for_tx_done after the first one. And now it "works" again for some reason. I am not happy with the solution, but after spending two days to understand how to add a brake at-front into the library I gave up. The break-at-end is so complicated embedded that I can not get it to send the break before the data....
Best regards and I hope the quick fix fix helps. I wish I could understand the UART code better and contribute something with more value.

phatpaul
Posts: 115
Joined: Fri Aug 24, 2018 1:14 pm

Re: UART break at first

Postby phatpaul » Mon Jun 06, 2022 2:29 pm

@FlorianR thanks! You saved me a bunch of time. (I had just reverted to 3.3.5 until I had time to bisect the problem.)

I also don't know why this works, but since this whole break-at-first method is a hack anyway, your solution fits perfectly.

To be clear, here's the modified

Code: Select all

void LIN_driver_start_frame(uint8_t pid)
{
    uint32_t baudrate;
    uint8_t master_tx_buf[2];
    master_tx_buf[0] = 0x55; // sync byte
    master_tx_buf[1] = pid;

    uint8_t dummy = 0;

    uart_flush_input(lin_uart_num);    // since we are the master, clear out rx buff before starting a frame
    xQueueReset(lin_uart_event_queue); // clear the q for slave rx uart breaks
    /* Send break character */
    // this method sucks b/c it must send a dummy char before the break
    // Note: if use this, then +1 to LIN_IN_HEADER_SIZE for dummy byte.
    //uart_write_bytes_with_break(lin_uart_num, (char *)&dummy, 1, LIN_BREAK_LEN);

    // this method really sucks because of a blocking delay!
    //uart_set_line_inverse(param_ptr_config->uart_port_num, UART_INVERSE_TXD);
    //ets_delay_us(677);
    //uart_set_line_inverse(param_ptr_config->uart_port_num, UART_INVERSE_DISABLE);

    //uart_hal_tx_break(&(uart_context[uart_num].hal), 0); - not available in IDF 3x

    // Temporarily change baudrate to slightly lower and send a 0 byte
    // to get the right baudrate, we want 9 bits (1 start + 8 data) to take the time of 13 bits (one break)
    uart_get_baudrate(lin_uart_num, &baudrate);
#define LIN_BREAK_BAUDRATE(BAUD) ((BAUD * 9) / 13)
    uart_set_baudrate(lin_uart_num, LIN_BREAK_BAUDRATE(baudrate));
    uart_write_bytes(lin_uart_num, (char *)&dummy, 1); // send a zero byte.  This call must be blocking.
    uart_wait_tx_done(lin_uart_num, 2);                // shouldn't be necessary??
    uart_wait_tx_done(lin_uart_num, 2);                // add 2nd uart_wait_tx_done per https://esp32.com/viewtopic.php?p=98456#p98456
    uart_set_baudrate(lin_uart_num, baudrate);         // set baudrate back to normal after break is sent
    // we have to fake a break detect for the slave, since it doesn't detect it with this method.
    uart_event_t event = {.type = UART_BREAK};
    xQueueSend(lin_uart_event_queue, (void *)&event, 0);

    // Now send the sync and PID bytes
    uart_write_bytes(lin_uart_num, (char *)master_tx_buf, sizeof(master_tx_buf));
}

FlorianR
Posts: 29
Joined: Fri Mar 12, 2021 2:00 pm

Re: UART break at first

Postby FlorianR » Thu Jul 09, 2026 8:41 am

I now added it to uart.h/.c itself. Also on git https://github.com/espressif/esp-idf/pull/10827

Hello I have a solid working function for that now:
uart.h:

Code: Select all

int uart_write_bytes_with_break(uart_port_t uart_num, const void* src, size_t size, int brk_len);

/**
 * @brief Send a break signal on the UART TX line without sending any data bytes.
 *
 * This function waits until the TX line is idle (all previously queued/buffered data
 * has been transmitted), then sends a break signal of the given duration.
 * It does not return until the break signal has been sent out.
 *
 * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1).
 * @param brk_len break signal duration(unit: the time it takes to send one bit at current baudrate)
 *
 * @return
 *     - (-1) Parameter error
 *     - (0) Break signal sent successfully
 */
int uart_write_break(uart_port_t uart_num, int brk_len);

uart.c:

Code: Select all

int uart_write_break(uart_port_t uart_num, int brk_len)
{
    ESP_RETURN_ON_FALSE((uart_num < UART_NUM_MAX), (-1), UART_TAG, "uart_num error");
    ESP_RETURN_ON_FALSE((p_uart_obj[uart_num]), (-1), UART_TAG, "uart driver error");
    ESP_RETURN_ON_FALSE((brk_len > 0 && brk_len < 256), (-1), UART_TAG, "break_num error");

    // The hardware break (txd_brk) is only appended after a data byte is transmitted.
    // To emit a standalone break without sending any data bytes, the idle-high TX line
    // is driven low (space level) for brk_len bit periods by inverting the TXD signal.
    uint32_t baud_rate = 0;
    if (uart_get_baudrate(uart_num, &baud_rate) != ESP_OK || baud_rate == 0) {
        return -1;
    }
    // Duration of the break in microseconds = brk_len bit periods at the current baudrate.
    uint32_t brk_us = (uint32_t)(((uint64_t)brk_len * 1000000ULL + baud_rate - 1) / baud_rate);

    //lock for uart_tx to serialize with other TX operations
    xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (TickType_t)portMAX_DELAY);
#if PROTECT_APB
    esp_pm_lock_acquire(p_uart_obj[uart_num]->pm_lock);
#endif
    //Wait until any previously queued/buffered data has been fully transmitted
    //so that the break signal is placed on an idle line.
    while (!uart_hal_is_tx_idle(&(uart_context[uart_num].hal))) {
        esp_rom_delay_us(1);
    }
    //Drive the idle-high TX line low to generate the break condition.
    UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
    uart_hal_inverse_signal(&(uart_context[uart_num].hal), UART_SIGNAL_TXD_INV);
    UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
    esp_rom_delay_us(brk_us);
    //Restore the TX line to its normal (non-inverted) idle-high level.
    UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
    uart_hal_inverse_signal(&(uart_context[uart_num].hal), 0);
    UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
#if PROTECT_APB
    esp_pm_lock_release(p_uart_obj[uart_num]->pm_lock);
#endif
    xSemaphoreGive(p_uart_obj[uart_num]->tx_mux);
    return 0;
}

Who is online

Users browsing this forum: ChatGPT-User and 3 guests