UART break at first

m3ayko83
Posts: 4
Joined: Sun Nov 25, 2018 1:43 pm

UART break at first

Postby m3ayko83 » Sun Nov 25, 2018 1:49 pm

Hallo ESP32 forum members

i want to send a break of minimum 13 bit time in the begining of the uart frame. I see there is already function uart_write_bytes_with_break to send break at the end of the frame(FIFO empty inteerupt is fired), but i need to send the break at first , can you provide me information if that possible? or some new version of uart.c

papaluna
Posts: 50
Joined: Tue Jan 30, 2018 11:27 am

Re: UART break at first

Postby papaluna » Sun Nov 25, 2018 3:58 pm

I have used the following approach to enforce a break of a certain length (time) for a Microchip RN2843 LoRa module using UART. The delay must be changed to fit your requirements.

Code: Select all

uart_set_line_inverse(param_ptr_config->uart_port_num, UART_INVERSE_TXD);
ets_delay_us(1250);
uart_set_line_inverse(param_ptr_config->uart_port_num, UART_INVERSE_DISABLE)


The working project is here for your inspiration:
https://github.com/pantaluna/esp32_lora ... ee.c#L1425
--
Paul.

m3ayko83
Posts: 4
Joined: Sun Nov 25, 2018 1:43 pm

Re: UART break at first

Postby m3ayko83 » Sun Nov 25, 2018 4:22 pm

thank you Paul that work very well.

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

Re: UART break at first

Postby phatpaul » Wed Jun 17, 2020 10:45 pm

This kind of sucks. Every LIN frame must start with a break.

I definitely don't want to introduce a ton of blocking waits for a LIN break.
At 19200 baud a 13 bit-time break is 677us. And I need to send lots of LIN frames while also serving HTTP server, BLE, WiFi, ...

Is it a hardware limitation that you can't send a break on it's own? Or just a limitation of the API?

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

Re: UART break at first

Postby phatpaul » Wed Aug 26, 2020 9:22 pm

I figured out a better solution:

Code: Select all

// 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)
#define LIN_BAUDRATE  (19200)
#define LIN_BREAK_BAUDRATE  ((LIN_BAUDRATE * 9)/13)

    uint8_t dummy = 0;
    uint8_t master_tx_buf[2];
    master_tx_buf[0] = 0x55; // sync byte
    master_tx_buf[1] = pid;
    
    uart_set_baudrate(lin_uart_num, LIN_BREAK_BAUDRATE);
    uart_write_bytes(lin_uart_num, (char *)&dummy, 1);  // send a zero byte
    uart_wait_tx_done(lin_uart_num, 2); // wait for 0 byte to finish before restore normal baudrate
    uart_set_baudrate(lin_uart_num, LIN_BAUDRATE); // set baudrate back to normal after break is sent

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

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

Re: UART break at first

Postby FlorianR » Fri Mar 12, 2021 2:10 pm

Hi There,
I got the same problem and I like the solution by phatpaul much more. Only issue is that it does not work for me...
I made two pictures one with a good break and one with the solution by phatpaul. be aware that I used accidentally 200us and 100us in the pictures. So the solution by phatpaul is optically more streched.
I played arround with the LIN_BREAK_BAUDRATE, but it did not help.
I assume that the long high level after the break is the problem. How do I get rid of it?

Best regards
Florian
Attachments
optimal break.jpg
optimal break.jpg (640.84 KiB) Viewed 15219 times
bad break.jpg
bad break.jpg (1.35 MiB) Viewed 15219 times

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

Re: UART break at first

Postby phatpaul » Sat Mar 13, 2021 1:19 am

Florian,
How did you determine that it doesn't work? Also, how do you know that it is the space after the break which is the problem?

Are you using it for LIN bus? My understanding is that LINbus spec allows for "inter-byte space", see Fig 2.3 in https://www.lin-cia.org/fileadmin/micro ... N_2.2A.pdf (But I can't find the max value in the spec, it only says it should be "non-negative".)

Or do you have an application where that timing is required? If so, then it may be difficult to achieve at this high level API. You would need to write your own low-level driver using interrupts.

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

Re: UART break at first

Postby FlorianR » Sun Mar 14, 2021 4:10 pm

The reason I say it is not working is that the own ESP32 Break event is not triggered on the UART RX side.

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

Re: UART break at first

Postby phatpaul » Tue Mar 16, 2021 1:48 am

Are you saying that you are expecting the break detect to trigger on the UART RX side in the same ESP32 chip which is sending the break using my baud-rate hack method?

It will not trigger because when you lower the baud-rate to TX the 0x00 (fake break), it changes the baud for the RX side at the same time. So the RX side sees a 0x00 as a valid frame, since it is at the same baud as master.

I also wanted the RX to see the break, so I had to force the RX driver to see the break by inserting the event into the lin_uart_event_queue. See my complete driver code below. Hope it helps.

Code: Select all

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#include "main.h"
#include "io.h" // for pin-out definitions
#include "LIN_driver.h"

#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "esp_log.h"

#include "esp_err.h"
#include "driver/uart.h"

/* LIN Packet Format:
    _________ __________ _________ ____________ __________ 
   |         |          |         |           |||         |
   |  Break  |Sync 0x55 |PID byte |Data Bytes |||Checksum |
   |_________|__________|_________|___________|||_________|
   
   Every byte has start bit and stop bit and it is send LSB first.
   Break - 13 bits of dominant state ("0"), followed by 1 bit recesive state ("1")
   Sync Byte - Byte for Bound rate syncronization, always 0x55
   ID Byte - consist of parity, length and address; parity is determined by LIN standard and depends from address and message length
   Data Bytes - user defined; depend on devices on LIN bus
   Checksum - inverted 256 checksum; data bytes are summed up and then inverted
*/

static const char *TAG = "LIN_driver";

#define LIN_FRAME_HEADER_SIZE (3) // break + 55 + PID

#define LIN_FRAME_LENTH (LIN_FRAME_HEADER_SIZE + LIN_MESSAGE_DATA_SIZE + 1) // +chksum
#define LIN_OUT_BUF_SIZE (LIN_MESSAGE_DATA_SIZE + 1)

#define LIN_IN_HEADER_SIZE (LIN_FRAME_HEADER_SIZE)
#define LIN_IN_RESPONSE_SIZE (LIN_MESSAGE_DATA_SIZE + 1) // +chksum
#define LIN_IN_BUF_SIZE (LIN_IN_HEADER_SIZE + LIN_IN_RESPONSE_SIZE)

// Setup UART buffered IO with event queue
static QueueHandle_t lin_uart_event_queue;

static const int lin_uart_num = UART_NUM_2;

#if defined APP_CONFIG_USE_LIN_MASTER && APP_CONFIG_USE_LIN_MASTER
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_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));
}
#endif

// returns length read
int LIN_driver_rx_header(uint8_t *pid, TickType_t ticksToWait)
{
    uart_event_t event;
    while (xQueueReceive(lin_uart_event_queue, (void *)&event, ticksToWait))
    {
        if (event.type == UART_BREAK)
        {
            //Event of UART RX break detected
            // run slave task once per break detected
            break;
        }
    }

    if (event.type == UART_BREAK)
    {
        // Is there a LIN frame header received?
        uint8_t slave_hdr_buf[4]; // we might recv some junk before the break (0) and sync (55)
        int rx_got_len = 0;
        int tries = 0; // prevent lockup
        while (tries++ < 4 && rx_got_len < sizeof(slave_hdr_buf))
        {
            rx_got_len += uart_read_bytes(lin_uart_num, (uint8_t *)&slave_hdr_buf[rx_got_len], 1, ticksToWait);
            if (rx_got_len >= 2) // need at least 2 bytes
            {
                ESP_LOG_BUFFER_HEX_LEVEL("LIN HDR", slave_hdr_buf, rx_got_len, ESP_LOG_VERBOSE);
                if (rx_got_len == 2 && slave_hdr_buf[0] == 0x55) //check break (0) and sync (55) are in the correct spot
                {
                    *pid = slave_hdr_buf[1]; // PID is last byte of header
                    return 1;                // non-zero means we rec a header with PID
                }
                if (rx_got_len == 3 && slave_hdr_buf[1] == 0x55) //check break (0) and sync (55) are in the correct spot
                {
                    *pid = slave_hdr_buf[2]; // PID is last byte of header
                    return 1;                // non-zero means we rec a header with PID
                }
                if (rx_got_len == 4 && slave_hdr_buf[2] == 0x55) //check break (0) and sync (55) are in the correct spot
                {
                    *pid = slave_hdr_buf[3]; // PID is last byte of header
                    return 1;                // non-zero means we rec a header with PID
                }
            }
        }
    }

    return 0; // 0 means we didn't get a valid header (i.e. timeout)
}

// returns length read
int LIN_driver_rx_data(uint8_t *rx_data, int rx_len, TickType_t ticksToWait)
{
    return uart_read_bytes(lin_uart_num, rx_data, rx_len, ticksToWait);
}

void LIN_driver_tx_data(uint8_t *tx_data, int tx_len)
{
    uart_write_bytes(lin_uart_num, (char *)tx_data, tx_len);
}

void LIN_driver_delete(void)
{
    ESP_LOGI(TAG, "Delete");
    uart_driver_delete(lin_uart_num);
}

/*
 *
 */
esp_err_t LIN_driver_init(int baud)
{
#if (defined APP_CONFIG_USE_LIN_SLAVE && APP_CONFIG_USE_LIN_SLAVE) || (defined APP_CONFIG_USE_LIN_MASTER && APP_CONFIG_USE_LIN_MASTER)
    esp_err_t err;
    uart_config_t lin_uart_config = {
        .baud_rate = baud,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .use_ref_tick = true // ref-tick doesn't change freq if/when CPU changes freq.
    };
    ESP_LOGI(TAG, "Init");
    if (baud < LIN_MIN_BAUDRATE || baud > LIN_MAX_BAUDRATE)
    {
        err = ESP_ERR_INVALID_ARG;
        goto err_out;
    }
    // Configure UART parameters
    err = uart_param_config(lin_uart_num, &lin_uart_config);
    if (err != ESP_OK)
    {
        goto err_out;
    }

    // Install UART driver using an event queue // don't use TX buffer, so all tx calls are blocking
    err = uart_driver_install(lin_uart_num, UART_FIFO_LEN * 2, 0, 10, &lin_uart_event_queue, 0);
    if (err != ESP_OK)
    {
        goto err_out;
    }

    // Set UART pins(TX, RX, RTS, CTS).  Do it after uart_param_config.
    err = uart_set_pin(lin_uart_num, IOPIN_LIN_TX, IOPIN_LIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    if (err != ESP_OK)
    {
        goto err_out;
    }

    return err;

err_out:
    ESP_ERROR_CHECK_WITHOUT_ABORT(err);
    LIN_driver_delete();
    return err;
#endif
}



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

Re: UART break at first

Postby FlorianR » Tue Mar 16, 2021 6:49 am

Thank you very much!
I sincerly hope for others that this small LIN feature will be implemented at some point. These workarounds are quite complicated for such a thing :) Furthermore when a send break at the end is already existing.

I am glad I asked for help. I would not have come up with that myself.

Best regards from Germany!
Florian

Who is online

Users browsing this forum: No registered users and 149 guests