UART Hardware Handshake

From Nutwiki
Revision as of 17:03, 27 October 2016 by Harald (Talk | contribs) (1 revision imported)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Description

This example demonstrates the use of the RTS/CTS hardware handshake. Since hardware handshakes are disabled by default in the configurator, you have to first adjust the configuration file and rebuild Nut/OS before this example will work correctly. You'll also have to adjust a jumper on the Ethernut 2 board.

Please note: Advanced drivers may allow this example to work on other boards as well, since those are not finished yet this example will only work on Ethernut 2 at the time of writing.

RTS/CTS Handshake

This pair is typically used by a device to signal the other end, that it is ready to accept more data. The Nut/OS driver will pull the RTS line high, when enough buffer space is available. Remember, that the USART drivers are fully interrupt controlled. Incoming data will be stored in a circular buffer. When the application calls a reading function (like fgets, read, getchar etc.), data will be moved from the circular buffer to the application buffer. If the application is too slow, the driver may run out of buffer space. If RTS/CTS handshake had been enabled, the driver will pull the RTS line low to stop the other end from sending more data.

Of course, both ends must have RTS/CTS handshake enabled. The transmitter in the Nut/OS driver will monitor its CTS line and stop sending out more characters if it's pulled low. Like on the receiver part, outgoing data from the application is first moved to a circular buffer, from which the characters are transmitted by an interrupt routine. If transmission has been stopped by CTS going low, the application may still write more data to the driver until the transmit buffer is exhausted. In this case the application will be blocked. As soon as CTS returns to high again, transmission starts again and the application will be woken up by the kernel.

Please note, that the procedure explained above refers to the full duplex mode, where transmitters and receivers are running concurrently. In half duplex mode, which allows one data transfer direction at a time only, the meaning of RTS and CTS signals are slightly different. See RS485_Communication.

Other Handshakes

The reference designs for Ethernut 3 and Ethernut 5 offer full modem handshake. Beside RTS/CTS the following signals are additionally available:

DTR Signals the other side, that Ethernut is ready for communication.
DSR Pulled high by the other side, if it is ready for communication.
DCD Pulled high by the other side, typically a modem, to signal that a carrier signal is available.
RI Pulled high by the other side, typically a modem, when an incoming call has been detected.

Currently the Nut/OS driver will ignore these signals. They may be directly handled by the application.

Requirements

You'll need some additional steps to make this example work. This has nothing to do with the example itself but with the RTS/CTS parts of Ethernut being deactivated by default. To get it working do this:

1. Adjust the settings in the configurator

Make sure you find and adjust the board specific settings to allow your board to use hardware flow control.

2. Rebuild Nut/OS

3. Adjust jumpers on the board

You will have to enable the hardware flow control on the board itself, too.

Ethernut 1

To enable RTS/CTS handshake on this board, some solder work is required. Add R32 and R33, both are 0Ω, 1206. You will find the soldering pads on the back side of the board.

After the resistors have been mounted, RTS is controlled by bit 3 of PORTD and CTS can be monitored via bit 2 of the same port.

Ethernut 2

The required jumper configuration as seen in the Ethernut 2 user's manual on page 18:

File:Enut2 rtscts jumpers.jpg

Use the following settings in the Configurator:

File:Enut2 rtscts settings.jpg

Ethernut 3

Up to Nut/OS 4.9 the hardware handshake support had a flaw in the UART driver causing hardware handshake to not work anymore. In order to fix this, please get at least Nut/OS 4.9 and adjust the file arch/arm/dev/usartat91.c in the following way:

Find the lines

<source lang="c">

  1. define UART_RTS_ON() cbi(NPL_RSCR, NPL_RSRTS_BIT)
  2. define UART_RTS_OFF() sbi(NPL_RSCR, NPL_RSRTS_BIT)

</source>

and replace them with

<source lang="c">

  1. define UART_RTS_ON() sbi(NPL_RSCR, NPL_RSRTS_BIT)
  2. define UART_RTS_OFF() cbi(NPL_RSCR, NPL_RSRTS_BIT)

</source>

Don't forget to rebuild Nut/OS afterwards!

The Other Side

In order to test RTS/CTS, you will of course have to enable it on the other side as well. For our example we will use TeraTerm. Its configuration should look like this:

File:Enut2 rtscts terminal.jpg

There is one potential pitfall with TeraTerm (and possibly with other terminal emulators as well): TeraTermn enables ALL hardware handshakes if you choose to use the hardware handshake option. This means it doesn't only use RTS/CTS but expects a full modem handshake instead. To make hardware handshakes work with TeraTerm, you have to enable DCD, DSR and DTR yourself.

To build yourself an adapter for the Ethernut 3 board to do this, you need to follow this wiring:

File:RS232C5HSTN.png

Source Code

<source lang="c">

  1. include <dev/board.h>
  2. include <sys/timer.h>
  1. include <io.h>
  2. include <stdio.h>
  3. include <string.h>
  1. define BUFFSIZE 10

char buffer[BUFFSIZE];

int main(void) {

   u_long baud = 115200;
   u_long flowcontrol = UART_HS_RTSCTS;
   FILE *fp;
   int i = 0;
   int bytes_read = 0;
   NutRegisterDevice(&DEV_UART, 0, 0);
   fp = fopen(DEV_UART_NAME, "r+b");
   _ioctl(_fileno(fp), UART_SETSPEED, &baud);
   _ioctl(_fileno(fp), UART_SETFLOWCONTROL, &flowcontrol);
   for (;;) {
       bytes_read = fread(buffer, 1, BUFFSIZE, fp);
       fwrite(buffer, 1, bytes_read, fp);
       fflush(fp);
       NutSleep(1000);
   }
   return 0;

} </source>

Details

<source lang="c"> u_long baud = 115200; u_long flowcontrol = UART_HS_RTSCTS; FILE *fp; int i = 0; int bytes_read = 0; </source>

The important part here is the declaration of "flowcontrol". The variable contents, in this case UART_HS_RTSCTS will be passed to an ioctl call and make sure the UART uses RTS/CTS. The other variables are pretty self-explanatory as they don't differ from other examples.

<source lang="c"> NutRegisterDevice(&DEV_UART, 0, 0); fp = fopen(DEV_UART_NAME, "r+b"); </source>

Next we register the UART device. Note that this time it is not the debug device but the real device driver. This makes a huge difference in how Nut/OS receives the data. We also create a file pointer for reading so we can read data that comes in later on.

<source lang="c"> _ioctl(_fileno(fp), UART_SETSPEED, &baud); _ioctl(_fileno(fp), UART_SETFLOWCONTROL, &flowcontrol); </source>

Setting the baud rate looks pretty standard here, except it uses fp instead of stdout. Besides that it is basically the same.

After that we issue another ioctl call, this time setting the flowcontrol to RTS/CTS. It is set to none by default so this step is required, even after all the setting up we did before.

In a real application you would also check the result for success, this time it is omitted as we assume it just works. It's a simple example so it should, unless you did something wrong with the configuration.

<source lang="c"> for (;;) {

   bytes_read = fread(buffer, 1, BUFFSIZE, fp);
   fwrite(buffer, 1, bytes_read, fp);
   fflush(fp);
   NutSleep(1000);

} </source>

Now this reads data from the UART buffer until our application internal buffer is full. It then writes the received characters back to the UART (and thus your terminal emulator) and flushes the buffer to make sure nothing is still left to be sent. The call to NutSleep lets the program wait for a moment, allowing the buffer to fill up again and - since this is what we want to demonstrate - fill the UART buffer to its limit.

Running the example

To test this example you should now open your terminal program, connect to the serial interface (make sure hardware flow control is used) and then send a huge file to the board. Any file with a size of 1MB or more will do.

TeraTerm provides convenient live statistics about how far the transfer has progressed. You will also notice some slight pauses within the transfer. These pauses are the RTS/CTS signals that temporarily hold the transfer when the Ethernut 2 board reports that its buffer is full.

See also