next up previous
Next: Add the device support Up: How to create EPICS Previous: Make some changes to

Subsections

Create the device support file

The contents of the device support file provide all the details of the communication between the device and EPICS. The easiest way to create a device support file is to copy the skeleton device support file from the gpibCore module source directory to your application source directory:
norume> cd AB300App/src
norume> cp /usr/EPICS/R3.14.2/modules/bus/gpib/gpibCore/gpib/devCommon/devSkeletonGpib.c devAB300.c
Of course, device support for a device similar to the one you're working with provides an even easier starting point.

The remainder this section describes the changes that I made to the skeleton file in order to support the AB300 filter wheel. You'll have to modify the steps as appropriate for your device.

Declare the DSET tables provided by the device support

Since the AB300 provides only longin and longout records most of the DSET_xxx define statements can be removed. Because of the way that the device initialization is performed you must define an analog-in DSET even if the device provides no analog-in records (as is the case for the AB300).
#define DSET_AI    devAB300_ai
#define DSET_LI    devAB300_li
#define DSET_LO    devAB300_lo

Change the debugging flag name

It's useful, but not necessary, to change the name of the debugging flag from SkeletonDebug to something more appropriate:
static int devAB300Debug = 0;

Select timeout values

The default value of TIME_WINDOW (2 seconds) is reasonable for the AB300, but I increased the value of IO_TIME to 5 seconds since the filter wheel can be slow in responding.
#define TIME_WINDOW 2000       /* wait 2 seconds after device timeout */
#define IO_TIME     5000       /* I/O must complete within 5 seconds */

Clean up some unused values

The skeleton file provides a number of character string arrays. None are needed for the AB300 so I just removed them. Not much space would be wasted by just leaving them in place however.

Declare the GPIB command array

This is the hardest part of the job. Here's where you have to figure how to produce the command strings required to control the device and how to convert the device responses into EPICS process variable values.

Each command array entry describes the details of a single I/O operation type. The application database uses the index of the entry in the command array to provide the link between the process variable and the I/O operation to read or write that value.

The command array entries I created for the AB300 are shown below. The elements of each entry are described using the names (f1-f13) from the GPIB documentation.

Command array index 0 - Device Reset

   {&DSET_LO, GPIBWRITE|GPIBEOS, IB_Q_HIGH, NULL, "\377\377\033", 10, 10,
       NULL, 0, 0, NULL, NULL, '\033'},
f1
This command is associated with an longout record.
f2
A WRITE operation is to be performed and that the readback from the device will end when the end-of-string character is received.
f3
This operation should be placed on the high-priority queue of I/O requests.
f4
Because this is a GPIBWRITE operation this element is unused.
f5
The format string to generate the command to be sent to the device. The first two bytes are the RESET command, the third byte is the ECHO command. The AB300 sends no response to a reset command so I send the 'ECHO' to verify that the device is responding. The AB300 resets itself fast enough that it can see an echo command immediately following the reset command.

Note that the process variable value is not used (there's no sprintf % format character in the command string). The AB300 is reset whenever the EPICS record is processed.

f6
The size of the readback buffer. Although only one readback byte is expected I allow for a few extra bytes just in case.
f7
The size of the buffer into which the command string is placed. I allowed a little extra space in case a longer command is used some day.
f8
No special conversion function is needed.
f9-f11
There's no special conversion function so no arguments are needed.
f12
There's no name table.
f13
The end-of-string value used to mark the end of the readback operation.

Command array index 1 - Go to new filter position

    {&DSET_LO, GPIBWRITE|GPIBEOS, IB_Q_LOW, NULL, "\017%c", 10, 10,
        NULL, 0, 0, NULL, NULL, '\030'},
f1
This command is associated with an longout record.
f2
A WRITE operation is to be performed and that the readback from the device will end when the end-of-string character is received.
f3
This operation should be placed on the high-priority queue of I/O requests.
f4
Because this is a GPIBWRITE operation this element is unused.
f5
The format string to generate the command to be sent to the device. The filter position (1-6) can be converted to the required command byte with the sprintf %c format.
f6
The size of the readback buffer. Although only two readback bytes are expected I allow for a few extra bytes just in case.
f7
The size of the buffer into which the command string is placed. I allowed a little extra space in case a longer command is used some day.
f8
No special conversion function is needed.
f9-f11
There's no special conversion function so no arguments are needed.
f12
There's no name table.
f13
The end-of-string value used to mark the end of the readback operation.

Command array index 2 - Query filter position

    {&DSET_LI, GPIBREAD|GPIBEOS, IB_Q_LOW, "\035", NULL, 0, 10,
        convertPositionReply, 0, 0, NULL, NULL, '\030'},
f1
This command is associated with an longin record.
f2
A READ operation is to be performed and that the read operation will end when the end-of-string character is received.
f3
This operation should be placed on the high-priority queue of I/O requests.
f4
The command string to be sent to the device. The AB300 responds to this command by sending back three bytes: the current position, the controller status, and a terminating '\030'.
f5
Because this is a GPIBREAD operation this element is unused.
f6
There is no command echo to be read.
f7
The size of the buffer into which the reply string is placed. Although only three reply bytes are expected I allow for a few extra bytes just in case.
f8
There's no sscanf format that can convert the reply from the AB300 so a special conversion function must be provided.
f9-f11
The special conversion function requires no arguments.
f12
There's no name table.
f13
The end-of-string value used to mark the end of the read operation.

Command array index 3 - Query controller status

This command array entry is almost identical to the previous entry. The only change is that a different custom conversion function is used.
    {&DSET_LI, GPIBREAD|GPIBEOS, IB_Q_LOW, "\035", NULL, 0, 10,
        convertStatusReply, 0, 0, NULL, NULL, '\030'},

Write the special conversion functions

As mentioned above, special conversion functions are need to convert reply messages from the AB300 into EPICS PV values. The easiest place to put these functions is just before the gpibCmds table. The conversion functions are passed a pointer to the gpibDpvt structure and three values from the command table entry. The gpibDpvt structure contains a pointer to the EPICS record. The custom conversion function uses this pointer to set the record's value field.

Here are the custom conversion functions I wrote for the AB300.

/*
 * Custom conversion routines
 */
static int
convertPositionReply(struct gpibDpvt *pdpvt, int P1, int P2, char **P3)
{
    struct longinRecord *pli = ((struct longinRecord *)(pdpvt->precord));

    if( pdpvt->msg[2] == '\030')
        pli->val = pdpvt->msg[0];
    else
        recGblSetSevr(pli, READ_ALARM, INVALID_ALARM);
    return 0;
}
static int
convertStatusReply(struct gpibDpvt *pdpvt, int P1, int P2, char **P3)
{
    struct longinRecord *pli = ((struct longinRecord *)(pdpvt->precord));

    if( pdpvt->msg[2] == '\030')
        pli->val = pdpvt->msg[1];
    else
        recGblSetSevr(pli, READ_ALARM, INVALID_ALARM);
    return 0;
}

Some points of interest:

  1. The routine which calls the custom conversion function ignores the return value, so the custom conversion functions must set the record alarm state directly.
  2. I put in a sanity check to ensure that the end-of-string character is where it should be.

Provide the device support initialization

Because of way code is stored in object libraries on different systems the device support parameter table must be initialized at run-time. The analog-in initializer is used to perform this operation. This is why all device support files must declare an analog-in DSET.

Here's the initialization for the AB300 device support. As you can see, most of the skeleton file values are left unchanged:

static long init_ai(int parm)
{
    if (parm == 0) {
        devSupParms.debugFlag = & devAB300Debug;
        devSupParms.respond2Writes = 0;
        devSupParms.timeWindow = TIME_WINDOW;
        devSupParms.hwpvtHead = 0;
        devSupParms.gpibCmds = gpibCmds;
        devSupParms.numparams = NUMPARAMS;
        devSupParms.magicSrq = -1;
        devSupParms.name = " AB300";
        devSupParms.timeout = IO_TIME;
        devSupParms.srqHandler = devGpibLib_srqHandler;
        devSupParms.wrConversion = NULL;
    }
    return (devGpibLib_initDevSup(parm, &DSET_AI));
}
Three values have been changed:
  1. The debugFlag entry points to the device support debugging flag.
  2. The AB300 sends back values in response to commands, but needs no time delay, so the respond2Writes entry is set to 0.
  3. The name entry is used for diagnostic purposes only.
The AB300 (like all other serial line 'GPIB' devices) does not generate service requests. The srqHandler field is unchanged from the skeleton file because it doesn't hurt to leave it that way.


next up previous
Next: Add the device support Up: How to create EPICS Previous: Make some changes to