ECOM Comm
Warning
This chapter describes the Foundation Library ECOM-COMM-1.1
.
ECOM-COMM-1.1
is deprecated in favor of ECOM-COMM-2.0
and has been removed from Architecture 8.0.0.
See Migrate ECOM-COMM Module for more details.
Principle
The ECOM Comm Java library provides support for serial communication. ECOM Comm extends ECOM to allow stream communication via serial communication ports (typically UARTs). In the MicroEJ Application, the connection is established using the Connector.open() method. The returned connection is a ej.ecom.io.CommConnection, and the input and output streams can be used for full duplex communication.
The use of ECOM Comm in a custom platform requires the implementation of an UART driver. There are two different modes of communication:
In Buffered mode, ECOM Comm manages software FIFO buffers for transmission and reception of data. The driver copies data between the buffers and the UART device.
In Custom mode, the buffering of characters is not managed by ECOM Comm. The driver has to manage its own buffers to make sure no data is lost in serial communications because of buffer overruns.
This ECOM Comm implementation also allows dynamic add or remove of a connection to the pool of available connections (typically hot-plug of a USB Comm port).
Functional Description
The ECOM Comm process respects the ECOM process. Please refer to the illustration “ECOM flow”.
Component Architecture
The ECOM Comm C module relies on a native driver to perform actual communication on the serial ports. Each port can be bound to a different driver implementation, but most of the time, it is possible to use the same implementation (i.e. same code) for multiple ports. Exceptions are the use of different hardware UART types, or the need for different behaviors.
Five C header files are provided:
LLCOMM_impl.h
Defines the set of functions that the driver must implement for the global ECOM comm stack, such as synchronization of accesses to the connections pool.
LLCOMM_BUFFERED_CONNECTION_impl.h
Defines the set of functions that the driver must implement to provide a Buffered connection
LLCOMM_BUFFERED_CONNECTION.h
Defines the set of functions provided by ECOM Comm that can be called by the driver (or other C code) when using a Buffered connection
LLCOMM_CUSTOM_CONNECTION_impl.h
Defines the set of functions that the driver must implement to provide a Custom connection
LLCOMM_CUSTOM_CONNECTION.h
Defines the set of functions provided by ECOM Comm that can be called by the driver (or other C code) when using a Custom connection
The ECOM Comm drivers are implemented using standard LLAPI features. The diagram below shows an example of the objects (both Java and C) that exist to support a Buffered connection.
The connection is implemented with three objects [1] :
The Java object used by the application; an instance of ej.ecom.io.CommConnection
The connection object within the ECOM Comm C module
The connection object within the driver
Each driver implementation provides one or more connections. Each connection typically corresponds to a physical UART.
Comm Port Identifier
Each serial port available for use in ECOM Comm can be identified in three ways:
An application port number. This identifier is specific to the application, and should be used to identify the data stream that the port will carry (for example, “debug traces” or “GPS data”).
A platform port number. This is specific to the platform, and may directly identify a hardware device [2].
A platform port name. This is mostly used for dynamic connections or on platforms having a file-system based device mapping.
When the Comm Port is identified by a number, its string identifier is the concatenation of “com” and the number (e.g. com11).
Some drivers may reuse the same UART device for different ECOM ports with a hardware multiplexer. Drivers can even treat the platform port number as a logical id and map the ids to various I/O channels.
Application Port Mapping
The mapping from application port numbers to platform ports is done in the application launch configuration. This way, the application can refer only to the application port number, and the data stream can be directed to the matching I/O port on different versions of the hardware.
Ultimately, the application port number is only visible to the application. The platform identifier will be sent to the driver.
Opening Sequence
The following flow chart explains Comm Port opening sequence according to the given Comm Port identifier.
Dynamic Connections
The ECOM Comm stack allows to dynamically add and remove connections from the Driver API. When a connection is added, it can be immediately open by the application. When a connection is removed, the connection cannot be open anymore and java.io.IOException is thrown in threads that are using it.
In addition, a dynamic connection can be registered and unregistered in ECOM device manager (see Device Management API). The registration mechanism is done in dedicated thread. It can be enabled or disabled, see Standalone Application Options.
A removed connection is alive until it is closed by the application and, if enabled, unregistered from ECOM device manager. A connection is effectively uninstalled (and thus eligible to be reused) only when it is released by the stack.
The following sequence diagram shows the lifecycle of a dynamic connection with ECOM registration mechanism enabled.
Java API
Opening a connection is done using
ej.ecom.io.Connector.open(String url). The connection string (the
url
parameter) must start with “comm:”, followed by the Comm port
identifier, and a semicolon-separated list of options. Options are the
baudrate, the parity, the number of bits per character, and the number
of stop bits:
baudrate=n (9600 by default)
bitsperchar=n where n is in the range 5 to 9 (8 by default)
stopbits=n where n is 1, 2, or 1.5 (1 by default)
parity=x where x is odd, even or none (none by default)
All of these are optional. Illegal or unrecognized parameters cause an IllegalArgumentException.
Driver API
The ECOM Comm Low Level API is designed to allow multiple implementations (e.g. drivers that support different UART hardware) and connection instances (see Low Level API Pattern chapter). Each ECOM Comm driver defines a data structure that holds information about a connection, and functions take an instance of this data structure as the first parameter.
The name of the implementation must be set at the top of the driver C file, for example [3]:
#define LLCOMM_BUFFERED_CONNECTION MY_LLCOMM
This defines the name of this implementation of the
LLCOMM_BUFFERED_CONNECTION
interface to be MY_LLCOMM
.
The data structure managed by the implementation must look like this:
typedef struct MY_LLCOMM{
struct LLCOMM_BUFFERED_CONNECTION header;
// extra data goes here
} MY_LLCOMM;
void MY_LLCOMM_new(MY_LLCOMM* env);
In this example the structure contains only the default data, in the
header field. Note that the header must be the first field in the
structure. The name of this structure must be the same as the
implementation name (MY_LLCOMM
in this example).
The driver must also declare the “new” function used to initialize
connection instances. The name of this function must be the
implementation name with _new
appended, and it takes as its sole
argument a pointer to an instance of the connection data structure, as
shown above.
The driver needs to implement the functions specified in the
LLCOMM_impl.h
file and for each kind of connection, the
LLCOMM_BUFFERED_CONNECTION_impl.h
(or
LLCOMM_CUSTOM_CONNECTION_impl.h
) file.
The driver defines the connections it provides by adding connection
objects using LLCOMM_addConnection
. Connections can be added to the
stack as soon as the LLCOMM_initialize
function is called.
Connections added during the call of the LLCOMM_impl_initialize
function are static connections. A static connection is registered to
the ECOM registry and cannot be removed. When a connection is
dynamically added outside the MicroJVM task context, a suitable
reentrant synchronization mechanism must be implemented (see
LLCOMM_IMPL_syncConnectionsEnter
and
LLCOMM_IMPL_syncConnectionsExit
).
When opening a port from the MicroEJ Application, each connection
declared in the connections pool will be asked about its platform port
number (using the getPlatformId
method) or its name (using the
getName
method) depending on the requested port identifier. The
first matching connection is used.
The life of a connection starts with the call to getPlatformId()
or
getName()
method. If the the connection matches the port identifier,
the connection will be initialized, configured and enabled.
Notifications and interrupts are then used to keep the stream of data
going. When the connection is closed by the application, interrupts are
disabled and the driver will not receive any more notifications. It is
important to remember that the transmit and receive sides of the
connection are separate Java stream objects, thus, they may have a
different life cycle and one side may be closed long before the other.
The following examples use Buffered connections, but Custom connections follow the same pattern.
The Buffered Comm Stream
In Buffered mode, two buffers are allocated by the driver for sending and receiving data. The ECOM Comm C module will fill the transmit buffer, and get bytes from the receive buffer. There is no flow control.
When the transmit buffer is full, an attempt to write more bytes from the MicroEJ Application will block the Java thread trying to write, until some characters are sent on the serial line and space in the buffer is available again.
When the receive buffer is full, characters coming from the serial line will be discarded. The driver must allocate a buffer big enough to avoid this, according to the UART baudrate, the expected amount of data to receive, and the speed at which the application can handle it.
The Buffered C module manages the characters sent by the application and stores them in the transmit buffer. On notification of available space in the hardware transmit buffer, it handles removing characters from this buffer and putting them in the hardware buffer. On the other side, the driver notifies the C module of data availability, and the C module will get the incoming character. This character is added to the receive buffer and stays there until the application reads it.
The driver should take care of the following:
Setting up interrupt handlers on reception of a character, and availability of space in the transmit buffer. The C module may mask these interrupts when it needs exclusive access to the buffers. If no interrupt is available from the hardware or underlying software layers, it may be faked using a polling thread that will notify the C module.
Initialization of the I/O pins, clocks, and other things needed to get the UART working.
Configuration of the UART baudrate, character size, flow control and stop bits according to the settings given by the C module.
Allocation of memory for the transmit and receive buffers.
Getting the state of the hardware: is it running, is there space left in the TX and RX hardware buffers, is it busy sending or receiving bytes?
The driver is notified on the following events:
Opening and closing a connection: the driver must activate the UART and enable interrupts for it.
A new byte is waiting in the transmit buffer and should be copied immediately to the hardware transmit unit. The C module makes sure the transmit unit is not busy before sending the notification, so it is not needed to check for that again.
The driver must notify the C module on the following events:
Data has arrived that should be added to the receive buffer (using the
LLCOMM_BUFFERED_CONNECTION_dataReceived
function)Space available in the transmit buffer (using the
LLCOMM_BUFFERED_CONNECTION_transmitBufferReady
function)
The Custom Comm Stream
In custom mode, the ECOM Comm C module will not do any buffering. Read and write requests from the application are immediately forwarded to the driver.
Since there is no buffer on the C module side when using this mode, the driver has to define a strategy to store received bytes that were not handed to the C module yet. This could be a fixed or variable side FIFO, the older received but unread bytes may be dropped, or a more complex priority arbitration could be set up. On the transmit side, if the driver does not do any buffering, the Java thread waiting to send something will be blocked and wait for the UART to send all the data.
In Custom mode flow control (eg. RTS/CTS or XON/XOFF) can be used to notify the device connected to the serial line and so avoid losing characters.
BSP File
The ECOM Comm C module needs to know, when the MicroEJ Application is
built, the name of the implementation. This mapping is defined in a BSP
definition file. The name of this file must be bsp.xml
and must be
written in the ECOM comm module configuration folder (near the
ecom-comm.xml
file). In previous example the bsp.xml
file would
contain:
<bsp>
<nativeImplementation
name="MY_LLCOMM"
nativeName="LLCOMM_BUFFERED_CONNECTION"
/>
</bsp>
where nativeName
is the name of the interface, and name
is the
name of the implementation.
XML File
The Java platform has to know the maximum number of Comm ports that can be managed by the ECOM Comm stack. It also has to know each Comm port that can be mapped from an application port number. Such Comm port is identified by its platform port number and by an optional nickname (The port and its nickname will be visible in the MicroEJ launcher options, see Standalone Application Options ).
A XML file is so required to configure the Java platform. The name of
this file must be ecom-comm.xml
. It has to be stored in the module
configuration folder (see Installation).
This file must start with the node <ecom>
and the sub node
<comms>
. It can contain several time this kind of line:
<comm platformId="A_COMM_PORT_NUMBER" nickname="A_NICKNAME"/>
where:
A_COMM_PORT_NUMBER
refers the Comm port the Java platform user will be able to use (see Application Port Mapping).A_NICKNAME
is optional. It allows to fix a printable name of the Comm port.
The maxConnections
attribute indicates the maximum number of
connections allowed, including static and dynamic connections. This
attribute is optional. By default, it is the number of declared Comm
Ports.
Example:
<ecom>
<comms maxConnections="20">
<comm platformId="2"/>
<comm platformId="3" nickname="DB9"/>
<comm platformId="5"/>
</comms>
</ecom>
First Comm port holds the port 2, second “3” and last “5”. Only the second Comm port holds a nickname “DB9”.
ECOM Comm Mock
In the simulation environment, no driver is required. The ECOM Comm mock handles communication for all the serial ports and can redirect each port to one of the following:
An actual serial port on the host computer: any serial port identified by your operating system can be used. The baudrate and flow control settings are forwarded to the actual port.
A TCP socket. You can connect to a socket on the local machine and use netcat or telnet to see the output, or you can forward the data to a remote device.
Files. You can redirect the input and output each to a different file. This is useful for sending precomputed data and looking at the output later on for offline analysis.
When using the socket and file modes, there is no simulation of an UART baudrate or flow control. On a file, data will always be available for reading and will be written without any delay. On a socket, you can reach the maximal speed allowed by the network interface.
Dependencies
ECOM (see Serial Communications).
LLCOMM_impl.h
andLLCOMM_xxx_CONNECTION_impl.h
implmentations (see LLCOMM: Serial Communications).
Installation
ECOM-Comm Java library is an additional library. In the platform
configuration file, check Serial Communication > ECOM-COMM to
install it. When checked, the xml file ecom-comm/ecom-comm.xml
is required during platform creation to configure the module (see
XML File).
Use
The ECOM Comm API Module must be added to the module.ivy of the MicroEJ Application project to use the ECOM Comm library.
<dependency org="ej.api" name="ecom-comm" rev="1.1.4"/>
This Foundation Library is always required when developing a MicroEJ Application which communicates with some external devices using the serial communication mode.
This library provides a set of options. Refer to the chapter Standalone Application Options which lists all available options.