A. Freyberger
The TS5500 PC1041 CPU available from Technologic Systems is a small single board computer [SBC] consisting of 133MHz 586 processor and has on board eight 12-bit ADCs, 38 digital input/output lines, three RS232 serial ports, two USB ports and a PCMCIA slot2. The TS5500 can be purchased with Linux pre-installed or other operating systems. EPICS as of release 3.14 now runs on Linux operating systems, can the TS5500 be turned into an EPICS IOC???
For those who do not care for the details of Epics/Linux, but just want to get a TS5500 on-line as soon as possible the next section should be sufficient. Work on providing compact flash images for other TS5?00 SBC is in progress.
The following sections describe the steps needed to develop device support for the ADC and DIO lines on the TS5500. First a description of how to build EPICS R3.14.? on your Linux box and set it up for local development. Then the device support for the ADC will be presented. The details of the DIO device support follow. The open issues of using these devices for controls will be presented in the final section.
For immediate deployment of a TS5500 in the field, EPICS support has been macro'ized so that all one needs to do is overwrite the compact flash with a copy that I'll make available. With an EPICS compact flash, the user then needs to adjust the network parameters and change the EPICS record name prefix so that the EPICS database records have unique names. Then a reboot3 and the device should be up and running. This will result 19 digital inputs, 8 digital outputs and 8 ADC EPICS records available on the network. There are additional 11 DIO channels available, but device support has not been written for these channels yet4.
When purchasing a TS5500 the following options need to be specified:
To change the TS5500 network configuration, you will need to:
Every EPICS record must have an unique name. To give the database records an unique name do the following:
The TS5500 is also running a webserver, so one can serve the ``adl'' files via http. Pointing a web browser to http://hostname.jlab.org/ts5500.adl will result in the adl file being sent to your browser, not very helpful. But you can save that to file named ts5500.adl and then execute:
![]()
|
![]()
|
This results in a set of very simple EPICS records. One can add additional database [software] records for more complicating activities. Adding new database records consists of adding or modifying the files in /usr/local/bin/epics/db and modifying /usr/local/bin/epics/iocBoot/ts5500/st.cmd accordingly. The EPICS State Notation Language [SNC] code has been ported to Linux/EPICS-3.14 as well for state machine applications. SNC code will require a complete recompile of the iocCore executable. For that you will need to read the rest of this document and a bit more.
In addition to the DIO and ADC channels the TS5500 has three RS232 ports of which two are available for controlling serial devices. The SBC has an application called ``minicom'' that allows one to communicate over these ports or one can write EPICS support for integration into the control system.
The following sections deal with developing EPICS software for the TS5500 SBC in a Linux environment. Presently most EPICS software on site is developed for VxWorks targets. VxWorks licenses are expensive. The TS5500 provides an inexpensive way to learn EPICS as the cross compilers are free and the hardware is about a factor of ten cheaper then VME hardware6.
Even though the TS5500 SBC runs Linux, it does not have a full Linux install so one still needs a host computer to compile software and then the resulting executables are copied to the TS5500 and executed. In principle this can be any computer with GCC appropriately configured to cross compile for 586 targets. In practice this means almost any PC running Linux is already setup as a host computer. There are some issues with the different versions of GCC and EPICS, but if you are running Redhat9 or Fedora Core2 you should be OK [don't try this on a Redhat8 machine].
The steps to building R3.14.2 have been posted on the archive previously7, the steps for getting R3.14.2 should still be close to correct for the more recent versions of 3.14.?. I'd recommend going with the latest version available as there has been a large amount of progress on R3.14 tree. The steps for R3.14.2 are:
In the previous section the EPICS example app was built and executed on the host computer. Now we must move the application to the SBC. Transferring files between the TS5500 and the host computer can be accomplished in many ways:
The build above results in a base build which you can work within if you so desire. Most developers use a more structured approach where the epics build resides in a safe place and an application development area that resides in a separate distinct area [preferably under the developers control]. This is usually accomplished by having an $EPICS (application area) and an $EPICS_BASE (base area) environment variables. This also helps migrating the application to the SBC since the necessary files are no longer buried in the base build. This works for R3.14.? as well. In the application area ($EPICS) the following subdirectories are found:
Technologic systems provides a Linux kernel driver for the eight on-board ADC channels. The driver supports two ranges, 5V and 10V as well as two polarities, uni-polar and bi-polar. The ADC can be read out at kHz rates. For the purposes of ``ai'' device support, which is processed at most five times a second, we ignore this capability. Rewriting the Linux char driver as a block memory driver would allow one to sample at a high rate and store the results in a waveform record. The DIO channels are access via direct memory read/writes9. The source files for TS5500 device support are located under $EPICS/dev/TS5500, in which you will find:
# device support
###############################################
#TS5500 on-board devices
device(ai,VME_IO,devAiTS5500,"TS5500_A2D")
device(bi,VME_IO,devBiTS5500,"TS5500_DIO")
device(bo,VME_IO,devBoTS5500,"TS5500_DIO")
#
include "TS5500Support.dbd"
As mentioned Technologic System provides a Linux kernel driver for the ADC channels. EPICS device support involves using this ``character'' driver to make this data available via EPICS. The ADC device nodes are created in /dev/ADC/0 ... /dev/ADC/7 and EPICS device support involves opening these devices, writing to them and reading from them. In addition ``ioctl'' is used for configuration. This is typical for Linux character devices. Note since the device support invokes only ``user'' calls this EPICS application does not have to be executed by ``root''.
For the ADC the analog input record is used [ai]. The device support initialization [init_record] consists of deciphering the INP field to configure the ADC range and polarity for each channel. The device support processing [read_ai] consists of reading the channel and posting the result. As of this writing the auto configuration the ESLO field [slope] is not working and this must be properly defined in the database file. The file devAiTS5500.c consists of 200 lines of code and comments and does not look that different than VME device support. The source for devAiTS5500.c is shown presented at the end of the paper and can be accessed electronically via.....
The digital input and output device support is not as clean as the device support for the ADC. The DIO ports are accessed by direct memory reads and writes and as such this application must be executed as root. One should write a Linux character driver for these ports, thereby allowing the EPICS application to be run in user space. The 38 DIO channels are distributed over three connector headers labels DIO1, DIO2 and LCD. Some channels are hardwired as inputs and outputs, but most can be configured [in groups of four] as either inputs or outputs. The LCD header is meant to drive a small LCD text screen and is configured in a weird way, so device support has not been implemented yet for these DIO channels. For the channels on DIO1 and DIO2 header, the device support configures the first eight channels on each header as inputs, the next four as outputs and the remaining odd channels are hardwired as inputs. This gives roughly two inputs for every outputs. This can be changed of course by modifying the source code if one desires a different ratio on outputs to inputs.
The source is listed at the end of the paper.
The three device support routines are located in the /dev/TS5500 directory. Executing ``make'' results in a library named libTS5500Support.a which can now be linked into an iocCore. Additionally TS5500Support.dbd is created in the $(EPICS)/dbd area. The final step is creating an application in $(EPICS)/app, for example ``ts5500''. In $(EPICS)/app/ts5500 there will be a src area for any additional SNC or other code, a Db for the databases and perhaps a medm area for the screens.
The src area should contain a ts5500Include.dbd file that holds all the database definitions that your application will be using. which in this case looks like:
include "../../../../dev/TS5500/TS5500Support.dbd"
include $(TOP)/configure/CONFIG
#--------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#=============================
# build an ioc application
PROD_IOC = ts5500
# <name>.dbd will be created from <name>Include.dbd
DBD += ts5500.dbd
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.dbd
ts5500_SRCS += ts5500_registerRecordDeviceDriver.cpp
ts5500_SRCS_DEFAULT += ts5500Main.cpp
ts5500_SRCS_vxWorks += -nil-
#
ts5500_LIBS += TS5500Support
ts5500_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
## The names of the binary above and .dbd file below will be incorrect
## unless you used the same names for the ioc and the app directory.
# create and register all record/device/driver/registrar support
dbLoadDatabase("/usr/local/epics/dbd/ts5500.dbd",0,0)
registerRecordDeviceDriver(pdbbase)
dbLoadRecords("/usr/local/epics/db/ts5500_ai.db", "prefix=casa-apf2")
dbLoadRecords("/usr/local/epics/db/ts5500_bi.db", "prefix=casa-apf2")
dbLoadRecords("/usr/local/epics/db/ts5500_bo.db", "prefix=casa-apf2")
iocInit()
## Start sequence programs here, if there are any
#
In order for the EPICS shell to get started upon reboot of the SBC a shell script has been written in the /etc/init.d area. This shell script gets executed at the end of ``runlevel 3''. Developers will want to disable this, by removing the softlink in the /etc/rc.d/rc3.d that points to this script.
To launch the EPICS shell, execute the st.cmd script as root. That is: /usr/local/epics/iocBoot/ts5500/st.cmd will drop you into the interactive epics shell. In order to get this shell to execute without being attached to a terminal some chicanery needs to invoked. Specifically within /etc/init.d/Epics.script one sees the line: sleep 99999d | $epics >& /dev/null &10. This line is waiting for an answer from the 99999days of sleep or the EPICS shell. If the EPICS shell does not crash, it will be terminated after 99999days or 273years, I doubt the Hampton Roads area will go that long without a power outage. Personally I'd like a more elegant/robust solution.
Using PC104 SBC for IOCs is still in its infancy. Using Linux as a operating system for EPICS application is also rather novel. So far the following EPICS applications have been successfully deployed on a PC104 SBC running Linux, serial port control of devices, SNC code, device support for daughter cards and the device support presented here. There are still a few things to try. For example: the TS5500 has inputs that will raise an interrupt level, associating an interrupt service routine to this would be a useful feature within the JLAB control environment11. The TS5500 can also come with a watchdog timer, which might have some use around the lab.
The Linux operating system on the TS5500 is not a real time OS. Any application that has a strict real-time requirement might not be satisfied by the TS5500. However, timing studies should be performed. Since the TS5500 SBC will not be loaded with as many applications as a typical VME crate it might be that the tasks are executed sufficiently fast to meet the requirement.
The EPICS software development environment can be made similar to the existing VxWorks environment such that developers do not suffer culture shock moving between the two environments. Debugging software on the SBC has been easier due to the fact that the SBC typically is running fewer [zero] applications and one can simply kill the EPICS application and restart it [no reboots required]. The application presented [35 database records, each scanned at a 5Hz rate] here utilizes less than 5% of the available CPU cycles and a few MBytes of memory.
Epics device support for on-board DIOs and ADCs on the TS5500 SBC has been successfully demonstrated. A software development model similar to existing EPICS development models using VxWorks has been developed and used in the course of this work. This development model should work with a group of concurrent developers although so far it has been used by a single user.
The TS5500 appears to be an inexpensive useful generic device which just on its own [no daughter cards] could be used for valve control[DIO], viewer control [DIO], power supply control [serial] or any other applications with modest DIO/ADC or serial requirements. This software was written for the Hall-A current calorimeter where the TS5500 and two Sensoray S518 daughter boards will be used to for 16 channels of thermometry [S518], selonoid control [DIO], interlock and position readbacks [DIO]. heater control [serial] and chiller control [serial]. A document on developing Linux drivers and EPICS device support for the Sensoray S518 PC104 daughter card is being written.
Not in any specific order.
/* devAiTS5500.c */
/*
* Technologic TS5500 analog digital converter PC104 form factor
*
* Kernel level driver support most be loaded for the EPICS
* device support to work
*
*
*/
/* Modification Log:
* ---------
* ...
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "alarm.h"
#include "cvtTable.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "recSup.h"
#include "devSup.h"
#include "link.h"
#include "dbScan.h"
#include "epicsExport.h"
#include "aiRecord.h"
#include "a2d_driver.h"
static unsigned getbit(unsigned, int);
static long init_record();
static long read_ai();
static long special_linconv();
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_ai;
DEVSUPFUN special_linconv;
} devAiTS5500={
6,
NULL,
NULL,
init_record,
NULL,
read_ai,
special_linconv};
epicsExportAddress(dset,devAiTS5500);
static long init_record(pai)
struct aiRecord *pai;
{
unsigned short value;
struct vmeio *pvmeio;
long status;
char dev[50];
int fd;
int range;
int card;
int channel;
int command;
/*
* ai.inp must be an VME_IO, hijack VME_IO link field for ISA bus
* on PC104 card [card/channel/config], card=0
*/
switch (pai->inp.type) {
case (VME_IO) :
printf("devAiTS5500: inp.type correctly set to VME_IO\n");
break;
default :
errlogPrintf("devAiTS5500 (init_record) Illegal INP field, should be VME_IO\n");
return(S_db_badField);
}
/* set linear conversion slope, this does not appear to be working*/
/* open the I/O device, get card, channel, and range from
* inp field */
pvmeio = (struct vmeio *)&(pai->inp.value);
sprintf(dev,"/dev/AtoD/%i",pvmeio->signal);
fd = open (dev, O_RDWR);
if(fd < 0) {
errlogPrintf("devAiTS5500 (init_record) error, unable to open device:%s\:\n", dev);
return(fd);
} else {
/* set range/polarity bit0=0 ->high range bit0=1 -> low range
bit1=0 ->bipolar bit1=1 -> unipolar */
range = atoi(pvmeio->parm);
if ( !(getbit(range,0)) && !(getbit(range,1))) {
command = (A2D_BIPOLAR | A2D_RANGE2);
} else if ( (getbit(range,0)) && !(getbit(range,1))) {
command = (A2D_BIPOLAR | A2D_RANGE1);
} else if ( !(getbit(range,0)) && (getbit(range,1))) {
command = (A2D_UNIPOLAR | A2D_RANGE2);
} else if ( (getbit(range,0)) && (getbit(range,1))) {
command = (A2D_UNIPOLAR | A2D_RANGE1);
} else {
printf("devAiTS5500 %i NOT A KNOWN RANGE/POLARITY %i %i\n",
pvmeio->signal, command, range);
command = (A2D_BIPOLAR | A2D_RANGE2);
}
printf("\nrange: %i command %i bit0 %i bit1 %i\n",
range, command, getbit(range,0), getbit(range,1));
errlogPrintf("devAiTS5500: setting card.chan: %i\.%i to type: %i\n",
pvmeio->card,pvmeio->signal,range);
ioctl(fd, TSA2D_CONFIG, command);
/* set the slope based on range/polarity */
if (command & A2D_RANGE2) {
if (command & A2D_BIPOLAR) {
/* +/- 10V */
pai->eslo = 20./4096;
} else {
/* 0->10V */
pai->eslo = 10./4096;
}
} else {
if (command & A2D_BIPOLAR) {
/* +/- 5V */
pai->eslo = 10./4096;
} else {
/* 0-> +5V */
pai->eslo = 5./4096;
}
}
printf("devAiTS5500 %i slope %f %i\n",
pvmeio->signal, pai->eslo, command);
}
close(fd);
return(0);
}
static long read_ai(pai)
struct aiRecord *pai;
{
struct vmeio *pvmeio;
int fd;
char dev[50];
short int datavalue;
short int status;
short int ret_status;
pvmeio = (struct vmeio *)&(pai->inp.value);
ret_status = -1;
sprintf(dev, "/dev/AtoD/%i",pvmeio->signal);
/* printf("devAiTS5500: read_ai: opening device %s\n", dev); */
fd = open (dev, O_RDWR);
if(fd > 0) {
status = write(fd, "trigger", sizeof("trigger"));
if (status < 0) {
errlogPrintf("devAiTS5500: write error: returned %d\n", status);
} else {
status = read (fd, &datavalue, sizeof(short int));
if (datavalue < 0) {
datavalue - (-1*datavalue);
}
pai->rval = datavalue;
/* printf("devAiTS5500: chan: %i reads %i %i \n", pvmeio->signal, datavalue, pai->rval); */
ret_status = 0;
}
} else {
errlogPrintf("devAiTS5500 (init_record) TS5500_driver error, unable to open device\n");
}
close(fd);
return(ret_status);
}
static long special_linconv(pai,after)
struct aiRecord *pai;
int after;
{
if(!after) return(0);
/* set linear conversion slope*/
pai->eslo = (pai->eguf - pai->egul)/32768.0;
errlogPrintf("devAiTS5500 slope %f\n",pai->eslo);
return(0);
}
/*
* dev support for DIO on TS5500 card
* default it to set the first four lines as output
* the rest are inputs.
*
* No use is made of the LCD port which can provide
* addition DIO if needed.
*
* This device supports makes direct access of IO ports
* and must be run as root. A better alternative would
* be to write char driver, so the epics device
* support could work in user space.
*/
/* Modification Log:
* ---------
* ...
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/io.h>
#include <sys/ioctl.h>
#include "alarm.h"
#include "cvtTable.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "recSup.h"
#include "devSup.h"
#include "link.h"
#include "dbScan.h"
#include "epicsExport.h"
#include "biRecord.h"
static long init_record();
static long read_bi();
unsigned getbit(unsigned, int);
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_bi;
} devBiTS5500={
5,
NULL,
NULL,
init_record,
NULL,
read_bi
};
epicsExportAddress(dset,devBiTS5500);
static long init_record(pbi)
struct biRecord *pbi;
{
struct vmeio *pvmeio;
int base_addr = 0x7A;
int addr;
/*
* bi.inp must be an VME_IO, hijack VME_IO link field for ISA bus
* on PC104 card [card/channel/config]
*/
switch (pbi->inp.type) {
case (VME_IO) :
printf("devBiTS5500: inp.type correctly set to VME_IO\n");
break;
default :
errlogPrintf("devBiTS5500 (init_record) Illegal INP field, should be VME_IO\n");
return(S_db_badField);
}
/* only need to do this once, does it hurt to do it for each record? */
pvmeio = (struct vmeio *)&(pbi->inp.value);
addr = base_addr + 3*(pvmeio->card - 1);
/* configure the ports on DIO1/2: 8->11 output rest input */
if (ioperm(addr,1,1)<0){
printf("Error\n");
return(-1);
}
/* set pins8 to 11 to outputs*/
printf("devBiTS5500: configuring port 0x%x\n",addr);
outb(0x20,addr);
/* release the port and return */
ioperm(addr,1,0);
return(0);
}
static long read_bi(pbi)
struct biRecord *pbi;
{
struct vmeio *pvmeio;
int bit;
short int ret_status;
int base_addr=0x7B;
int addr;
int cnt=0;
ret_status = -1;
pvmeio = (struct vmeio *)&(pbi->inp.value);
bit = pvmeio->signal % 8;
addr = base_addr + 3*(pvmeio->card - 1) + pvmeio->signal/8;
if (ioperm(addr,1,1)<0){
printf("Error cannot access DIO port/channel %i %i addr: 0x%x\n",
pvmeio->card, pvmeio->signal, addr);
} else {
ret_status = 0;
pbi->rval = getbit(inb(addr), bit);
}
ioperm(addr,1,0);
return(ret_status);
}
/*
* dev support for DIO on TS5500 card
* default it to set the first four lines as output
* the rest are inputs.
*
* No use is made of the LCD port which can provide
* addition DIO if needed.
*
* This device supports makes direct access of IO ports
* and must be run as root. A better alternative would
* be to write char driver, so the epics device
* support could work in user space.
*/
/* Modification Log:
* ---------
* ...
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/io.h>
#include <sys/ioctl.h>
#include "alarm.h"
#include "cvtTable.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "recSup.h"
#include "devSup.h"
#include "link.h"
#include "dbScan.h"
#include "epicsExport.h"
#include "boRecord.h"
static long init_record();
static long write_bo();
unsigned setbit(unsigned, int, unsigned );
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write_bo;
} devBoTS5500={
5,
NULL,
NULL,
init_record,
NULL,
write_bo
};
epicsExportAddress(dset,devBoTS5500);
static long init_record(pbo)
struct boRecord *pbo;
{
struct vmeio *pvmeio;
int base_addr = 0x7A;
int addr;
/*
* bo.inp must be an VME_IO, hijack VME_IO link field for ISA bus
* on PC104 card [card/channel/config]
*/
switch (pbo->out.type) {
case (VME_IO) :
printf("devBiTS5500: inp.type correctly set to VME_IO\n");
break;
default :
errlogPrintf("devBiTS5500 (init_record) Illegal INP field, should be VME_IO\n");
return(S_db_badField);
}
/* only need to do this once, does it hurt to do it for each record? */
pvmeio = (struct vmeio *)&(pbo->out.value);
addr = base_addr + 3*(pvmeio->card - 1);
/* configure the ports on DIO1/2: 8->11 output rest input */
if (ioperm(addr,1,1)<0){
printf("Error\n");
return(-1);
}
/* set pins8 to 11 to outputs*/
printf("devBiTS5500: configuring port 0x%x\n",addr);
outb(0x20,addr);
/* release the port and return */
ioperm(addr,1,0);
return(0);
}
static long write_bo(pbo)
struct boRecord *pbo;
{
struct vmeio *pvmeio;
int bit;
short int ret_status;
int base_addr=0x7B;
int addr;
int value;
ret_status = -1;
pvmeio = (struct vmeio *)&(pbo->out.value);
bit = pvmeio->signal % 8;
addr = base_addr + 3*(pvmeio->card - 1) + pvmeio->signal/8;
if (ioperm(addr,1,1)<0){
printf("Error cannot access DIO port/channel %i %i addr: 0x%x\n",
pvmeio->card, pvmeio->signal, addr);
} else {
ret_status = 0;
/* read the port */
value = inb(addr);
/* overwrite the appropriate bit */
value = setbit((unsigned) value, bit,(unsigned) pbo->rval);
/* pbo->rval = getbit(inb(addr), bit); */
outb(value, addr);
}
ioperm(addr,1,0);
return(ret_status);
}
This document was generated using the LaTeX2HTML translator Version 2002 (1.62)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir1774156jv4F/lyx_tmpbuf0/ts5500.tex
The translation was initiated by Arne Freyberger on 2004-09-23