UART break at first
UART break at first
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
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
Re: UART break at first
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.
The working project is here for your inspiration:
https://github.com/pantaluna/esp32_lora ... ee.c#L1425
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.
Paul.
Re: UART break at first
thank you Paul that work very well.
Re: UART break at first
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?
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?
Re: UART break at first
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));
Re: UART break at first
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
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 (640.84 KiB) Viewed 15311 times
-
- bad break.jpg (1.35 MiB) Viewed 15311 times
Re: UART break at first
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.
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.
Re: UART break at first
The reason I say it is not working is that the own ESP32 Break event is not triggered on the UART RX side.
Re: UART break at first
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.
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
}
Re: UART break at first
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
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 423 guests