Open neorevx opened 5 years ago
Could it be because of the uart_set_line_inverse?
No. I thought about that possibility. Then I shorted RX with TX. In this case there is no collision. I think it's something about sampling. As the data is sent by the transceiver to the bus and is later read, there is a small time lag that the hardware is not accepting. I am using the MAX3485 as a transceiver in the "C" mode described in the documentation.
Well this seems wrong, mixing enable bits and status bits (I guess this doesn't matter if they are the same mask)
And only clearing one
This is not the best practice, but it should not be the problem. It's same mask. Otherwise, the interrupt would not be triggered. In my version I used the correct flag and got same problem.
About clear, it's missing! And it can be a big problem: I think interrupt will be triggered every time. However, it still would not solve the problem. I have verified that the clash is responsible for the error, not frame error or parity error.
I found another error in uart.c, but I'll open another Issue.
Hi neorevx, negativekelvin,
Thank you for reporting this issue. I agree that UART_RS485_FRM_ERR_INT_ST and UART_RS485_PARITY_ERR_INT_ST bits should be cleared in status register. Your issue reproduced on my side. I perform investigation of collision detection issue and will communicate with hardware team to clarify hw behavior of the feature. It will take some time to get information and test it. I will let you know as soon as receive any information. The driver will be corrected accordingly after this.
Hi, @neorevx This is a hardware issue, the clash interrupt will be triggered by mistake in half-duplex mode(rs485_conf.tx_rx_en=0). Can you accept the use of full-duplex((rs485_conf.tx_rx_en=1) mode to workaround this issue?
For my implementation there will be few problems. Nothing severe. I will have to process the sent message because the sending is received. Or discard my actively sent message. But in order to implement correctly, please provide me with some information:
By the way, the GLITCH interrupt does not work as START BIT. I imagine there should be some confusion in the documentation (reference manual). I get GLITCH when there is noise in the signal. Is that correct? Or could it be the same problem with the half-duplex?
Thank you.
Hi, @neorevx
When tx_rx_en
is set, the hardware can not detect the collision(hardware issue). If you are not using UART2, please try the following method(I guess you just want to discard the data you received when sending data.).
rxfifo_rst = 1
before you fill the txfifo.tx_done
interrupt after the data is filled in txfifo. rxfifo_rst=0
when the tx_done
interrupt arrives.thanks !!
Hi, @koobest, You told me that the interrupt does not work correctly in half-duplex (rs485_conf.tx_rx_en = 0). But now you told me that it also does not work in full-duplex (rs485_conf.tx_rx_en = 1).
When tx_rx_en is set
I think it meant "when tx_rx_en is not set". Or there is no way to detect collision via hardware. I will have to implement reading and comparing to the sent byte. In that case I would need a timeout if the collision generates an invalid frame and does not get back into the RX.
Look at this snippet of code. The comment says one thing and the code another. This is the code that initializes the UART to collision detection mode. https://github.com/espressif/esp-idf/blob/58df1d93bc17c74499d58e05390af9c309192a5c/components/driver/uart.c#L1503-L1504
I will try to use rxfifo_rst = 1
. However, I read somewhere that this flag can cause problems in other FIFOs. Do you think there will be problems?
So will there be normal collision detection?
I need to detect collision when transmitting the first byte of a message. It is a key point of the J1708 protocol. So here's the question: if there is a collision in the first byte and there are other bytes in the FIFO, Is the rest discarded or will it be sent after the collision?
Thank you!!!
Hi,@neorevx
In your hardware, the data you send will be read back by the hardware.
Currently, the collision detection mode works only in half-duplex mode (rs485_conf.tx_rx_en = 0), and the RX line needs to be held low while transmitting data. Otherwise, a collision interrupt will occur. The data you send will be read back by the hardware and the ESP32 thinks it is a conflict. I think this is not what you want. you can use full-duplex mode(rs485_conf.tx_rx_en = 1), determine whether conflicts occur by comparing the received and transmitted data, if they are the same, no conflicts occur, and if they are not the same, a conflict occurs.
thanks !!
Hi, @neorevx There are some new updates to this issue. A few days ago, I did this test in an incorrect way, so I got the wrong conclusion. I'm sorry for this. Now I can provide a test code to support this conclusion.
Due to hardware problems, the collision detection function of half-duplex mode (rs485_conf.tx_rx_en = 0) does not work (in fact, it can be used to detect collisions, but the RX idle level is required to be low when transmitting data).
The collision detection function of full-duplex mode (rs485_conf.tx_rx_en = 1) can work. If the data you send (on TX line) is different from the received data (on the RX line), a collision inerrupt will occur. Data will continue to be sent when a collision occurs.
You need two ESP32 board to do this test(you need to set rs485_conf.tx_rx_en=1).
You will see the collision detection works.
//Test code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/uart.h"
#include "freertos/queue.h"
#include "esp_log.h"
#define ECHO_TEST_TXD (18)
#define ECHO_TEST_RXD (19)
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS UART_PIN_NO_CHANGE
#define BUF_SIZE (1024)
#define BAUD_RATE (115200)
// Read packet timeout
#define PACKET_READ_TICS (100 / portTICK_RATE_MS)
#define ECHO_TASK_STACK_SIZE (2048)
#define ECHO_TASK_PRIO (10)
#define ECHO_UART_PORT (UART_NUM_1)
static const char *TAG = "RS485_ECHO_APP";
#include "soc/io_mux_reg.h"
#include "driver/gpio.h"
//make flash to board 1
#if 1
void test_task(void *param)
{
uint8_t* data = (uint8_t*) malloc(BUF_SIZE);
for(int i = 0; i < 1024; i++) {
data[i] = i + 1;
}
const int uart_num = ECHO_UART_PORT;
uart_config_t uart_config = {
.baud_rate = BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
.rx_flow_ctrl_thresh = 122,
};
// Configure UART parameters
uart_param_config(uart_num, &uart_config);
uart_set_pin(uart_num, 18, 19, -1, 13);
uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0);
while(1) {
vTaskDelay(2000/portTICK_PERIOD_MS);
uart_write_bytes(uart_num, (const char *)data, 1024);
uart_wait_tx_done(uart_num, 1000/portTICK_PERIOD_MS);
vTaskDelay(2000/portTICK_PERIOD_MS);
}
}
//make flash to board 0
#else
void test_task(void *param)
{
uint8_t* data = (uint8_t*) malloc(BUF_SIZE);
for(int i = 0; i < 1024; i++) {
data[i] = 0xff;
}
const int uart_num = ECHO_UART_PORT;
uart_config_t uart_config = {
.baud_rate = BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 122,
};
// Configure UART parameters
uart_param_config(uart_num, &uart_config);
uart_set_pin(uart_num, 18, 19, -1, -1);
uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0);
uart_set_mode(uart_num, UART_MODE_RS485_COLLISION_DETECT);
gpio_set_level(13, 1);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[13], PIN_FUNC_GPIO);
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);
gpio_set_direction(13, GPIO_MODE_OUTPUT);
while(1) {
printf("start\n");
vTaskDelay(2000/portTICK_PERIOD_MS);
gpio_set_level(13, 0);
ets_delay_us(1000)
uart_write_bytes(uart_num, (const char *)data, 1024);
vTaskDelay(2000/portTICK_PERIOD_MS);
gpio_set_level(13, 1);
}
}
#endif
void app_main()
{
test_task(NULL);
}
thanks!!
Hello, I had already tested the full-duplex collision and it worked (with interruption). However, I need to remove the sent message. In most cases it is only necessary to read all data from the RX-FIFO in the TX-DONE interrupt. But in some cases, TX-DONE is sent before the byte is in the RX-FIFO. In that case, I believe we can look at the RX finite state machine and check if the byte is being read when TX-DONE happens. And if there is a byte, we discard the next byte of the RX-FIFO at the next reading. There is also a collision before the TX-DONE. In my case, I need to record that there was a collision and discard all bytes when TX-DONE happens.
In half-duplex, I've inverted the TX with external hardware to see if it impacts the collision, but the answer is: No. But I got some conclusions about inverting the logic level of the pins. There is no collision when I invert the RX (logical only). Obviously, there is also no correct data, since the RX is inverted. I tried to invert the RX only when sending data, but it generated false collisions as well. In some cases a 0x00 byte is also read by the transmitter, indicating that the hardware could not remove the byte sent, probably because the data is inverted ou RX delay.
It is interesting to note that jump RX with TX does not cause collision! But only with TX or RX inverted. When both are inverted or not, there is a collision. Apparently the hardware expects the inverted bits but with no logical level inverted! I could use another GPIO and external hardware to invert the bits of the RX during the transmission, but it's a lot of effort. The inversion could only happen during transmission, while reception can not be inverted. Better to remove the Full-Duplex bytes. There is also no guarantee that the collision will be detected when there is. I still do not know exactly the behavior of the hardware.
I also tried other things without success.
About your example, I believe you miss something. You can not test by directly connecting the pins of two boards. It is necessary that you have a transceiver. The transceiver will always send the TX value to the RX, with some microseconds or nanoseconds of delay. The ESP32 hardware that removes TX from the RX. By connecting the pins directly, you will not have the loopback, so you will not be able to find collisions in the transmitter.
I'll test some things with RTS. Currently I control sw_rts during transmission. But in J1708 there is no RTS line, so I can not use it. I think there no where to go with RTS.
I tried putting the RX Pull-Down during transmission. But it did not help.
I will implement using full-duplex. If I have other news, I'll let you know.
The update for this issue to demonstrate described configuration of collision detection feature is added here .
Hi,
Could anyone check the above mentioned code with collision detection using HAL layer and provide some feedback for this issue?
Related issue: https://github.com/espressif/esp-idf/issues/7446
Hi,
I'm working with UART RS485 at low level and I received UART_RS485_CLASH_INT interruptions without actually there is data collision. Oscilloscope image attached: yellow is TX and blue is RX, connected direct on ESP32 pins.
My code was inspired by the source "uart.c". To prove that the system does not detect collision correctly, I did a little test as shown:
The result is basically:
In my version, when a put:
uart->rs485_conf.tx_rx_en = 1;
there's no collision, but I receive data from rx by tx.There any workaround for that? Maybe uart.c miss some configuration.