serial - Generic Serial Record

Mark Rivers

Contents

Overview

The Serial record is designed to perform generic serial (RS-232, RS-485, etc.) I/O. It is intended to allow EPICS to communicate with a new serial instrument without even rebooting the IOC, i.e. without writing any C code or changing the database. The Serial record is thus very useful for allowing Channel Access clients to communicate with serial devices for which no EPICS device support exists. It is also useful for serial diagnostics. A Serial record is associated with a particular serial port. All of the port parameters (baud rate, parity, etc.) are controlled by fields in the record, and can thus be changed at any time.

There are two output fields, AOUT (ASCII Output) and BOUT (Binary Output). The OFMT (Output Format) field is used to select one of these fields or the other as the output source to the serial device. Similarly, there are two input fields, AINP (ASCII Input) and BINP (Binary Input). The IFMT (Input Format) field is used to select one or the other as the destination of data sent from the serial device. The ASCII fields are type DBF_STRING, and are very convenient for typical communication with many serial devices. They permit, for example, medm screens where the user can type a string and observe the response from the instrument. The ASCII fields, however are limited to 40 characters in length, and cannot be used to read or write binary data. The binary input and output fields are DBF_CHAR arrays, and can be used to transfer large blocks of arbitrary data, either ASCII or binary.

A read operation on input continues until 1 of the following 3 conditions is met:

  1. The input delimiter (IDEL) is found
  2. The desired number of input characters (NRRD) are received
  3. The timeout (TMOT) expires

The Serial record uses a separate device support layer, so that it can in principle be used with a variety of serial I/O cards. The Serial record presently has device support for HiDEOS serial devices.

The HiDEOS device support permits I/O Event Scanning (SCAN="I/O Intr") when the transfer mode (TMOD) is "Read". In this case the record will be processed whenever a complete message is received. A complete message is defined by the input delimiter (IDEL) and/or the requested number of input characters (NRRD), being received. The timeout field (TMOT) does not apply when SCAN="I/O Intr". For more information see the note under Restrictions below.


Record Field Descriptions

Name Access PromptData type Description
VAL R/W "Value field (unused)" DBF_STRING This field is unused. The functions normally assigned to the VAL field in many records are performed by the AOUT, BOUT, AINP, and BINP fields in the Serial record.
TMOD R/W "Transaction mode" DBF_RECCHOICE The type of Serial transaction which is desired. The choices are:
"Write/Read" (default) The output source (AOUT or BOUT as selected by OFMT) is sent to the Serial device. A response is then read back into AINP or BINP (as selected by IFMT). The response must be received within the time specified by TMOT. The input ring buffer is cleared before the read operation, so that any characters received prior to the write operation are discarded.
"Write" The output source (AOUT or BOUT as selected by OFMT) is sent to the Serial device. No response is read back.
"Read" Data is read from the Serial device into the input field (AINP or BINP as selected by IFMT). The response must be received within the time specified by TMOT. No output is sent to the device prior to the read operation.
AOUT R/W* "Output (command) string" DBF_STRING The output string which is sent to the device if OFMT="ASCII". The number of bytes sent to the device will be strlen(AOUT) (plus 1 if ODEL is not -1).
BOUT R/W* "Output binary data" DBF_CHAR (array) The output data which is sent to the device if OFMT="Binary". The maximum length of this field is controlled by OMAX. The actual number of bytes to be sent to the device is equal to NOWT (or NOWT plus 1 if ODEL is not -1).
ODEL R/W "Output delimiter" DBF_LONG A character which is appended to the output (AOUT or BOUT) before transmission to the device. Set this field to -1 to suppress transmission of a delimiter. Commonly used values are 10 (Line Feed) or 13 (Carriage Return, the default).
OMAX R "Max. size of output array" DBF_LONG The allocated length of the BOUT array. This value cannot be changed after IOC initialization. Default=512.
NOWT R/W "Number of bytes to write" DBF_LONG The actual number of bytes to send from the BOUT array to the serial device if OFMT="Binary". This value does not include the optional output delimiter. This value must be less than or equal to OMAX. Default=512.
OFMT R/W "Output format" DBF_RECCHOICE The output format. The choices are:
"ASCII (default)" The data sent to the device will be taken from the AOUT field.
"Binary" The data sent to the device will be taken from the BOUT field.
AINP R "Input (response) string" DBF_STRING The input string which is read from the device if IFMT="ASCII". The string will be NULL terminated. Note that due to the maximum size of a string in EPICS, the input string must be less than 40 characters. If longer strings are required then set IFMT="Binary" and read into the BINP field.
BINP R "Input binary data" DBF_CHAR (array) The input data which is read from the device if IFMT="Binary". The maximum length of this field is controlled by IMAX. The actual number of bytes read from the device is given by NORD.
IDEL R/W "Input delimiter" DBF_LONG A character which indicates the end of a message on input. Set this field to -1 if no character should be used as an input delimiter. Commonly used values are 10 (Line Feed) or 13 (Carriage Return, the default). The input delimiter will be removed from the input buffer on both ASCII and Binary read operations.
IMAX R "Max. size of input array" DBF_LONG The allocated length of the BINP array. This value cannot be changed after IOC initialization. Default=512.
NRRD R/W "Number of bytes to read" DBF_LONG The requested number of bytes to read. This field is valid for both "ASCII" and "Binary" input formats. If this field is zero, then the requested number of bytes to read will be the EPICS defined MAX_STRING_SIZE=40 (if IFMT="ASCII") or IMAX (if IFMT="Binary"). Default=0.
NORD R "Number of bytes read" DBF_LONG The actual number of bytes read in the last read operation. This field is valid for both "ASCII" and "Binary" input formats. This number includes the input delimiter, if any.
IFMT R/W "Input format" DBF_RECCHOICE The input format. The choices are:
"ASCII" (default) The data read from the device will be placed in the AINP field.
"Binary" The data read from the device will be placed in the BINP field.
INP R "Input Specification" DBF_INLINK The input link specification. This field is used to select the serial device link information.
TMOT R/W "Timeout (msec)" DBF_LONG The timeout value for serial read operations in milliseconds. If a response is not received from the device within this time then the record sets a major alarm. Default=500.
Special Fields
Note: The following fields are used to set the serial port parameters. A write to any of these fields causes the port parameters to be changed immediately, but does not cause the record to process.
BAUD R/W "Baud rate" DBF_RECCHOICE The baud rate for the port. Choices are "300", "600", "1200", "2400", "4800", "9600", "19200", and "38400". Default="9600".
PRTY R/W "Parity" DBF_RECCHOICE The device parity. Choices are "None", "Even", and "Odd". Default="None".
DBIT R/W "Data bits" DBF_RECCHOICE The number of data bits. Choices are "5", "6", "7", and "8". Default="8".
SBIT R/W "Stop bits" DBF_RECCHOICE The number of stop bits. Choices are "1" and "2". Default="1".
FCTL R/W "Flow control" DBF_RECCHOICE The flow control. Choices are "None" and "Hardware". Default="None".
Private Fields
IPTR N "Input buffer pointer" DBF_NOACCESS The pointer to the buffer for the BINP field.
OPTR N "Output buffer pointer" DBF_NOACCESS The pointer to the buffer for the BOUT field.
OINP R "Previous input string" DBF_STRING The previous input string. Used for posting events when IFMT="ASCII".
ONRD R "Previous nord" DBF_LONG The previous value of NORD. Used for posting events.
ONWT R "Previous nowt" DBF_LONG The previous value of NOWT. Used for posting events.
Note: In the Access column above:
R Read only
R/W Read and write are allowed
R/W* Read and write are allowed; write triggers record processing if the record's SCAN field is set to "Passive".
N No access allowed

Files

The following table briefly describes all of the files required to implement the Serial record. The reader is assumed to be familiar with the EPICS Application Source/Release Control document which describes how to build an EPICS application tree into which these files are to be placed, and how to run "gnumake" to build the record support. These files can all be obtained in a compressed tar file. This file should be untarred in a <top>/xxxApp/ directory.

Files to be placed in <top>/xxxApp/src/
serialRecord.c The source file for the record
serialRecord.dbd The database definition file for the record
devSerial.cc The Hideos device support code for the record
Makefile.Vx This file is not included in the distribution. However, the user must edit this file and add lines similar to the following:
RECTYPES += serialRecord.h
SRCS.c   += ../serialRecord.c 
SRCS.c   += ../devSerial.cc
LIBOBJS  += serialRecord.o
LIBOBJS  += devSerial.o
USR_INCLUDES += -I$(HIDEOS_DIR)/include

Files to be placed in <top>/xxxApp/op/adl/
Serial_IO.adl This file builds an medm screen to access the Serial record. The medm screen is most useful for communicating with serial devices in ASCII. To use it from the command line, type the following:
cars> medm -x -macro REC=my_serial_record Serial_IO.adl

where my_serial_record is the name of a Serial record in an IOC.

This file can also be used as a related display from other medm screens by passing the argument REC=my_serial_record.


Restrictions

There should be a way to optionally force the input ring buffer to be flushed on a "Read" operation, as it is on "Write/Read". This may be added in the future.

There are two bugs in the HiDEOS device support when using I/O Event Scanning. If the SCAN field is changed from "I/O Intr" to any other value then the record will "hang-up" until at least one more input message is received. The reason for this is that "I/O Intr" scanning is implemented by passing a HiDEOS "Read" request with infinite time out. There is presently no way to abort this request, and the HiDEOS task will wait until the request is satisfied. The second bug is that specifying an infinite timeout actually does not work, so the record actually requests 10^9 milliseconds = 1 million seconds. Thus, in I/O Event Scanning mode the record will actually time out about every 10 days if the device does not send any messages. Both of these bugs should be fixed in the near future.

The HiDEOS input ring buffer is limited to 500 characters. This is thus an upper limit on the maximum message size which can be received. If a device sends a larger message it will need to be read by processing the record multiple times. This will probably require using flow control.


Release Notes


Example

The following is an IDL program which demonstrates the use of the Serial record. It transfers data in both ASCII and binary formats.
; This IDL program demonstrates the use of the EPICS Serial record.
; The program uses 2 serial records.  The ports corresponding to these
; 2 records are connected with a null-modem cable
; Record 1 sends a message to record 2 in ASCII.
; Record 2 sends a message back to record 1 in binary.

; Record names
rec1 = 'test_serial1'
rec2 = 'test_serial2'
recs = [rec1, rec2] ; Array with both record names
; Set up port parameters for both records:
;   19,200 baud, 8 data bits, 1 stop bit, no parity, no flow control
;   Timeout=1 second
castartgroup
for i=0, 1 do begin
    rec = recs(i)
    t = caput(rec+'.BAUD', '19200')
    t = caput(rec+'.DBIT', '8')
    t = caput(rec+'.SBIT', '1')
    t = caput(rec+'.PRTY', 'None')
    t = caput(rec+'.FCTL', 'None')
    t = caput(rec+'.TMOT', 1000)
endfor

; Put record 1 in ASCII output mode, <CR> output delimiter,
;   binary input mode, no input delimiter
t = caput(rec1+'.OFMT', 'ASCII')
t = caput(rec1+'.ODEL', 13)
t = caput(rec1+'.IFMT', 'Binary')
t = caput(rec1+'.IDEL', -1)
; Put a monitor on record 1 Binary input field
t = casetmonitor(rec1+'.BINP')

; Put record 2 in Binary output mode, no output delimiter
;   ASCII input mode, <CR> input delimiter
t = caput(rec2+'.OFMT', 'Binary')
t = caput(rec2+'.ODEL', -1)
t = caput(rec2+'.IFMT', 'ASCII')
t = caput(rec2+'.IDEL', 13)

; Put record 2 in read transfer mode
t = caput(rec2+'.TMOD', 'Read')
; Put a monitor on record2 ASCII input field
t = casetmonitor(rec2+'.AINP')
; Process record 2; this will cause it to wait for data
t = caput(rec2+'.PROC', 1)

; Put record 1 in write transfer mode
t = caput(rec1+'.TMOD', 'Write')
; Send a message to port 2
message = 'Request data: '+string(systime())
print, 'Record 1 sent message: ' + message
t = caput(rec1+'.AOUT', message)
; End the group - this causes all of the above channel access
; commands to actually be sent to the IOC
t = caendgroup()


; Wait for monitor on record2 ASCII input field
while (not cacheckmonitor(rec2+'.AINP')) do wait, .1
; Read data from record 2
t = caget(rec2+'.AINP', input)
print, 'Got a message from record 1: ', input

castartgroup
size=256
; Put record 1 in read mode, expect "size" byte input
t = caput(rec1+'.TMOD', 'Read')
t = caput(rec1+'.NRRD', size)
; Process record 1; this will cause it to wait for data
t = caput(rec1+'.PROC', '1')

; Put record 2 in write mode
t = caput(rec2+'.TMOD', 'Write')
; Send an 8 bit binary sin wave, "size" points long from
; port 2 to port 1
send_data = byte(sin(findgen(size)/5)*126 + 127)
t = caput(rec2+'.NOWT', size)
t = caput(rec2+'.BOUT', send_data)
t = caendgroup()

; Wait for monitor on channel 1 binary input
while (not cacheckmonitor(rec1+'.BINP')) do wait, .1

; Record 1 should have received "size" bytes. Make sure NORD=size
t = caget(rec1+'.NORD', nord)
if (nord eq size) then $
    print, 'Read array data OK' $
else $
    print, 'Error reading array data!'

; Read data from record 1
t = caget(rec1+'.BINP', rec_data, max=nord)

; Plot it
plot, rec_data

end

Suggestions and comments to: Mark Rivers : (rivers@cars.uchicago.edu)
Last modified: December 14, 1997