EPICS goes CAN with
Multiple Protocol Support
Within the BESSY II control system the CAN field bus will serve a variety of tasks: In addition to the 'normal' device control it will interface underlying intelligent subsystems (like measurement PCs, PLCs), can be used for downloading configuration data and possibly object code to the embedded controllers, carries the ID compensation data and can be used for synchronization of IOC tasks. These different communication tasks require different protocols to be used in parallel on one CAN segment.
The CAN interface has to be implemented on three platforms: The IOC (Motorola MVME with VxWorks/EPICS and commercial 68000-based CAN cards), the embedded controller (i80386EX without OS with a single on-board CAN chip) and a PC used for measurement and debugging (80x86 using DOS and an AT bus CAN card).
SCI, the Simple CAN Interface , is the specification of a library that embodies a standard for accessing CAN interface cards independent of the actual hardware. Programs that use this library to access the CAN field bus will be portable between different CAN cards and even different operating systems. SCI is intentionally kept simple and it largely represents the data-link layer of the OSI model.
SCI is prepared to handle several CAN ports which are distinguished by port numbers. The ports may be realized on one or more interface cards, not necessarily from the same vendor. Different interface cards may have to be accessed through different drivers, but these differences are hidden by SCI.
SCI is capable of multithreading. In order to facilitate this function, each thread that opens the library is provided with a unique pointer. This pointer, which is a parameter for all SCI functions, is used to distinguish different threads. The properties of a COB1 -- identifier, data length, timeout and type, where type can be one of read, write, remote-read and remote-write -- are defined when it is initialized. SCI then returns a pointer to an internal structure that is allocated and initialized per object. All other functions use this sci_object pointer as a handle to a certain object. Read and write functions exist in order to transfer data to and from CAN objects.
Reading can be done in three basically different ways: Immediate read gets whatever data is stored on the CAN hardware for a certain object. Read with timeout gets new data or -- if there is no new data -- waits for the COB to arrive. Install a callback function that is called when new data arrives for an object is the asynchronous mode to retrieve data. On multitasking operating systems this callback is called from within a signal handler.
The requirements of portability and reusability are fulfilled best by a layered interface structure. This allows the parallel use of different protocols through well-defined interfaces into the protocol layer and collects the platform dependencies in another layer. Thus the support for a protocol can be implemented fully independent from hardware and OS properties. See Overview of the MultiCAN Concept shows the task level structure of the CAN interface in front of its layer structure.
Above the low level CAN library SCI is the GPS layer ( Generic Protocol Support ) hiding the Operating System and hardware dependencies. The tasks and functions in this layer set up the data transfer between the CAN segments (through SCI) and a COB oriented cache structure which is an interface to the next layer handling the different protocols. This layer consists of a small set of functions for each protocol to be run over the CAN bus. These functions include initialization, reading and writing data in protocol specific format (the upper interface) as well as reading and writing data in SCI conformal format (the lower interface).
EPICS device support writes and reads data to/from the cache using the upper interface functions of the appropriate Protocol Function Set. For output records the write request is fed into a queue. The mCANWriter dequeues it, fetches the data using the lower interface of the protocol layer and calls SCI to send the message. If SCI rejects the message (there might be minimal delays between messages to be kept), the Writer puts it back into the protocol, where it gets buffered and is resent after the SCI-requested delay. For asynchronous record processing an EPICS callback may be requested. On an incoming message SCI calls the mCANReader task. The mCANReader gets the COB, stores it through the appropriate protocol function and eventually requests an EPICS callback, which gets the data out of the protocol layer and moves it to the EPICS record. To minimize any mutual influences between protocols there is a mCANReader task for every protocol in use (not shown in figure). A third task, mCANTimer, periodically checks the protocol's status to detect timeouts caused by failures of the CAN hardware or the peer CAN node.
Since the Protocol Function Sets are almost symmetrical and platform independent, the implementation of a new protocol is cut down to implementing its set of functions, which should be running on all target systems (i.e., the IOC as well as the embedded controller or PC).
It allows an application to announce a number of entities on one of the supported protocols. The application has to provide a function, which is called in a later pass of the initialization by the device support of the corresponding protocol. The pointer of that function is the last parameter of mcan_attach() . That function must contain the calls for the initialization of all entities of that protocoll used by the application.
GPS uses this function to return those messages back to protocol that were rejected by sci_write_inhibit because of their inhibit time not having expired. The protocol takes the message back and organizes re-notification of the GPS writer task after delay microseconds.
This function is called by the reading task of the GPS layer to put the object handle and the raw data of a received message into the protocol layer. The object handle may be changed by the protocol, if post-processing is desired for a different object. This may be needed for different variables in a LowCAL variable set, where one object receives the data for all variables of a variable set.
The architecture of CAL allows to implement and use these services independently. Since only small parts of the CAL definition are relevant for use in the BESSY II controls, this feature was used to minimize the implementation effort.
Additionally arrays of variables of these basic types can be defined, as long as the CMS Encoding Rules are obeyed.4
The server of a variable is the CAN node where the variable originally resides. Accesses on the server side do a local update/read of the variable's value, accesses from the client side lead to request and response messages being sent over the CAN bus.
Variable sets can be used to "multiplex" several variables. All these multiplexed variables will then be mapped onto the COBs that are used by that variable set. This indirect addressing scheme reduces the number of used COBs. Within a variable set the variables are identified by a unique 7 bit "multiplexor", so the number of variables in a set is limited to 128. Since the multiplexor is transmitted in the data part of the CAN message, the maximum data size for a multiplexed variable is reduced to 7 bytes.
The access type of a variable is seen from the point of view of the client. Read-only variables can be used by a client only to collect data. The collected data will be the data that was set by the server in its last write access -- previous updates are lost. Write-only variables can be used by a client to request one or more servers to execute a command. This access is not confirmed (multicast). Read-write variables can be used by a client to collect the `current data' from the server (read access) or to request the server to take a value and/or execute a command (write access). Both read and write accesses are confirmed.
The inhibit time enforces a minimal time delay between accesses on the same variable resp. variable set. This can be used to slow down a client not to exceed the capabilities of the variable's server.
Order and type of the messages sent over the CAN bus during a LowCAL variable access strongly depend on the attributes of that variable. There are unconfirmed and confirmed services (`service' being a read or write variable access). The unconfirmed services use one COB that may have more than one recipient, confirmed services usually use two COBs (one for each direction) and connect exactly one server/client pair.
One confirmed service (Read Variable) is defined. The client sends a Remote Transmit Request (RTR) frame over the CAN, the server responds with a message containing the requested data. One COB is needed for the connection. There has to be exactly one server and an arbitrary number of clients.
One confirmed service (Read Variable) is defined. The client sends a request frame which contains the multiplexor of the variable to be read. The server returns the data and a flag indicating the result of the operation. Two COBs are needed for the connection (one for each direction). There has to be at most one server and one client per connection.
One unconfirmed service (Write Variable) is defined. The client sends a data frame to
the server(s). One COB is needed for the connection. There may be one or more server(s) and at most one client.most one client.most one client.
Two confirmed services (Read Variable and Write Variable) are defined. The client sends a request frame which contains the multiplexor of the variable to be accessed and a flag indicating the operation type. The server returns the data and a flag indicating the result of the operation. Two COBs are needed for the connection (one for each direction). There has to be at most one server and one client per connection.
The CAN message encoding rules are conformant with the CMS (CAL) encoding rules. Since LowCAL defines a subset of the CMS data types (only 8 bit aligned types), the encoding rules can be simplified as follows:
See Encoding Rules (Example) shows the encoding for an array of two signed short integer variables.
lowcal_init_entity ( unsigned short port ,
unsigned short out_id ,
unsigned short in_id ,
byte max_length ,
mcan_Obj_Handle* obj_handle_p ,
lcal_Attrib var_attrib ,
lcal_Data_Type var_type ,
unsigned long timeout );
The second pass of initialization initializes a variable's buffer entry, creates a new variable set (if needed) and initializes the SCI in and out objects used for this variable. Arguments might be ignored, if not applicable (e.g., in_obj resp. out_obj and timeout for unconfirmed services). The returned object handle is used to identify the variable in subsequent LowCAL calls.
On the client side, this routine copies the data into the protocol's cache buffer. If there is no request pending, it issues the sending of the appropriate write request by putting the request into the send queue and notifying the writer task.
On the client side, this routine copies the cache buffer's contents to the variable data_p points to. If there is no request pending, it issues the sending of the appropriate read request by putting the request into the send queue and notifying the writer task.
Calling this function signals the completion of post processing to the protocol. The buffer entry's flags are set accordingly. On the client side, if a read or write access has been postponed because of post-processing being busy, it is issued by putting the request into the send queue and notifying the writer task.
The Generic Protocol Support is the glue between the SCI library and the different protocols. It is able to notify the applications by means of semaphores or the EPICS callback mechanism. EPICS needs this facility because the main EPICS tasks mustn't wait and EPICS has to be called again in some cases for finishing data transmission (asynchronous record processing).
The central data structure is the GPS table of protocols in use, gps_table , whose entries' (one for each protocol used) structure is shown in See GPS Table of Protocols in Use.
Since each protocol does its own SCI open, the first entry is a copy of the protocol's SCI handle. The other entries are pointers to the protocol specific functions of the lower (GPS) interface: get function (e.g. rflatcan_get()), the protocol specific put function (e.g. rflatcan_put()) and the protocol specific time function (rflatcan_time()).
The initialization function gpsStart() starts a Reader task for every protocol used. [Footnote: The Reader tasks' names are 'mCANReadr<n>', where <n> is the protocol's offset in the gps_table.] After a consistency check on correct initialization the reader enters its infinite loop: It waits for any CAN messages by calling sci_queue_read(), then storing the collected data into the protocol layer using the protocol's put() function (which it gets out of the gps_table). If the protocol demands post processing, the Reader analyzes the switch in the object's user part and sets up the appropriate post processing (EPICS callback or ScanIO request, posting a semaphore etc.).
After initialization the Writer [Footnote: The Writer's task name is 'mCANWriter'.] enters a nested endless loop through the gps_table. It waits for a notification by any of the protocols that there are messages ready to be sent (i.e. it waits for the writer semaphore prot_write_sem). When this notification is given, the writer loops through the protocols calling the protocol specific get functions until it finds a protocol that returns a message. It calls sci_write() to send the message and performs any demanded post processing (e.g. EPICS callback requests). This post processing is controlled by the pp_switch in the SCI object's user part. After a successful write and post processing the writer waits for the next message by waiting for the semaphore again.
After initialization the Timer [Footnote: The Timer's task name is 'mCANTimer'.] enters an endless loop through the protocols: It calls the protocol specific time function (registered in the gps_table). If the time function demands post processing (i.e. if a timeout occurred), the appropriate post processing is performed (EPICS callback/ScanIO or posting a semaphore). If the time function's pending flag (PROT_PEND) is set, the same time function of the same protocol is called again -- in this case more than one request of a protocol had a timeout. After all protocols have been processed, the Timer delays itself and starts all over again.
The startup script has to do certain steps of initialization to set up the MultiCAN environment before iocInit or any of the protocols' init routines may be called. Figure [Ref: fig:startup] shows a partial startup script.
First of all, the MultiCAN core gets loaded into the IOC, second step is the driver initialization (in this case the vcan driver for the VME CAN 02 card by esd). mCANInit(char *initfile) loads the bitrate-port-table (the first line contains the number of entries (i.e. the number of can ports on this IOC), the following lines contain a port number and the corresponding bitrate; fig. [Ref: fig:bitrate-table] shows a sample)
into the IOC. It performs a sci_open() and a sci_init() for every port defined in the initfile, then the SCI library is closed again (using sci_close()). The pointer to the bitrate-table is stored in a global variable (multican_bitrate). Besides the list-table needed in the following phase of initialization is allocated. For every protocol it has three entries: protocol, pointer to (EPICS) record and done flag.
gpsInit() initializes some GPS internals (e.g. semaphores) and starts the Writer task (see [Ref: sec:GPSwriter] ) to avoid things (e.g. EPICS records) being blocked because CAN messages cannot be sent yet. After the initialization of CAN links (by EPICS' iocInit() or other user software) gpsStart() sets up the Reader tasks (one for each protocol used) and the Timer task (timeout watchdog).
Using MultiCAN for transmitting data from an EPICS database to a variety of devices over the CAN field bus needs appropriate device support. The runtime interface of that layered architecture is provided by the set of protocols. Every implemented protocol needs an own device support. At the moment the protocol LowCAL is supported for the following record types:
The following modules have to be rebuilt7 for supporting MultiCAN:
For initialization reasons the file initHooks.c has to be compiled after the setting of MCAN. If it is intended to use MultiCAN with other applications beside EPICS, the environment variable ATTACH must be set. Besides the file .../epics/base/src/db/initHooks.c has to be modified in the here shown way:
More detailed information about attaching applications to the MultiCAN initialization done by EPICS can be found in See The use of MultiCAN by applications beside EPICS.
The other is field is the INP field or the OUT field. It has to contain a string with the maximum length of 35 characters . That sequence of characters describes the connection to another entity using MultiCAN.
For LowCAL that string has the following syntax. Like for all strings in these fields the first character is '@' . It is followed by the first character describing attributes of the LowCAL variable. These are the meanings of the several permissible characters:
String being encoded or decoded by the device support8
At the moment there are no array records, for instance array of short, available in EPICS. For reading and writing such arrays to or from a set of single records over the CAN bus it needs a workaround. With the help of a string record and a sequencer it is now possible to substitute the missing record types. The in described sequencer realizes the connection between the single records and the string record. The string record is connected over its hardlink via MultiCAN to a peer entity using LowCAL too. For avoiding an occurrence of '\0' in the string, what would lead to an arbitrary cut off, encoding and decoding functions are used in the sequencer and in the device support. The setting of the data type in the hardware link to'b' activate that feature. Every simulation of an array record requires one separate state set in the state program.
J. Bergl, B. Kuner, R. Lange, I. Müller, R. Müller, G. Pfeiffer, J. Rahn, H. Rüdiger: Controller Area Network (CAN) -- a Field Bus Gives Access to the Bulk of BESSY II Devices. Presented at the ICALEPCS '95, Chicago, 1995.