Flexcom USART in RS485 mode

Moderator: nferre

dthomas
Posts: 10
Joined: Mon Oct 21, 2019 4:27 pm

Flexcom USART in RS485 mode

Fri Nov 15, 2019 10:36 pm

I am encountering unexpected behavior when using the FLEXCOM device as a USART in RS485 mode. The expectation for RS485 mode is that the RTS line will be high during the transmission of bytes so it can be used as a driver enable. Meaning RTS should be the inverse of TXEMPTY in the following picture: Figure_46-6_TransmitterStatus

Instead, this is what I am seeing: Screenshot2

The picture shows:
  • Data (yellow) – 0xAA, 0x55
  • RTS (blue)
The documentation states: “The RTS pin is at a level inverse to the TXEMPTY bit.” It also states: “The TXEMPTY flag is cleared as long as there are characters in the Transmit FIFO or in the internal shift register. TXEMPTY is set when there are no characters in the Transmit FIFO and in the internal shift register.”

From examining the kernel source, I believe I am using the 32-byte transmit FIFO (is there a way to confirm that?). If that is the case, I would expect TXEMPTY to be cleared during the transmission of both bytes. Instead it is getting set after every byte (this has been confirmed with longer transmission strings). Why is that? Is there something that needs to be done to enable the desired behavior?

For reference, I’m using the following python script to send the bytes:

Code: Select all

import serial
import serial.rs485
import time


ser = serial.Serial('/dev/ttyS1', 19200)
ser.rs485_mode = serial.rs485.RS485Settings()
print(ser.name) # check which port was really used

ser.write(bytearray.fromhex("AA55"))

ser.close()
Is there a way to get the behavior to match expectations? I’m concerned this behavior will not work with all of our devices.

Finally, I am using the linux4sam_6.1 kernel.
dthomas
Posts: 10
Joined: Mon Oct 21, 2019 4:27 pm

Re: Flexcom USART in RS485 mode

Fri Nov 15, 2019 10:40 pm

My apologies, but attaching images does not seem to be working for me. Figure 46.6 is from the DS60001476C datasheet.
blue_z
Location: USA
Posts: 2005
Joined: Thu Apr 19, 2007 10:15 pm

Re: Flexcom USART in RS485 mode

Tue Nov 19, 2019 2:22 am

dthomas wrote: I am encountering unexpected behavior when using the FLEXCOM device as a USART in RS485 mode.
...
The documentation states: ...
Seems like the "documentation" you read was just selective parts of the SoC datasheet.
The salient document to study is Linux kernel Documentation/serial/serial-rs485.txt.
A 'delay_rts_after_send' capability is implemented in hardware by the Transmitter Timeguard of the Atmel USART.

dthomas wrote: From examining the kernel source, I believe I am using the 32-byte transmit FIFO (is there a way to confirm that?).
If you want to confirm what you see in the source code or what you "believe", then you can either dump the appropriate register(s) from the shell prompt (e.g. the devmem2.c program or Busybox's devmem) or insert appropriate printk() statements in the driver.
The kernel has some built-in debugging messages that can be configured and runtime enabled, e.g. dynamic debug.

dthomas wrote: Why is that? Is there something that needs to be done to enable the desired behavior?
You are not clear as to how you have configured the Flexcom and RS485 mode, and your "reference Python script" provides no information.
Are you using PIO or DMA?
Have you inspected the syslog for pertinent messages?
(FYI I don't use and know nothing about Python.)

dthomas wrote: Is there a way to get the behavior to match expectations?
The 'delay_rts_after_send' capability seems to be what you are looking for.

Regards
dthomas
Posts: 10
Joined: Mon Oct 21, 2019 4:27 pm

Re: Flexcom USART in RS485 mode

Tue Nov 19, 2019 6:05 pm

blue_z wrote: The salient document to study is Linux kernel Documentation/serial/serial-rs485.txt.
A 'delay_rts_after_send' capability is implemented in hardware by the Transmitter Timeguard of the Atmel USART.
Yes, I studied that document. The "ser.rs485_mode = serial.rs485.RS485Settings()" line from the Python script enables the SER_RS485_ENABLED flag. The defaults from rs485.RS485Settings are appropriate for SER_RS485_RTS_ON_SEND and SER_RS485_RTS_AFTER_SEND.

I can set a "delay_before_rx" value which serves as the "delay_rts_after_send" value. My observation is that this delays the dropping of the RTS line following the transmission of a byte (and the delay matches the time that I set), but that it does not stop the behavior. The RTS line still drops after every byte in the transmission rather than staying high until the entire string is sent.
blue_z wrote: you can either dump the appropriate register(s) from the shell prompt (e.g. the devmem2.c program or Busybox's devmem) or insert appropriate printk() statements in the driver.
The kernel has some built-in debugging messages that can be configured and runtime enabled, e.g. dynamic debug.
Thank you, I'll have to enable the kernel's built-in debugging messages. I looked at the FLEXCOM's USART registers with devmem2, but the FLEX_US_CR register used to enable the FIFO is write-only. I do not see a status register with read access to confirm the setting.
blue_z wrote:
Are you using PIO or DMA?
Could you speak more to this? I am unclear how PIO would effect this. I am not explicitly setting up DMA.
blue_z wrote:
Have you inspected the syslog for pertinent messages?
I can check that next.
dthomas
Posts: 10
Joined: Mon Oct 21, 2019 4:27 pm

Re: Flexcom USART in RS485 mode

Tue Nov 19, 2019 11:11 pm

I see the following in syslog:

Code: Select all

[    1.640000] atmel_usart_serial atmel_usart_serial.0.auto: using dma0chan5 for rx DMA transfers
[    1.640000] atmel_usart_serial atmel_usart_serial.0.auto: using dma0chan6 for tx DMA transfers
I do not see the equivalent for atmel_usart_serial.2, which is what I am using for my ttyS1 device. If this is sufficient indication that DMA is not enabled, then that might explain the behavior I am seeing. I'm using the following configuration in the DTS file:

Code: Select all

			flx4: flexcom@fc018000 {
				atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_USART>;
				status = "okay";

				uart6: serial@200 {
					compatible = "atmel,at91sam9260-usart";
					reg = <0x200 0x200>;
					interrupts = <23 IRQ_TYPE_LEVEL_HIGH 7>;
					clocks = <&flx4_clk>;
					clock-names = "usart";
					pinctrl-names = "default";
					pinctrl-0 = <&pinctrl_flx4_default>;
					atmel,fifo-size = <32>;
					status = "okay"; /* Conflict with spi3 and i2c3. */
				};
How do I enable DMA for this device or where can I find the documentation on how to do this? There is not an equivalent example in the DTS file that I have found.
blue_z
Location: USA
Posts: 2005
Joined: Thu Apr 19, 2007 10:15 pm

Re: Flexcom USART in RS485 mode

Fri Nov 22, 2019 1:40 am

dthomas wrote: From examining the kernel source, I believe I am using the 32-byte transmit FIFO (is there a way to confirm that?)
...
I see the following in syslog:
You overlooked the salient message:
atmel_usart_serial atmel_usart_serial.2.auto: Using FIFO (32 data)
atmel_usart_serial.2.auto: ttyS2 at MMIO 0xf8034200 (irq = 181, base_baud = 5187500) is a ATMEL_SERIAL

dthomas wrote: The "ser.rs485_mode = serial.rs485.RS485Settings()" line from the Python script enables the SER_RS485_ENABLED flag. The defaults from rs485.RS485Settings are appropriate for SER_RS485_RTS_ON_SEND and SER_RS485_RTS_AFTER_SEND.
That only the describes the method.
What are these "default" values, and why do you assume that they are "appropriate" while you are "encountering unexpected behavior"?

dthomas wrote: I can set a "delay_before_rx" value which serves as the "delay_rts_after_send" value.
That makes no sense at all.
What is this "delay_before_rx" value? That's not part of the Linux RS485 API.

But it really doesn't matter, as the "delay_rts_after_send" capability is flawed (as implemented by Atmel RS485 mode).
This shortcoming has been previously reported in this topic and the Linux serial mailing list.

The root cause of the unsustained RTS signal that you report is due to the combination of using programmed I/O (PIO) and the RS485 mode's substitution of TXEMPTY for TXRDY.
The ramifications are actually twofold.
First the existence of any FIFO and even the Transmit Holding register are negated.
Second the ISR latency will add a delay between transmitted characters to reduce throughput and glitch the RTS.

All characters can/should be written on a TXRDY condition (even in RS485 mode) rather than TXEMPTY.
The TXEMPTY interrupt is only needed for notification of the completion of the last character of the transmit chunk.
In its current state the atmel_serial driver has flawed PIO in RS485 mode, and therefore PIO should be avoided (in RS485 mode).

dthomas wrote: I see the following in syslog:

Code: Select all

[    1.640000] atmel_usart_serial atmel_usart_serial.0.auto: using dma0chan5 for rx DMA transfers
[    1.640000] atmel_usart_serial atmel_usart_serial.0.auto: using dma0chan6 for tx DMA transfers

...
There is not an equivalent example in the DTS file that I have found.
Since there is a USART on your board that uses DMA, then there is an "equivalent example" in your Device Tree.

Regards
dthomas
Posts: 10
Joined: Mon Oct 21, 2019 4:27 pm

Re: Flexcom USART in RS485 mode

Fri Nov 22, 2019 7:29 pm

blue_z wrote:
Fri Nov 22, 2019 1:40 am
All characters can/should be written on a TXRDY condition (even in RS485 mode) rather than TXEMPTY.
The TXEMPTY interrupt is only needed for notification of the completion of the last character of the transmit chunk.
In its current state the atmel_serial driver has flawed PIO in RS485 mode, and therefore PIO should be avoided (in RS485 mode).
Thank you, that is very interesting and helpful. I wonder why the driver persists with this flaw? Anyway, I created the following patch:

Code: Select all

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 032c9471e892..611458004d25 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -357,7 +357,7 @@ static int atmel_config_rs485(struct uart_port *port,
 
 	if (rs485conf->flags & SER_RS485_ENABLED) {
 		dev_dbg(port->dev, "Setting UART to RS485\n");
-		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+		atmel_port->tx_done_mask = ATMEL_US_TXRDY;
 		atmel_uart_writel(port, ATMEL_US_TTGR,
 				  rs485conf->delay_rts_after_send);
 		mode |= ATMEL_US_USMODE_RS485;
@@ -2578,7 +2578,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
 	 */
 	if (port->rs485.flags & SER_RS485_ENABLED ||
 	    port->iso7816.flags & SER_ISO7816_ENABLED)
-		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+		atmel_port->tx_done_mask = ATMEL_US_TXRDY;
 	else if (atmel_use_pdc_tx(port)) {
 		port->fifosize = PDC_BUFFER_SIZE;
 		atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
The behavior on the oscilloscope now matches the desired behavior and what I expected based on Figure_46-6_TransmitterStatus from the data sheet. I'll confirm once I can test with more hardware.
blue_z
Location: USA
Posts: 2005
Joined: Thu Apr 19, 2007 10:15 pm

Re: Flexcom USART in RS485 mode

Sat Nov 23, 2019 3:39 am

A two-line patch is not sufficient to rectify this RS485-mode PIO issue.
Looks like you're doing incomplete testing just like the original patch.

If you're interested in the origin of this RS485 patch:
https://lkml.org/lkml/2010/3/19/52
https://lkml.org/lkml/2010/3/29/51
And the patch that went into mainline:
https://git.kernel.org/pub/scm/linux/ke ... f444be29f5

Regards
dthomas
Posts: 10
Joined: Mon Oct 21, 2019 4:27 pm

Re: Flexcom USART in RS485 mode

Mon Dec 09, 2019 4:48 pm

blue_z wrote:
Sat Nov 23, 2019 3:39 am
A two-line patch is not sufficient to rectify this RS485-mode PIO issue.
Thank you very much for your help. Could you clarify what you mean by that? I apologize, but I do not understand what part of the issue remains a concern. I opened a case with Microchip to ask them if the patch required additional changes and to try to gain a better understanding of the issue. Today they replied with:
Please find below our internal team feedback on your request.
=======================
When FIFO is enabled:

The TXEMPTY flag is cleared as long as there are characters in the Transmit FIFO or in the internal shift register. TXEMPTY is set when there are no characters in the Transmit FIFO and in the internal shift register.

TXRDY indicates if a data can be written in the Transmit FIFO. Thus the TXRDY flag is set as long as the Transmit FIFO can accept new data.

So in this case, if we want TXEMPTY to stay 0 (RTS 1), data needs to be feed each time TXRDY is set and assure that the FIFO won't become empty. The fix should be to replace ATMEL_US_TXEMPTY with ATMEL_US_TXRDY in both atmel_init_port() and atmel_config_rs485() functions.
=======================
That answer does not completely resolve my concerns so I will continue to follow up with them. In the meantime, I have my hardware in a state where I can perform more testing (i.e. round-trip communication with a test harness versus monitoring traffic on an oscilloscope).

Return to “SAMA5-based”

Who is online

Users browsing this forum: No registered users and 3 guests