Files
gitea-pages/software/regDev.html
T
2025-07-08 08:18:09 +02:00

1560 lines
51 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<meta http-equiv="author" content="Dirk Zimoch">
<style type="text/css">
<!--
body { font-family:Arial,sans-serif,espy;
background:white; margin:0; }
body,p,ul,ol,dl,div,table {font-size:12pt; }
p { line-height:120%; }
pre,code,tt { font-family:"Courier New",Courier,monospace; }
code,tt { font-size:13pt; }
h1 { font-size: 24pt; margin-top:1ex; margin-bottom:0; text-align:center; }
h2 { font-size: 18pt; margin-bottom:0.5ex; }
h3 { font-size: 14pt; margin-bottom:0.5ex; }
h4 { font-size: 13pt; margin-bottom:0.5ex; }
h5 { font-size: 12pt; margin-bottom:0.5ex; }
small { font-size: 9pt; }
code { color:#0000C0; }
li { margin-top: 2pt; margin-bottom: 2pt; }
pre { font-size: 13pt;
white-space:pre; }
.box { border:1px solid #000000;
background-color:#f4f4f4;
padding:1ex;
margin: 0 2ex 1ex 2ex; font-size:90%; }
th.PageTitle {
font-size : 14pt;
font-weight : bold; }
.menuf {
font-size:11pt;
color:#333333;
font-weight:600;
text-decoration:none;
}
.menuf:link {color:#333333;}
.menuf:visited {color:#666666;}
.menuf:hover {color:#FF0000;}
.menuf:active {color:#FF0000;}
.menus, .menus a {
font-size:10pt;
color:#333333;
text-decoration:none;
}
.menus:visited, .menus a:visited {color:#666666;}
.menus:hover, .menus a:hover {color:#FF0000;}
.menus:active, .menus a:active {color:#FF0000;}
a:hover {color:#FF0000;}
.red {color:#FF0000;}
.green {color:#008000;}
.blue {color:#0000C0;}
@media screen {
.printlayout {display:none;}
}
@media print {
.screenlayout {display:none;}
code { color:#000000; }
a:link {color:#000000;text-decoration:none}
a:visited {color:#000000;text-decoration:none}
a:hover {color:#000000;text-decoration:none}
a:active {color:#000000;text-decoration:none}
}
-->
</style>
<link rel="shortcut icon" href="http://epics.web.psi.ch/images/sls_icon.ico">
</head>
<body bgcolor="#FFFFFF">
<table cellspacing="0" cellpadding="0" border="0" width="100%">
<tr class="screenlayout">
<td bgcolor="#267ab8" colspan="5" valign="bottom" height="10">
<img
src="../images/black"
width="100%" height="1" alt=""></td>
</tr>
<tr class="screenlayout">
<td rowspan="2" colspan="2" align="center">
<a class="menuf" href="http://www.psi.ch"><img
src="../images/psi_logo" border="0"
alt="PAUL SCHERRER INSTITUT"></a></td>
<td rowspan="2"><img
src="../images/space"
width="1" height="65" alt=""></td>
<th class="PageTitle"><center>
<big>EPICS at PSI<big>
</center>
</th>
<td><img
src="../images/space"
width="10" height="1" alt=""></td>
</tr>
<tr class="screenlayout">
<th width="100%" valign="bottom" align="left">
<table border="0" cellspacing="0" cellpadding="0"><tr><td><a
href="https://www.psi.ch"
class="menuf">PSI</a></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td bgcolor="black"><img
src="../images/space"
width="1" height="1" alt=""></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td><a
href="https://www.aps.anl.gov/epics"
class="menuf">EPICS</a></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td bgcolor="black"><img
src="../images/space"
width="1" height="1" alt=""></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td><a
href="https://www.psi.ch/en/sls"
class="menuf">SLS</a></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td bgcolor="black"><img
src="../images/space"
width="1" height="1" alt=""></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td><a
href="https://www.psi.ch/en/swissfel"
class="menuf">SwissFEL</a></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td bgcolor="black"><img
src="../images/space"
width="1" height="1" alt=""></td><td><img
src="../images/space"
width="10" height="1" alt=""></td><td><a
href="https://www.psi.ch/en/protontherapy"
class="menuf">Proscan</a></td></tr></table>
</th>
</tr>
<tr class="screenlayout">
<td height="5" bgcolor="#267ab8" colspan="6" valign="bottom">
<img
src="../images/black"
width="100%" height="1" alt=""></td>
</tr>
<tr>
<td class="screenlayout" bgcolor="#e3eaf6" valign="top" align="center" width="179">
<br><a href="http://www.aps.anl.gov/epics"><img
src="../images/EPICS_small"
alt="EPICS" border=0></a><br><br>
<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td><img
src="../images/space"
width="20" height="29" alt=""></td><th width="100%" align="left"><a
href="../index.html"
class="menuf">EPICS at PSI</a>
<img
src="../images/black"
height="1" width="100%" alt=""></th></tr></table>
<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td><img
src="../images/space"
width="20" height="29" alt=""></td><th width="100%" align="left"><a
href="../software.html"
class="menuf">Software</a>
<ul style="margin:0 0 0 0; padding-left:1em;">
<li class="menus"><a href="s7plc.html"
>PLC Communication</a></li>
<li class="menus"><a href="streamdevice.html"
>StreamDevice</a></li>
<li class="menus"><a href="regDev.html"
>regDev</a></li>
<li class="menus"><a href="iocmon.html"
>IOC Monitoring</a></li>
<li class="menus"><a href="pcmon.html"
>PC Monitoring</a></li>
<li class="menus"><a href="firewire.html"
>Firewire Camera</a></li>
<li class="menus"><a href="http://dps.web.psi.ch/template.html?/psc/"
>Digital PS Control</a></li>
<li class="menus"><a href="tclioc.html"
>Tcl-IOC</a></li>
<li class="menus"><a href="iocLogBrowser.html"
>iocLogBrowser</a></li>
<li class="menus"><a href="ioc-octave.html"
>IOC-Octave</a></li>
<li class="menus"><a href="http://gfa-it.web.psi.ch/epicsSharp/"
>C# EPICS</a></li>
<li class="menus"><a href="generalTime.html"
>generalTime</a></li>
<li class="menus"><a href="GPMM.html"
>GPMM</a></li>
<li class="menus"><a href="sequencer.html"
>SNC/SEQ</a></li>
<li class="menus"><a href="caqtdm.html"
>caQtDM</a></li>
<li class="menus"><a href="CSDataQuick.html"
>CSDataQuick</a></li>
</ul>
<img
src="../images/black"
height="1" width="100%" alt=""></th></tr></table>
<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td><img
src="../images/space"
width="20" height="29" alt=""></td><th width="100%" align="left"><a
href="../training.html"
class="menuf">Training</a>
<ul style="margin:0 0 0 0; padding-left:1em;">
<li class="menus"><a href="../training/PSI-training.html"
>Courses at PSI</a></li>
<li class="menus"><a href="../training/caClientLessons.html"
>CA client lessons</a></li>
<li class="menus"><a href="../training/downloads.html"
>Downloads</a></li>
</ul>
<img
src="../images/black"
height="1" width="100%" alt=""></th></tr></table>
<form method="get" action="http://www.google.com/search">
<table border="0" cellpadding="0" cellspacing="0">
<tr><td align="center" nowrap>
<input type="search" placeholder="search" name="q" size="13" maxlength="255" value=""><br>
<input type="hidden" name="domains" value="epics.web.psi.ch">
<small>
<input type="radio" name="sitesearch" value=""> web
<input type="radio" name="sitesearch" value="epics.web.psi.ch"
checked="checked"> epics.web.psi.ch<br>
</small>
</td></tr></table>
</form><p></p><small>
Author: <a href="mailto:Dirk Zimoch &lt;dirk.zimoch&#64;psi.&#99;&#104;&gt;?subject=http://epics.web.psi.ch%2Fsoftware%2FregDev%2F">Dirk Zimoch</a><br>
Phone: +41 56 310 5182<br>
Updated: 24.08.2021<br>
<br><a
href="http://epics.web.psi.ch/software/regDev/%2Fsoftware%2FregDev%2Findex.php?forprint"><img
src="../images/printer"
border="0" alt=""></a>
<br><a
href="http://epics.web.psi.ch/software/regDev/%2Fsoftware%2FregDev%2Findex.php?forprint">Printer friendly version</a>
<br>&nbsp;</small><br><div><a href="http://validator.w3.org/check?uri=http://epics.web.psi.ch/software/regDev/index.php&amp;ss=1"><img
border="0" src="../images/valid-html401.png"
alt="[Validate HTML]" height="31" width="88"></a><a
href="http://jigsaw.w3.org/css-validator/validator?uri=http://epics.web.psi.ch/software/regDev/index.php"><img
style="border:0;width:88px;height:31px"
src="../images/vcss.png"
alt="[Validate CSS]"></a></div>
<br><img
src="../images/space"
width="179" height="1" alt=""></td>
<td width="1" bgcolor="#000000"><img
src="../images/space"
width="1" height="1" alt=""></td>
<td><img
src="../images/space"
width="10" height="1" alt=""></td>
<td valign="top" width="100%" colspan="2">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>RegDev EPICS Device Support</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
<style type="text/css">
<!--
.indent {text-indent:-4ex; margin-left:4ex; text-align:left;}
@media screen {
pre,code {color:green; }
}
@media print {
a {color:black; text-decoration:none; }
}
-->
</style>
</head>
<body>
<h1><em>RegDev</em> EPICS Device Support Documentation</h1>
<h2>Contents</h2>
<ol>
<li><a href="regDev.html#device">Device Support</a>
<ol>
<li><a href="regDev.html#stat">Connection Status (bi)</a></li>
<li><a href="regDev.html#ai">Analog Input (ai)</a></li>
<li><a href="regDev.html#ao">Analog Output (ao)</a></li>
<li><a href="regDev.html#calcout">Calculation Output (calcout)</a></li>
<li><a href="regDev.html#bi">Binary Input (bi)</a></li>
<li><a href="regDev.html#bo">Binary Outpu (bo)t</a></li>
<li><a href="regDev.html#mbbi">Multi Bit Binary Input (mbbi)</a></li>
<li><a href="regDev.html#mbbo">Multi Bit Binary Output (mbbo)</a></li>
<li><a href="regDev.html#mbbiDirect">Multi Bit Binary Input Direct (mbbiDirect)</a></li>
<li><a href="regDev.html#mbboDirect">Multi Bit Binary Output Direct (mbboDirect)</a></li>
<li><a href="regDev.html#longin">Long Input (longin)</a></li>
<li><a href="regDev.html#longout">Long Output (longout)</a></li>
<li><a href="regDev.html#int64in">Integer 64 bit Input (int64in)</a></li>
<li><a href="regDev.html#int64out">Integer 64 bit Output (int64out)</a></li>
<li><a href="regDev.html#stringin">String Input (stringin)</a></li>
<li><a href="regDev.html#stringout">String Output (stringout)</a></li>
<li><a href="regDev.html#waveform">Waveform Input (waveform)</a></li>
<li><a href="regDev.html#aai">Array Analog Input (aai)</a></li>
<li><a href="regDev.html#aao">Array Analog Output (aao)</a></li>
</ol></li>
<li><a href="regDev.html#driver">Driver Functions</a></li>
<li><a href="regDev.html#debugging">Debugging</a></li>
</ol>
<h2>Intro</h2>
<p>
In this document, I assume that the reader is familiar with EPICS, the
record concept and meanings of the fields of the standard records.
Recommended reading: EPICS Record Reference Manual.
</p>
<p>
This generic device support is intended to connect arbitrary synchronous
register based devices to EPICS in a simple and flexible way.
It can be used for rapid prototyping before a proper driver is available,
to access registers that are unsupported by the proper driver or as the
ultimate driver for any device which is "simple enough".
</p>
<p>
It is assumed that the device allocates a continuous block of registers.
The registers should be independent, that means one device action is
performed by reading or writing only one register.
Because each record connects to one register, it is not possible to lock
the device for several register accesses with this device support,
except if that task is performed in the low level driver and hidden from
<em>regDev</em>.
The user has to know the register offsets and data types when configuring
the records.
</p>
<p>
The actual hardware access is neither defined nor implemented in this
device support.
Instead, <em>regDev</em> defines an interface for low level drivers that
implement the hardware access.
Examples are <em>mmap</em> (a driver for memory mapped registers, VME or
mmap()) or <em>s7plc</em> (a driver for network communication to a Siemens
S7 PLC).
What is defined in <em>regDev</em> is the interface to the standard
records.
Thus it is not necessary to deal with record when writing a new low-level
driver.
Consequently, all <em>regDev</em> compatible devices look the same
on the record level.
The general idea is to have a simple and flexible device support interface
which is independent of the underlying hardware structure.
</p>
<p>
To be usable with <em>regDev</em>, a driver must implement some API
functions and register a device to <em>regDev</em> by name.
Each piece of <em>regDev</em> hardware must have a name that is unique on
the IOC.
Typically, the low level driver registers a device in a configuration
function called from the startup script.
</p>
<p>
In principle, a piece of hardware can have many blocks of registers
(e.g. on differenct VME address spaces).
The low level driver may handle this by mapping ranges of register offsets
to different blocks.
Or the low level driver registers each block with an individual name.
But then, <em>regDev</em> will handle them as different independent
devices.
</p>
<p>
A demo driver is included.
</p>
<p>
<a href="mailto:Dirk Zimoch <dirk.zimoch&#64;psi&#46;ch>">Dirk
Zimoch &lt;dirk.zimoch&#64;psi&#46;ch&gt;</a>, 2009.
</p>
<a name="device"></a>
<h2>Device Support</h2>
<p>
<em>RegDev</em> supports the standard record types
<a href="regDev.html#ai">ai</a>,
<a href="regDev.html#ao">ao</a>,
<a href="regDev.html#bi">bi</a>,
<a href="regDev.html#bo">bo</a>,
<a href="regDev.html#mbbi">mbbi</a>,
<a href="regDev.html#mbbo">mbbo</a>,
<a href="regDev.html#mbbiDirect">mbbiDirect</a>,
<a href="regDev.html#mbboDirect">mbboDirect</a>,
<a href="regDev.html#longin">longin</a>,
<a href="regDev.html#longout">longout</a>,
<a href="regDev.html#stringin">stringin</a>,
<a href="regDev.html#stringout">stringout</a>,
and <a href="regDev.html#waveform">waveform</a>.
From EPICS R3.14.5 on <a href="regDev.html#calcout">calcout</a> is supported.
From EPICS R3.14.12 on <a href="regDev.html#aai">aai</a> and <a href="regDev.html#aao">aao</a>
records are supported.
(These records used to be commented out in earlier EPICS versions.)
From EPICS R3.16 on <a href="regDev.html#int64in">int64in</a> and
<a href="regDev.html#int64out">int64out</a> are supported.
</p>
<p>
The <code>DTYP</code> is <code>"regDev"</code> for all record types,
independent of the low level driver.
</p>
<p>
If a record processes when the device is not connected (off, down,
unreachable), the record raises an alarm with <code>SEVR="INVALID"</code>
and <code>STAT="READ"</code> or <code>"WRITE"</code>
</p>
<p>
There is also a connection status support for <a href="regDev.html#stat">bi</a>. The
<code>DTYP</code> is <code>"regDev&nbsp;stat"</code>. This record does not
raise an alarm when the device is disconnected. It just changes to
<code>0</code> when the device disconencts and to 1 when it connects.
</p>
<p>
It is possible to use <code>SCAN="I/O Intr"</code> if the low level driver
supports it.
Input records in <code>"I/O Intr"</code> process whenever new data has
been received from the device.
If supported by the driver, output records are processed in
<code>"I/O Intr"</code> mode whenever the driver is ready to accept new
output.
This may be useful for drivers with externally triggered or periodic
output cycles.
</p>
<p class="indent">
The general form of the <code>INP</code> or <code>OUT</code> link is:<br>
<code>
"@<i>devicename</i>:<i>offset</i>:<i>initoffset</i> T=<i>type</i>
L=<i>low</i> H=<i>high</i> B=<i>bit</i> I=<i>invertmask</i>
P=<i>packing</i> U=<i>update_ms</i>"
</code>
</p>
<ul>
<li>
<code><i>devicename</i></code> is the name that a low level driver has
used to register the device.
</li>
<li>
<code><i>offset</i></code> is the address of the register relative to the
beginning of the register block for this device.
It depends on the low level driver if offset is measured in bytes
or anything else.
The offset must be a positive integer expression within the limits of
the device register block size.
It may be calculated using the operators <code>+-*()</code>.<br>
It is possible to calculate <code><i>offset</i></code> dynamically from
another record. In this case the name of the other record must be the the
first operand in the calculation. If the record name contains any of the
characters <code>:+-*()</code>, it must be single quoted.<br>
Example: <code>'otherrecord'*8+0x100</code>;<br>
If dynamic offsets exceed the limits of the register block, the record
will rais an <code>INVALID</code> alarm.
</li>
<li>
<code><i>initoffset</i></code> is optionally used by output records to
initialize from a hardware register.
If <code><i>initoffset</i></code> is not specified but the second
<code>:</code> is there, the record initializes from the normal
<code><i>offset</i></code> address.
If <code>:<i>initoffset</i></code> is skipped (including the
<code>:</code>),
the record is not initialized by <em>regDev</em> and may be initialized
for example by auto save and restore.
Records read from <code><i>initoffset</i></code> during the
<code>init_record</code> phase in unpredictable order.
</li>
<li>
<code>T=<i>type</i></code> defines the data type of the register.
See <a href="regDev.html#type">table below</a>.
The default is <code>T=int16</code> for most record types.
</li>
<li>
<code>L=<i>low</i></code> and <code>H=<i>high</i></code> are used for
linear conversion in <a href="http://epics.web.psi.ch/software/regDev/ai">ai</a> and <a href="http://epics.web.psi.ch/software/regDev/ao">ao</a> records
if <code>LINR=LINEAR</code> and in <a href="http://epics.web.psi.ch/software/regDev/waveform">waveform</a>,
<a href="http://epics.web.psi.ch/software/regDev/aai">aai</a> and <a href="http://epics.web.psi.ch/software/regDev/aao">aao</a> if <code>FTVL=FLOAT</code>
or <code>DOUBLE</code> and <code>T</code> is an integer type.
They define the raw values which correspond to <code>EGUL</code> and
<code>EGUF</code> (or <code>LOPR</code> and <code>HOPR</code> for arrays)
respectively.
Output records will never write intger values lower than
<code>L</code> or higher than <code>H</code>.
Instead the output value saturates at the nearest limit.
The default values for
<code>L</code> and <code>H</code> depend on the data type.
</li>
<li>
<code>B=<i>bit</i></code> is only used for <a href="regDev.html#bi">bi</a> and
<a href="regDev.html#bo">bo</a> records to define the bit number within the data
byte, word, or doubleword (depending on <code>T</code>).
Bit number 0 is the least significant bit.
Note that in big endian byte order (also known as motorola format) bit 0
is in the last byte, while in little endian byte order (intel format)
bit 0 is in the first byte.
If in doubt and allowed by the hardware, use <code>T=byte</code> to avoid
all byte order problems when handling single bits.
</li>
<li>
<code>I=<i>invertmask</i></code> is used to flip bits of integer values
before writing them to or after reading them from a register.
This allows to invert the logic of bits.
The default value is 0 (i.e. do not flip bits).
</li>
<li>
<code>P=<i>packing</i></code> is used for accessing arrays through
FIFO registers.
It defines how many array elements are packed in one register access.
For example <code>T=int16 P=1</code> defines a FIFO of 16 bit values
while <code>T=int16 P=2</code> is a FIFO with 32 bits width that
contains two 16 bit values in each access.
</li>
<li>
<code>U=<i>update_ms</i></code> is used to update output records
periodically. Every <code><i>update_ms</i></code> milliseconds the output
record reads back the value from the register at <code><i>initoffset</i></code>
or <code><i>offset</i></code> if <code><i>initoffset</i></code> is not specified.
</li>
</ul>
<p>
Not all parameters <code>T</code>, <code>L</code>, <code>H</code>,
<code>B</code>, <code>I</code>, and <code>P</code> are always
required.
Unused parameters or parameters equal to the default value may be omitted.
The default values depend on the data type.
All parameters are case insensitive.
</p>
<a name="type"></a>
<center>
<table border=1 cellpadding=5>
<tr>
<th>T</th>
<th>Register Data Type</th>
<th>Default L</th>
<th>Default H</th>
</tr>
<tr>
<td><tt>int8</tt>
</td><td>8 bit (1 byte) signed integer number</td>
<td><tt>-0x7f<br>-127</tt></td>
<td><tt>0x7f<br>127</tt></td>
</tr>
<tr>
<td><tt>uint8<br>unsign8<br>unsigned8<br>byte<br>char</tt>
</td><td>8 bit (1 byte) unsigned integer number</td>
<td><tt>0x00<br>0</tt></td>
<td><tt>0xff<br>255</tt></td>
</tr>
<tr>
<td><tt>int16<br>short</tt></td>
<td>16 bit (2 bytes) signed integer number</td>
<td><tt>-0x7fff<br>-32767</tt></td>
<td><tt>0x7fff<br>32767</tt></td>
</tr>
<tr>
<td><tt>uint16<br>unsign16<br>unsigned16<br>word</tt></td>
<td>16 bit (2 bytes) unsigned integer number</td>
<td><tt>0x0000<br>0</tt></td>
<td><tt>0xffff<br>65535</tt></td>
</tr>
<tr>
<td><tt>int32<br>long</tt></td>
<td>32 bit (4 bytes) signed integer number</td>
<td><tt>-0x7fffffff<br>-2147483647</tt></td>
<td><tt>0x7fffffff<br>2147483647</tt></td>
</tr>
<tr>
<td><tt>uint32<br>unsign32<br>unsigned32<br>dword</tt></td>
<td>32 bit (4 bytes) unsigned integer number</td>
<td><tt>0x00000000<br>0</tt></td>
<td><tt>0xffffffff<br>4294967295</tt></td>
</tr>
<tr>
<td><tt>int64<br>longlong</tt></td>
<td>64 bit (8 bytes) signed integer number</td>
<td><tt>-0x7fffffffffffffff<br>-9223372036854775807</tt></td>
<td><tt>0x7fffffffffffffff<br>9223372036854775807</tt></td>
</tr>
<tr>
<td><tt>uint64<br>unsign64<br>unsigned64<br>dword</tt></td>
<td>64 bit (8 bytes) unsigned integer number</td>
<td><tt>0x0000000000000000<br>0</tt></td>
<td><tt>0xffffffffffffffff<br>18446744073709551615</tt></td>
</tr>
<tr>
<td><tt>bcd8<br>bcd</tt>
</td><td>8 bit (1 byte, 2 digits) unsigned binary coded decimal</td>
<td><tt>00</tt></td>
<td><tt>99</tt></td>
</tr>
<tr>
<td><tt>bcd16</tt>
</td><td>16 bit (2 bytes, 4 digits) unsigned binary coded decimal</td>
<td><tt>0000</tt></td>
<td><tt>9999</tt></td>
</tr>
<tr>
<td><tt>bcd32</tt>
</td><td>32 bit (4 bytes, 8 digits) unsigned binary coded decimal</td>
<td><tt>00000000</tt></td>
<td><tt>99999999</tt></td>
</tr>
<tr>
<td><tt>bcd64</tt>
</td><td>64 bit (8 bytes, 16 digits) unsigned binary coded decimal</td>
<td><tt>0000000000000000</tt></td>
<td><tt>9999999999999999</tt></td>
</tr>
<tr>
<td><tt>real32<br>float32<br>float<br>single</tt></td>
<td>32 bit (4 bytes) floating point number</td>
<td>n/a</td><td>n/a</td>
</tr>
<tr>
<td><tt>real64<br>float64<br>double</tt></td>
<td>64 bit (8 bytes) floating point number</td>
<td>n/a</td><td>n/a</td>
</tr>
<tr>
<td><tt>string</tt></td>
<td>character array</td>
<td>40</td><td>n/a</td>
</tr>
</table>
</center>
<p>
If <code>T=string</code>, <code>L</code> means <i>length</i>,
not <i>low</i>.
The length includes the terminating null byte.
The default value is the length of the <code>VAL</code> field.
In the case of the stringin and stringout records, this is 40.
In case of array records (<a href="regDev.html#aai">aai</a>, <a href="regDev.html#aao">aao</a>,
<a href="regDev.html#waveform">waveform</a>) with <code>CHAR</code>
data this is the size of the array (usually <code>NELM</code>).
</p>
<p>
Note that for signed integer types, <code>L</code> is one off the smallest
possible value. This makes linear conversion symmatric on the positive
and negative side (i.e. makes sure 0 is in the middle).
As a side effect, the most negative value will never be written to the
register but will be saturated to <code>L</code>.
If that is a problem, set <code>L</code> explicitly (and maybe adjust
<code>LOPR</code>).
</p>
<h2>Example Records</h2>
<p>
In the following examples it is assumed that the low level driver supports
<code>"I/O Intr"</code> for input registers.
If that is not the case or not applicable for all registers use any other
scanning method instead.
All output records in the examples use <code>PINI=YES</code> to make sure
an initial value is written to the register. Depending on the low level driver,
this may not be necessary if the record gets initialized from an
<code><i>initoffset</i></code>.
</p>
<a name="stat"></a>
<h3>Connection Status (bi)</h3>
<pre>
record (bi, "$(RECORDNAME)") {
field (DTYP, "regDev stat")
field (INP, "@$(DEVICE)")
field (SCAN, "I/O Intr")
}
</pre>
<p>
The record value is 1 if a connection to the device is established and 0
if not.
Disconnect does not raise an alarm.
</p>
<a name="ai"></a>
<h3>Analog Input (ai)</h3>
<h4>Integer registers with linear conversion</h4>
<pre>
record (ai, "$(RECORDNAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T) L=$(L) H=$(H)")
field (SCAN, "I/O Intr")
field (LINR, "LINEAR")
field (EGUL, "$(MINVAL)")
field (EGUF, "$(MAXVAL)")
}
</pre>
<p>
If <code>T</code> is an integer type, the register is read into
<code>RVAL</code>.
If <code>LINR=LINEAR</code>, then the record
converts <code>RVAL</code> to <code>VAL</code> so that <code>L</code>
maps to <code>MINVAL</code> and <code>H</code> maps to <code>MAXVAL</code>.
</p>
<p>
Default type is <code>T=int16</code>.
Defaults for <code>L</code> and <code>H</code> depend
on <code>T</code> (see table <a href="regDev.html#type">above</a>).
</p>
<h4>Floating point registers</h4>
<p>
</p>
<pre>
record (ai, "$(RECORDNAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T)")
field (SCAN, "I/O Intr")
}
</pre>
<p>
If <code>T=float</code> or <code>T=double</code>,
the register is read directly into <code>VAL</code> and <code>L</code>,
<code>H</code>, <code>EGUL</code> and <code>EGUF</code> are ignored.
The device support emulates adjustment scaling and smoothing according to
<code>ASLO</code>, <code>AOFF</code> and <code>SMOO</code> which is done
by the record itself only when using conversion from integer registers.
</p>
<p>
<code>VAL = (<i>register</i>*ASLO+AOFF)*(1-SMOO) +
VAL<sub>old</sub>*SMOO</code>
</p>
<p>
<code>T=string</code> is not valid for ai records.
</p>
<a name="ao"></a>
<h3>Analog Output (ao)</h3>
<h4>Integer registers with linear conversion</h4>
<pre>
record (ao, "$(RECORDNAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T) L=$(L) H=$(H)")
field (LINR, "LINEAR")
field (PINI, "YES")
field (EGUL, "$(MINVAL)")
field (EGUF, "$(MAXVAL)")
}
</pre>
<p>
If <code>T</code> is an integer type, <code>RVAL</code> is written to the
register.
If <code>LINR=LINEAR</code>, then the record
support first scales the record value (to be exact <code>OVAL</code>) so
that <code>MINVAL</code> maps to <code>L</code> and <code>MAXVAL</code>
maps to <code>H</code>.
The record may then use adjustment scaling to modify <code>RVAL</code>.
However, the device support will never write any value lower than
<code>L</code> or higher than <code>H</code>.
If necessary the value will be saturated to the nearest limit.
</p>
<p>
Default type is <code>T=int16</code>.
Defaults for <code>L</code> and <code>H</code> depend
on <code>T</code> (see table <a href="regDev.html#type">above</a>).
</p>
<h4>Floating point registers</h4>
<pre>
record (ao, "$(RECORDNAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T)")
field (PINI, "YES")
}
</pre>
<p>
If <code>T=float</code> or <code>T=double</code>,
<code>OVAL</code> is written to the register. <code>L</code>,
<code>H</code>, <code>EGUL</code> and <code>EGUF</code> are ignored.
The device support emulates adjustment scaling according to
<code>AOFF</code> and <code>ASLO</code> which is done by the
record itself only when converting to integer registers.
</p>
<p>
<code><i>register</i>=(OVAL-AOFF)/ASLO</code>
</p>
<p>
<code>T=string</code> is not valid for ao records.
</p>
<a name="calcout"></a>
<h3>Calculation Output (calcout)</h3>
<pre>
record(calcout, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T) L=$(L) H=$(H)")
field (PINI, "YES")
}
</pre>
<p>
Default type is <code>T=int16</code>.
Defaults for <code>L</code> and <code>H</code> depend
on <code>T</code> (see table <a href="regDev.html#type">above</a>).
</p>
<p>
<code>OVAL</code> (the result of <code>CALC</code> or <code>OCAL</code>,
depending on <code>DOPT</code>) is written to the register. If
<code>T</code> is an integer type, the value is truncated to an
integer and compared to <code>L</code> and <code>H</code>.
If <code>OVAL</code> is lower than <code>L</code> or higher than
<code>H</code>, it is saturated to the nearest limit.
</p>
<p>
If <code>T=float</code> or <code>T=double</code>,
<code>OVAL</code> is written to the register directly without any
conversion.
</p>
<p>
<code>T=string</code> is not valid for calcout records.
</p>
<p>
To use this device support with calcout records, you need at least EPICS R3.14.5.
</p>
<a name="bi"></a>
<h3>Binary Input (bi)</h3>
<pre>
record(bi, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T) B=$(B) I=$(I)")
field (SCAN, "I/O Intr")
}
</pre>
<p>
Default type is <code>T=int16</code>.
Default bit is <code>B=0</code>.
Default invert mask is <code>I=0</code>.
</p>
<p>
Depending on <code>T</code>, <code>B</code> can vary from 0 to 7, 15,
or 31.
Bit 0 is the least significant bit. In little endian byte order, bit 0 is
in the first byte, in big endian byte order it is in the last byte of the
register.
If in doubt and allowed by the hardware, use <code>T=byte</code> to avoid
all byte order problems when handling single bits.
</p>
<p>
The register is read to <code>RVAL</code> and masked with
2<sup><code>B</code></sup>.
If <code>I!=0</code> the bit is inverted.
The record sets <code>VAL</code> to 1 if <code>RVAL</code> is not 0.
</p>
<p>
<code>RVAL=(I==0?<i>register</i>:~<i>register</i>)&(1&lt;&lt;B);
VAL=(RVAL!=0)?1:0</code>
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for bo records. Signed and unsigned types are equivalent.
</p>
<a name="bo"></a>
<h3>Binary Output (bo)</h3>
<pre>
record(bo, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T) B=<i>$(B)</i>")
field (PINI, "YES")
}
</pre>
<p>
Default type is <code>T=int16</code>. Default bit is <code>B=0</code>.
</p>
<p>
Depending on <code>T</code>, <code>B</code> can vary from 0 to 7, 15, or
31.
Bit 0 is the least significant bit.
In little endian byte order, bit 0 is in the first byte, in big endian byte
order it is in the last byte of the register.
If in doubt and allowed by the hardware, use <code>T=byte</code> to avoid
all byte order problems when handling single bits.
</p>
<p>
If <code>VAL</code> is not 0, then <code>RVAL</code> is set to
2<sup><code>B</code></sup>, else <code>RVAL</code> is set to 0.
Only the referenced bit of the register is modified while all other bits
remain unchanged.
Thus, other output records can write to different bits of the same
register.
<p>
<code>RVAL=(VAL!=0)?(1&lt;&lt;<i>B</i>):0;
<i>register</i>=(<i>register</i><sub>old</sub>&~(1&lt;&lt;<i>B</i>)) |
RVAL</code>
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for bo records. Signed and unsigned types are equivalent.
</p>
<a name="mbbi"></a>
<h3>Multi Bit Binary Input (mbbi)</h3>
<pre>
record(mbbi, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T)")
field (SCAN, "I/O Intr")
field (NOBT, "$(NUMBER_OF_BITS)")
field (SHFT, "$(RIGHT_SHIFT)")
}
</pre>
<p>
Default type is <code>T=int16</code>.
</p>
<p>
The register is read to <code>RVAL</code>, shifted right by
<code>SHFT</code> bits and masked with <code>NOBT</code> bits.
Valid values for <code>NOBT</code> and <code>SHFT</code> depend on
<code>T</code>:
<code>NOBT+SHFT</code> must not exceed the number of bits of
the type.
</p>
<p>
Bit 0 is the least significant bit.
In little endian byte order, bit 0 is in the first byte, in big endian
byte order it is in the last byte of the register.
</p>
<p>
<b>Example:</b> Use bits 4 to 9 out of 16.
<code>T=int16</code>, <code>NOBT=6</code>, <code>SHFT=4</code>
</p>
<table border=1 cellspacing=0>
<tr>
<td>register</td>
<th bgcolor="#c0c0c0" width="5%">15</th>
<th bgcolor="#c0c0c0" width="5%">14</th>
<th bgcolor="#c0c0c0" width="5%">13</th>
<th bgcolor="#c0c0c0" width="5%">12</th>
<th bgcolor="#c0c0c0" width="5%">11</th>
<th bgcolor="#c0c0c0" width="5%">10</th>
<th width="5%">9</th>
<th width="5%">8</th>
<th width="5%">7</th>
<th width="5%">6</th>
<th width="5%">5</th>
<th width="5%">4</th>
<th bgcolor="#c0c0c0" width="5%">3</th>
<th bgcolor="#c0c0c0" width="5%">2</th>
<th bgcolor="#c0c0c0" width="5%">1</th>
<th bgcolor="#c0c0c0" width="5%">0</th>
</tr>
<tr>
<td>RVAL</td>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th>9</th>
<th>8</th>
<th>7</th>
<th>6</th>
<th>5</th>
<th>4</th>
</tr>
</table>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for mbbi records. Signed and unsigned types are equivalent.
</p>
<a name="mbbo"></a>
<h3>Multi Bit Binary Output (mbbo)</h3>
<pre>
record(mbbo, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T)")
field (PINI, "YES")
field (NOBT, "$(NUMBER_OF_BITS)")
field (SHFT, "$(LEFT_SHIFT)")
}
</pre>
<p>
Default type is <code>T=int16</code>.
</p>
<p>
RVAL is masked with <code>NOBT</code> bits, shifted left by
<code>SHFT</code> bits and written to the register. Valid values for
<code>NOBT</code> and <code>SHFT</code> depend on <code>T</code>:
<code>NOBT+SHFT</code> must not exceed the number of bits of
the type.
</p>
<p>
Bit 0 is the least significant bit.
In little endian byte order, bit 0 is in the first byte, in big endian
byte order it is in the last byte of the register.
</p>
<p>
Only the referenced <code>NOBT</code> bits of the register are modified.
All other bits remain unchanged.
Thus, other output records can write to different bits of the same
register.
</p>
<p>
<b>Example:</b> Use bits 5 to 8 out of 16.
<code>T=int16</code>, <code>NOBT=4</code>, <code>SHFT=5</code>
</p>
<table border=1 cellspacing=0>
<tr>
<td>RVAL</td>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th bgcolor="#c0c0c0">&nbsp;</th>
<th>8</th>
<th>7</th>
<th>6</th>
<th>5</th>
</tr>
<tr>
<td>register</td>
<th bgcolor="#c0c0c0" width="5%">15</th>
<th bgcolor="#c0c0c0" width="5%">14</th>
<th bgcolor="#c0c0c0" width="5%">13</th>
<th bgcolor="#c0c0c0" width="5%">12</th>
<th bgcolor="#c0c0c0" width="5%">11</th>
<th bgcolor="#c0c0c0" width="5%">10</th>
<th bgcolor="#c0c0c0" width="5%">9</th>
<th width="5%">8</th>
<th width="5%">7</th>
<th width="5%">6</th>
<th width="5%">5</th>
<th bgcolor="#c0c0c0" width="5%">4</th>
<th bgcolor="#c0c0c0" width="5%">3</th>
<th bgcolor="#c0c0c0" width="5%">2</th>
<th bgcolor="#c0c0c0" width="5%">1</th>
<th bgcolor="#c0c0c0" width="5%">0</th>
</tr>
</table>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for mbbo records. Signed and unsigned types are equivalent.
</p>
<a name="mbbiDirect"></a>
<h3>Multi Bit Binary Input Direct (mbbiDirect)</h3>
<pre>
record(mbbiDirect, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T)")
field (SCAN, "I/O Intr")
field (NOBT, "$(NUMBER_OF_BITS)")
field (SHFT, "$(RIGHT_SHIFT)")
}
</pre>
<p>
Default type is <code>T=int16</code>.
</p>
<p>
The register is read to <code>VAL</code>, shifted right by
<code>SHFT</code> bits and masked with <code>NOBT</code> bits (see
<a href="regDev.html#mbbi">mbbi</a>).
Valid values for <code>NOBT</code> and <code>SHFT</code> depend on
<code>T</code>:
<code>NOBT+SHFT</code> must not exceed the number of bits of
the type.
</p>
<p>
Bit 0 is the least significant bit.
In little endian byte order, bit 0 is in the first byte, in big endian
byte order it is in the last byte of the register.
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for mbbiDirect records. Signed and unsigned types are equivalent.
</p>
<a name="mbboDirect"></a>
<h3>Multi Bit Binary Output Direct (mbboDirect)</h3>
<pre>
record(mbboDirect, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T)")
field (PINI, "YES")
field (NOBT, "$(NUMBER_OF_BITS)")
field (SHFT, "$(LEFT_SHIFT)")
}
</pre>
<p>
Default type is <code>T=int16</code>.
</p>
<p>
VAL is masked with <code>NOBT</code> bits, shifted left by
<code>SHFT</code> bits and written to the register (see
<a href="regDev.html#mbbo">mbbo</a>).
Valid values for <code>NOBT</code> and <code>SHFT</code> depend on
<code>T</code>:
<code>NOBT+SHFT</code> must not exceed the number of bits of
the type.
</p>
<p>
Bit 0 is the least significant bit.
In little endian byte order, bit 0 is in the first byte, in big endian
byte order it is in the last byte of the register.
</p>
<p>
Only the referenced <code>NOBT</code> bits of the register are modified.
All other bits remain unchanged. Thus, other output records can write to
different bits of the same register.
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for mbboDirect records. Signed and unsigned types are equivalent.
</p>
<a name="longin"></a>
<h3>Long Input (longin)</h3>
<pre>
record(longin, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T)")
field (SCAN, "I/O Intr")
}
</pre>
<p>
Default type is <code>T=int16</code>.
</p>
<p>
The register is read to <code>VAL</code>. If the type has less than 32
bits, the value is zero extended or sign extended depending on the
signedness of the type.
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for longin records.
</p>
<a name="longout"></a>
<h3>Long Output (longout)</h3>
<pre>
record(longout, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T)")
field (PINI, "YES")
}
</pre>
<p>
Default type is <code>T=int16</code>.
</p>
<p>
Depending on <code>T</code>, the least significant 8, 16, or 32 bits
of <code>VAL</code> are written to the register.
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for longout records.
</p>
<a name="int64in"></a>
<h3>Integer 64 Input (int64in)</h3>
<pre>
record(int64in, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) T=$(T)")
field (SCAN, "I/O Intr")
}
</pre>
<p>
Default type is <code>T=int64</code>.
</p>
<p>
The register is read to <code>VAL</code>. If the type has less than 64
bits, the value is zero extended or sign extended depending on the
signedness of the type.
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for int64in records.
</p>
<p>
To use int64in records, you need at least EPICS R3.16.
</p>
<a name="int64out"></a>
<h3>Integer 64 Output (int64out)</h3>
<pre>
record(int64out, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) T=$(T)")
field (PINI, "YES")
}
</pre>
<p>
Default type is <code>T=int64</code>.
</p>
<p>
Depending on <code>T</code>, the least significant 8, 16, 32, or 64 bits
of <code>VAL</code> are written to the register.
</p>
<p>
<code>T=string</code>, <code>T=float</code> or <code>T=double</code> are
not valid for int64out records.
</p>
<p>
To use int64out records, you need at least EPICS R3.16.
</p>
<a name="stringin"></a>
<h3>String Input (stringin)</h3>
<pre>
record(stringin, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET) L=$(LENGTH)")
field (SCAN, "I/O Intr")
}
</pre>
<p>
Default and only valid type is <code>T=string</code>.
Default length is <code>L=40</code>.
</p>
<p>
<code>L</code> bytes are read from the register to <code>VAL</code> and
null terminated. Thus, the effective string length is maximal
<code>L</code>-1 bytes.
</p>
<a name="stringout"></a>
<h3>String Output (stringout)</h3>
<pre>
record(stringout, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET) L=$(LENGTH)")
field (PINI, "YES")
}
</pre>
<p>
Default and only valid type is <code>T=string</code>.
Default length is <code>L=40</code>.
</p>
<p>
<code>L</code> bytes are written from <code>VAL</code> to the register.
If the actual string length of <code>VAL</code> is shorter than
<code>L</code>, the remaining space is filled with null bytes. If
it is longer than <code>L</code>, the string is truncated and not
null terminated.
</p>
<a name="waveform"></a>
<h3>Waveform Input (waveform)</h3>
<pre>
record(waveform, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET)")
field (SCAN, "I/O Intr")
field (NELM, "$(NUMBER_OF_ELEMENTS)")
field (FTVL, "$(DATATYPE)")
}
</pre>
<p>
<code>NELM</code> elements are read from registers to <code>VAL</code>.
</p>
<p>
The default type <code>T</code> depends on <code>FTVL</code>.
For example <code>FTVL=LONG</code> results in <code>T=INT32</code>.
</p>
<p>
If <code>T</code> is an integer type, e.g. <code>T=INT32</code> but
<code>FTVL</code> is either <code>FLOAT</code> or <code>DOUBLE</code>,
then values are scaled so that <code>L=<i>low</i></code> and
<code>H=<i>high</i></code> map to the fields <code>LOPR</code> and
<code>HOPR</code> respectively.
</p>
<p>
If <code>T=string</code> then <code>FTVL</code> must be <code>CHAR</code> or
<code>UCHAR</code> or <code>STRING</code>.
For <code>CHAR</code> and <code>UCHAR</code>,
<code>L=<i>length</i></code> can be specified but
defaults to <code>NELM</code> and should not exceed it.
If <code>L</code> is less than <code>NELM</code>, the
remaining elements are left unchanged.
For <code>STRING</code>, <code>NELM</code> strings, each of length <code>L</code>
(default 40), are read.
</p>
<a name="aai"></a>
<h3>Array Analog Input (aai)</h3>
<pre>
record(aai, "$(NAME)") {
field (DTYP, "regDev")
field (INP, "@$(DEVICE):$(OFFSET)")
field (SCAN, "I/O Intr")
field (NELM, "$(NUMBER_OF_ELEMENTS)")
field (FTVL, "$(DATATYPE)")
}
</pre>
<p>
<code>NELM</code> elements are read from registers to <code>VAL</code>.
</p>
<p>
The default type <code>T</code> depends on <code>FTVL</code>.
For example <code>FTVL=LONG</code> results in <code>T=INT32</code>.
</p>
<p>
If <code>T</code> is an integer type, e.g. <code>T=INT32</code> but
<code>FTVL</code> is either <code>FLOAT</code> or <code>DOUBLE</code>,
then values are scaled so that <code>L=<i>low</i></code> and
<code>H=<i>high</i></code> map to the fields <code>LOPR</code> and
<code>HOPR</code> respectively.
</p>
<p>
If <code>T=string</code> then <code>FTVL</code> must be <code>CHAR</code> or
<code>UCHAR</code> or <code>STRING</code>.
For <code>CHAR</code> and <code>UCHAR</code>,
<code>L=<i>length</i></code> can be specified but
defaults to <code>NELM</code> and should not exceed it.
If <code>L</code> is less than <code>NELM</code>, the
remaining elements are left unchanged.
For <code>STRING</code>, <code>NELM</code> strings, each of length <code>L</code>
(default 40), are read.
</p>
<p>
The aai record is similar to the waveform record, but may be more
efficient, because the low level driver may use DMA to fill the record.
Aai records must be enabled in EPICS base, which is not the case by
default before EPICS version 3.14.12.
</p>
<a name="aao"></a>
<h3>Array Analog Output (aao)</h3>
<pre>
record(aai, "$(NAME)") {
field (DTYP, "regDev")
field (OUT, "@$(DEVICE):$(OFFSET)")
field (NELM, "$(NUMBER_OF_ELEMENTS)")
field (FTVL, "$(DATATYPE)")
field (PINI, "YES")
}
</pre>
<p>
<code>NELM</code> elements of <code>VAL</code> are written to
registers.
</p>
<p>
The default type <code>T</code> depends on <code>FTVL</code>.
For example <code>FTVL=LONG</code> results in <code>T=INT32</code>.
</p>
<p>
If <code>T</code> is an integer type, e.g. <code>T=INT32</code> but
<code>FTVL</code> is either <code>FLOAT</code> or <code>DOUBLE</code>,
then values are scaled so that fields <code>LOPR</code> and
<code>HOPR</code> map to <code>L=<i>low</i></code> and
<code>H=<i>high</i></code> respectively.
However, the device support will never write any value lower than
<code>L</code> or higher than <code>H</code>.
If necessary the value will be saturated to the nearest limit.
</p>
<p>
If <code>T=string</code> then <code>FTVL</code> must be <code>CHAR</code> or
<code>UCHAR</code> or <code>STRING</code>.
For <code>CHAR</code> and <code>UCHAR</code>,
<code>L=<i>length</i></code> can be specified but
defaults to <code>NELM</code> and should not exceed it.
If <code>L</code> is less than <code>NELM</code>, the
remaining elements are ignored.
For <code>STRING</code>, <code>NELM</code> strings, each of length <code>L</code>
(default 40), are written.
</p>
<p>
The aai record is similar to the waveform record, but may be more
efficient, because the low level driver may use DMA to fill the record.
Aai records must be enabled in EPICS base, which is not the case by
default before EPICS version 3.14.12.
</p>
<a name="driver"></a>
<h2>Driver Functions</h2>
<h3>Registration</h3>
<p>
A driver must implement the API functions it wants to support and
fill the function pointers into a <code>regDevSupport</code> structure.
It contains the functions <code>report</code>, <code>getInScanPvt</code>,
<code>getOutScanPvt</code>, <code>read</code>, and <code>write</code>.
Write <code>NULL</code> for any API function that is not implemented by
the driver.
The support structure can be global and static like this:
</p>
<p class="indent">
<pre>
static regDevSupport support = {
report,
getInScanPvt,
getOutScanPvt,
read,
write
};
</pre>
</p>
<p>
Each configured device must be registered with the following function:
</p>
<p class="indent">
<code>
int&nbsp;regDevRegisterDevice (const&nbsp;char*&nbsp;devicename,
const&nbsp;regDevSupport*&nbsp;support,
regDevice*&nbsp;device);
</code>
</p>
<p>
The <code>devicename</code> must be unique on the IOC and is used in the
record links to reference the device.
The <code>support</code> parameter is a pointer to the
<code>regDevSupport</code> structure of this driver.
The last parameter <code>driver</code> is a pointer to a driver private
<code>regDevice</code> structure.
It will be passed as a device handle to any API function.
The driver can freely define <code>struct&nbsp;regDevice</code> and put
in any information it needs to operate one device.
</p>
<h3>API functions</h3>
<p>
The driver can assume that all API functions are called in a thread save
context.
That means it will never happen that two API functions are called for the
same device at the same time.
However, API functions for different devices may be called at the same
time.
</p>
<p class="indent">
<code>
void&nbsp;report (regDevice*&nbsp;<em>device</em>,
int&nbsp;<em>level</em>);
</code>
</p>
<p class="indent">
<code>
IOSCANPVT&nbsp;getInScanPvt (regDevice*&nbsp;<em>device</em>,
size_t&nbsp;<em>offset</em>,
unsigned&nbsp;int&nbsp;<em>vec</em>,
const&nbsp;char&nbsp;*<em>user</em>);
</code>
</p>
<p class="indent">
<code>
IOSCANPVT&nbsp;getOutScanPvt (regDevice*&nbsp;<em>device</em>,
size_t&nbsp;<em>offset</em>,
unsigned&nbsp;int&nbsp;<em>vec</em>,
const&nbsp;char&nbsp;*<em>user</em>);
</code>
</p>
<p class="indent">
<code>
int&nbsp;read (regDevice*&nbsp;<em>device</em>,
size_t&nbsp;<em>offset</em>,
unsigned&nbsp;int&nbsp;<em>datalength</em>,
size_t&nbsp;<em>nelem</em>,
void*&nbsp;<em>pdata</em>,
int&nbsp;<em>priority</em>,
regDevTransferComplete&nbsp;<em>callback</em>,
const&nbsp;char&nbsp;*<em>user</em>);
</code>
</p>
<p class="indent">
<code>
int&nbsp; write (regDevice*&nbsp;<em>device</em>,
size_t&nbsp;<em>offset</em>,
unsigned&nbsp;int&nbsp;<em>datalength</em>,
size_t&nbsp;<em>nelem</em>,
void*&nbsp;<em>pdata</em>,
void*&nbsp;<em>pmask</em>,
int&nbsp;<em>priority</em>,
regDevTransferComplete&nbsp;<em>callback</em>,
const&nbsp;char&nbsp;*<em>user</em>);
</code>
</p>
<ul>
<li>
<code><em>offset</em></code> is the address offset of the register
relative to the beginning to the register block.
</li>
<li>
<code><em>datalength</em></code> is the length of the register in bytes
(one element in case of arrays).
</li>
<li>
<code><em>nelem</em></code> is the number of elements in an array.
It is 1 for scalar values but may be larger for arrays or strings.
It may also be 0. In that case the driver shall just report the connection
state by returning <code>SUCCESS</code> or an error code.
</li>
<li>
<code><em>pdata</em></code> is a pointer to a buffer of
<code><em>nelem</em></code>*<code><em>dlen</em></code> bytes.
registers values are copied to or from this buffer.
If <code><em>nelem==0</em></code>, <code><em>pdata</em></code> may be
<code>NULL</code>.
</li>
<li>
<code><em>mask</em></code> is a pointer to a bit mask of
<code><em>datalength</em></code> bytes or <code>NULL</code>.
Only those bits shall be modified where the mask contains 1 bits.
All other bits shall remain unchanged.
</li>
<li>
<code><em>priority</em></code> is a number 0...2 and may be used
as a hint for the driver to shedule requests.
</li>
<li>
<code><em>callback</em></code> is a function to be called upon request
completion if the driver decides to handle the request asynchronously
and returns <code>ASYNC_COMPLETION</code>. The driver may ignore it
completely and handle all requests synchronously. The function may be
<code>NULL</code>. In that case the driver must handle the request
synchroously and not return <code>ASYNC_COMPLETION</code>.
in wh
</li>
<li>
<code><em>user</em></code> is a string which can be used for messages.
It is the record name (actually a pointer to the record itself). If
the driver decides to use the <em>callback</em>, this pointer must be
passed to the callback function.
</li>
</ul>
<p>
Strings are handled as arrays of characters:
<code><em>datalength</em>=1</code> and <code><em>nelem</em></code> is the
buffer size including space for the terminating null byte.
</p>
<a name="debugging"></a>
<h2>1 Debugging</h2>
<p>
The variable <code>regDevDebug</code> can be set in the startup script or
at any time on the command line to change the amount or debug output.
The following levels are supported:
</p>
<p>
-1:&nbsp;&nbsp;fatal errors only<br>
&nbsp;0:&nbsp;&nbsp;errors only<br>
&nbsp;1:&nbsp;&nbsp;startup messages<br>
&nbsp;2:+ output record processing<br>
&nbsp;3:+ input record processing<br>
&nbsp;4:+ driver calls<br>
&nbsp;5:+ io printout<br>
</p>
<p>
Be careful using level&gt;1 because many messages can introduce
considerable delays which may result in connection losses.
Default level is 0.
</p>
<p>
On vxWorks, <code>regDevDebug</code> can be set with
<code>regDevDebug=<i>level</i></code>
</p>
<p>
In the iocsh use
<code>var regDevDebug <i>level</i></code>
</p>
<hr>
<small>Dirk Zimoch, 2009</small>
</body>
</html>
<hr noshade size=1><small>
<span class="printlayout">Author:&nbsp;Dirk Zimoch&nbsp;&nbsp; Phone:&nbsp;+41&nbsp;56&nbsp;310&nbsp;5182&nbsp;&nbsp; Email:&nbsp;dirk.zimoch&#64;psi.&#99;&#104;&nbsp;&nbsp; Updated:&nbsp;24.08.2021&nbsp;&nbsp; </span>
Source:&nbsp;/afs/psi.ch/project/epics/webhosting/software/regDev/index.php&nbsp;&nbsp;
</small>
</td></tr></table>
</body></html>