Connect-Me + RS485

Hi all.

I’m in the process of developping a Modbus master based on the Connect-ME and LxNETES. All RS485 ic’s need the transmit enable signal to be managed by the processor and this task is usually done through the UART RTS line as this has to be done as close to the hardware as possible. In fact, toggling RTS from the user application adds too big delays to the equation. The problem is that RTS is correctly switched on by the UART when a message is written but it does not recovers the low state after the send is complete.

I’ve read in the uClinux mailing list that a hack should be done in serial.c (serial_netarm_dna.c for the Connect-ME I guess) in order for the RTS line to recover the low state after the UART has sent the last bit.

Has anyone done a similar hack for any processor running LxNETES? Is there a RS485 driver for LxNETES? Any other suggestion?

Thank you very much,

Daniel Berenguer
http://www.opnode.org

Hi Daniel,

Did you manage to solve the problem?

I am trying to do the same as you with ConnectCore7U and NET+OS 7.3.

This is my code to try to transmit using a RS485 transceiver:
portb = open( “/com/0”, O_RDWR );
result = tcgetattr( portb, &portbCfg );
portbCfg.c_iflag = 0;
//portbCfg.c_cflag |= CRTS;
//portbCfg.c_cflag |= CRTSTOGGLE;
result = cfsetospeed( &portbCfg, 2400 );
result = tcsetattr( portb, TCSANOW, &portbCfg );

result = write( portb, message, sizeof( message ) - 1 );

I have configured the serial port as 4 wire UART. I have also tried setting the c_cflag to CRTS or CRTSTOGGLE, but it did not work, RTS line does not toggle.

I don’t know if the serial port driver is the same in NET+OS as in LxNETES, but any help is very welcomed.

Best regards,
Jose

Hi Jose. Sorry for the long delay in my response.

No, the NS7520 is not prepared to manage RS485 communications through the RTS line, mainly because the RTSTX bit has no effect on this processor. I’m waiting for the Connect-ME 9210, based on the NS9210, that is supposed to support this feature via RTSEN=1

Daniel.

For NET+OS (sorry, I don’t know Linux): CTRSTOGGLE is the correct way to get RTS toggle working with the serial driver. Make sure in gpio.h that the serial port is setup for at least 4 wire. Also, you need to make sure to fully setup the termios structure (setup all the flags, not just the cflag).

On a side note HW RTS toggle is not working properly in the NS7520 chip, so NET+OS only supports a software RTS toggle (in the serial driver) on NS7520 based modules (CC7U, ME, etc).

Finally, make sure you have the latest patches installed.

I finally did my tests on the new Connect-ME 9210 platform and… RTS does not work in “RS485 mode” by default and I can’t find either an option in the kernel for doing so.

Following the NS9210 hardware manual seems that making RTSEN = 1 in the Wrapper Configuration Register converts RTS into a RS485 transmit enable line. The problem is that I don’t know where to do this.

Any suggestion is welcome.

Thanks again,

Daniel.

The linux serial driver for the NS921x processors doesnt implement any specific functionality to enable the RS485 mode.

In linux, the usual way for controling the direction of the RS485 transceiver is by connecting that line to one of the flow control lines of the uart (RTS, for instance).

The driver allows to control the value of that line by using the standard ioctl commands TIOCMGET/TIOCMSET over the file descriptor returned by the open system call, for the desired serial port.

Additional information about this can be found in any guide about programming serial ports on unix systems.

A good reference…

http://www.easysw.com/~mike/serial/serial.html

Again for NET_O/S (7.4) I’ve very successfully used the hardware toggle on RTS, setting the CRTSTOGGLE bit. I’ve also found that its sufficient to define a 2-wire UART (maybe we should have a 3-wire UART definition - I’m sure RxD/TxD/RTS is a popular configuration).

I modified camry_serl.h line 20 to give a little setup and hold on RTS relative to the data:

#define RTSEN ((1 << 19) | 0xf)

Above gives the maximum setup and hold times.

Do you mean that you manually control RTS via CRTSTOGGLE on each transmission?

This doesn’t work for Linux as Linux is not a real-time OS. I tried that before on the old Connect-ME and didn’t work because releasing RTS after the transmission takes too much time.

I think the solution is still in setting the RTSEN bit.

I tried doing
((volatile unsigned int) 0x90011000U) |= 0x80000U;

from my application but I get a segmentation fault…

Thanks again,

Daniel.

No, CRTSTOGGLE is essentially a ‘mode’ flag for the serial port. Under NET+O/S on the ME9210 it uses the RTSEN value (IIRC) to update a UART control register. I simply modified the value so that RTS went active two bit times before data transmission started, and was held for two bit times after data transmission ended. Works well.

You can not access directly physical memory inside linux (while that can be done in uClinux).
Memory has to be accessed through its virtual memory.

That can be done because there is a static mapping and therefore you already know the virtual addres or because you have remapped that physical memory into a virtual one, using ioremap kernel function.

Thanks Steve.

Unfortunately, Linux does not support the CRTSTOGGLE flag… but at least I know that RTSEN works on the ME9210.

Thanks again,

Daniel.

I’ve found that setting the serial flag CRTSCTS disables the serial port!!

CTRCTS should just enable the RTS line. This makes me thing that the serial flow control must be enabled anywhere in the kernel before playing with bits and flags.

Any idea?

Daniel.

Does the NS9210 have a MMU? I always thought that Digi Embedded Linux was based on uClinux 2.6… Thanks for the information.

Which is then the way for changing UART system registers?

Besides the RS485 issue, I’m having difficulties to make the RTS line work on Serial Port A. RTS/CTS/RX/TX lines are enabled in the kernel but RTS never changes with serial transmissions. As I said in a precedent post, RTS should work after setting the CRSCTS flag but this is not the case either. Same result running this command:

stty -F /dev/ttyS0 crtscts

Has anyone been able to use the RTS line for serial transmissions on the Dev board?

Thank you very much guys for your help!!

Daniel.

The NS9210 integrates an ARM9 core (ARM926EJS) with MMU.
uClinux was supported on the NS7520. On the CME9210 we integrate full linux.

A good reference about how to access the register of the UART is having a look into the serial driver.
It is located under (assuming that you installed the distribution at the default installation path):
/usr/local/DigiEL-X.Y/kernel/linux/drivers/serial/ns921x-serial.c

If you just want to toggle the RTS line, I would recommend you to do that from a user space application with the ioctl call and the commands TIOCMGET/TIOCMSET.

As I stated in a previous post, a good reference on serial port programming would be:

http://www.easysw.com/~mike/serial/serial.html

Hi Pedro,

I’ve already tried the TIOCMGET/TIOCMSET way but RTS doesn’t change. I’m maybe mixing two problems here… I should make the RTS line work before working on the RS485 stuff.

I’ve set “full control” for the Serial port A in the kernel config but this makes no difference. In a standard Linux distribution I should also set the CRTSCTS as attribute for the serial port but this is disabling the serial communications on the dev board…

This is the result of running “stty -a -F /dev/ttyS0” just after booting Linux:

speed 38400 baud; rows 24; columns 80;
intr = ; quit = ; erase = ; kill = ; eof = ;
eol = ; eol2 = ; start = ; stop = ; susp = ;
rprnt = ; werase = ; lnext = ; flush = ;
min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb -cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon
-ixoff -iuclc -ixany -imaxbel
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0
ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop
-echoprt -echoctl -echoke

Here CRTSCTS seems to be enabled

Then, looking at the specific registers I have:
(after doing)
ioctl(fd, TIOCMGET, &status);
status |= TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);

UART A Modem Control Register = 0x00000003

Bit 1 = 1 -> RTS = 0

Can anyone confirm that RTS can be controlled by the Linux serial driver? If it can, then I’m missing some step in the kernel config.

Thanks again,

Daniel.

Being unable to control the RTS line from the serial driver, I’ve tried controlling RTS as GPIO again (after disabling the flow control from the kernel, of course). The result is that RTS switches correctly but it recovers the low state too late after having sent the serial data. Thus, the Modbus slaves try responding the master whilst the transmission lines are not high impedance yet and their response is lost.

Message was edited by: estratos

I’ve done some progresses here.

Controlling the RTS line with ioctl DOES work. The problem was that I was expecting to switch RTS high when setting TIOCM_RTS, and it’s just the opposite. On the other hand, I can manually control RTS using “memwatch”.

memwatch -x -a 0x90011110 -d 0x00

Sets the UART Modem Control Register RTS bit to 0

memwatch -x -a 0x90011110 -d 0x02

Sets the UART Modem Control Register RTS bit to 1

Now I can freely change the RTS line through the serial driver (ns921x-serial.c), from my application.

However, this doesn’t solve the main problem, as RTS needs to be directly controlled by the serial driver (due to the timing issues). Then I realized that the UART Modem Control Register has a bit (bit 5, Automatic Flow Control) used as follows:

0 RTS controlled by bit 1 (RTS)
1 RTS controlled by 4-byte RX FIFO status

This bit is defined in nx921x-serial.c as UART_MCR_AFE but is nowhere managed by the serial driver. Thus, I did a small modification in the ns921x_uart_set_mctrl function:

static void ns921x_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
//u32 mcr = 0;
u32 mcr = UART_MCR_AFE; // <– MODIFIED

/* RTS signal should be controlled as gpio to allow using AFE */
if (mctrl & TIOCM_RTS)
mcr |= UART_MCR_RTS;

if (mctrl & TIOCM_DTR)
mcr |= UART_MCR_DTR;

if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;

uartwrite32(port, mcr, UART_MCR);
}

The result is not good… This disables the serial communications in the same way than setting CRTSCTS from user space…

For most UART applications, RTS is not used under these critical conditions. Nevertheless, RS485 needs RTS to be high only during transmissions. Longer delays always prevent the RS485 slaves from responding as the data liens are not high impedance. Thus, RS485 needs the serial driver to do “Automatic Flow Control” (MCR bit 5 = 1) and “RS485 Transceiver control” (WCR bit 19 = 1).

Thanks for your time,

Daniel.

One little point. On most chips I’ve encountered, RTS is active low, inactive high (at the UART pin, that is - before putting it through an appropriate buffer). This is certainly the case on the ME9210.

Could this be confusing things for you?

(Just a reminder - #define RTSEN ((1 << 19) | 0xf) works well on Net+O/S)

Yes, that was confusing me. Now I can control RTS manually without problems but this is useless for my application.

Pedro from Digi said in a precedent post that I had to do this “manually” (with the ioctl method). What about the “AFE + RTSEN features”?. NetOS users are taking advantage of these nice features provided by the NS9210. Is there any reason for not doing so from Linux?

Thanks again,

Daniel.

I have some new information coming from the Support Team regarding this issue.

It seems that the Hardware Automatic Flow Control initially provided by the NS9210 processor is not exploited under Linux or NetOS, as this conflicts with an inherent limitation of the CTS line…


Netos implemented FIX :

Description: On NS9210 and NS9215, RTS in modem_ctrl register can’t be controlled by software while CTS is used for HW flow control. To be able to use CTS to halt the transmitter, the HW requires the AFE bit in modem_ctrl register to be 1, but this requirement prohibits us writing to RTS directly.

The work around of this problem is to use RTS pin as GPIO output 0/1 to control RTS.

Refer to NS9215/NS9210 ASIC Errata on RTS.

As result, RTS has to be controlled by software from both Linux and NetOS. NetOS users don’t see this as they have maybe automatic routines that simplify this. On the other hand, Linux users can’t do this workaround from our applications due to the inherent delays added in user space. As result, we have to implement the automatic RTS control into the serial driver and here a new challenge is introduced.

Following Digi’s suggestions, I’m now trying to control RTS in the same way than CTS from ns921x-serial.c. In ns921x_uart_check_msr we have:

cts_gpio = unp->data->gpios[2];
status = gpio_get_value(cts_gpio);

So I’m trying to do this:

rts_gpio = unp->data->gpios[5];
gpio_set_value(rts_gpio, value);

But gpio_set_value does not switch gpio[5]. I’m maybe not using gpio_set_value correctly (this function is used nowhere in ns921x-serial.c). On the other hand, I’m not sure that RTS is really gpio[5].

I’m going to continue with my researches. Meanwhile, I accept any kind of advice. I’m sure this is going to be a hard task as RS485 needs to be very precise with timings but this is the only way to take at this moment. If we don’t get to implement an automatic flow control into the serial driver, then NS9210 + Linux will become a useless solution for RS485 applications (Modbus, Profibus and many more). I’ve myself developed a Modbus master using the CME9210 and this is the last step before releasing the product.

Thanks guys for following this thread.

Daniel.

> It seems that the Hardware Automatic Flow Control
> initially provided by the NS9210 processor is not
> exploited under Linux or NetOS, as this conflicts
> with an inherent limitation of the CTS line…

I wonder whether the support team have misunderstood the application? Or are you wishing to use CTS for flow control as well?

I have an ME9210 under Net+O/S which is successfully using auto-RTS enable, with the potential CTS line used for other purposes.
I set things up as follows:
gpio.h - configured as a 2-wire UART
serial port setup - CRTSTOGGLE mode

and this works fine. The adjusted value of RTSEN simply gives some setup and hold time on RTS.