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
}