computer aided control of the rotary inverted...

23
LAWS OF SAINT VINCENT AND THE GRENADINES Fisheries REVISED EDITION 1990 CHAPTER 52 FISHERIES ACT Act 8 of 1986 amended by Act '32 of 1986 Act 25 of 1989 [CAP. 52 1 . Printed and published with the authority of the Government of Saint Vincent and the Grenadines

Upload: buique

Post on 08-Jul-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

Computer aided control of the rotary invertedpendulum

A. Dubov A. Falkovich

Supervisor: Mark Moulin

Abstract

The computer assisted control and design of the automatic systems is one of the most importantaspects in modern control theory. Here we are proud to present our modest excursion into therealm of the computer aided control. The effort consisted of the several parts. Between them:causing system to work, investigation of the user interface and design of the viable controller.The results were not exciting, but the numerous lessons we learned can serve as an aid to theforthcoming researcher.

Contents

1 Mechanics and Electronics of thePlant 1-11.1 General considerations . . . . . . . 1-11.2 Insight into mechanical problems . 1-21.3 Electronics of the control loop . . . 1-41.4 Insights to the future modification

of the control loop electronics . . . 1-5

2 The software side of the problem 2-12.1 Top-level considerations . . . . . . 2-12.2 Proof of concept design . . . . . . . 2-2

2.2.1 Basics . . . . . . . . . . . . . 2-2

2.2.2 Driver side . . . . . . . . . . 2-62.2.3 Client side . . . . . . . . . . 2-142.2.4 Going C . . . . . . . . . . . 2-22

2.3 An accomplished system . . . . . . 2-452.3.1 General description . . . . . 2-452.3.2 Linux kernel interface . . . 2-472.3.3 Simpler card subsystems . . 2-562.3.4 ADC subsystem . . . . . . . 2-622.3.5 Client software - the con-

troller . . . . . . . . . . . . . 2-772.4 Conclusions . . . . . . . . . . . . . 2-82

Chapter 1

Mechanics and Electronics of the Plant

1.1 General considerations

The rotary inverted pendulum1 serves as an ideal plant for various investigations in the field ofmodern control. It has an essentially non-linear behaviour, but still can serve as a testbench for awide spectrum of linearization-based techniques, such as polynomial or state-space observer basedcontrollers. The non-linearities, present in this plant, are well-behaved and “soft” (in other wordsthey are free of sharp discontinuities). The locations and nature of equilibrium points are well-known and easy to analyze, and, most important of all, there is a well-formed tendency of the plantto sustain harmonic oscillations, thus allowing us to use a classical non-linear approaches, such asSIDF2-referenced linear control.

Consequently, the aforementioned plant was subjected to frequent use in the educational areasof Control Systems Engineering. Few numerical models were developed to simulate the dynamicsof the plant within high level development environments, such as Mathwork’s Simulinkr, etc. Butobviously, a real plant obeys somewhat more complex laws, than we can account in simulation for.

The mathematical description of the theoretical plant (the one without frictional forces andmeasurement problems) can be developed from the conservation of energy and conservation of an-gular momentum principles, yielding a system of four non-linear equations (if there is a need toestimate the angles of base and auxiliary arms; see figure 1.1). Such a system can be easily mod-eled for computer analysis. Then a variety of controllers can be found to achieve a control objective.An LQ-type of controller is frequently used, because the linear controllers are not robust enoughto handle sufficiently complex non-linear models. Another approach is to use adaptive, switchedor continuous architecture linear controller. Continuous tuning of the filter constants can, theoret-ically, lead to the better system performance, but it is extremely difficult to find a good adaptationrule for such a controller. Switching between several fixed values of filter constants is much easierto achieve, because the plant can be piecewise linearized for any set of trajectory regions (the planthas smooth phase trajectory and all high-order derivatives exist). Switched constants design is vi-able method, but it is very difficult to achieve a sufficient performance level using it. In our casethe problem was even worse, because in our plant (unlike pendulums that are used in most theo-retical and practical studies of the problem) the auxiliary arm is not allowed to rotate freely, butrestricted to the sector of (approximately) 40. The retaining barriers introduced to limit the armmotion interact elasticly with the arm, thus acting as a hard non-linearity (sign change functionintroduced upon angular speed parameter) superimposed with parasite vibrational modes (in our

1Referred as a “plant” in the following text2Sinusoidal input describing function

1-1

very long auxiliary arm); they also contribute to back-lashes in the flexible joints (we will addressthis issues later). All these make the adaptive linear controller unsuitable for our model.

Another difficult problem is a measurement of position. Our use of primitive sensors causedmany problems, contributing to the parasite non-linearities and degrading the control loop perfor-mance by adding delays and noise. Because of somewhat naive design of the feedback loop, thesignal to noise ratio of the controller was obviously insufficient. The use of full-featured, time-sharing OS (GNU/Linux) to run the controller, also contributed to complexity, but those shouldbe resolved anyway, because there is growing interest in real time control provided by ordinaryworkstation, instead of dedicated system. Recent developments (namely, RTLinux environment)in the field of real time tasking support for GNU/Linux are promising enough to allow good resultsto be obtained.

1.2 Insight into mechanical problems

Figure 1.1 hides many important aspects of theFigure 1.1: Schematics of the inverted pen-dulum: ψ stands for base angle and φ standsfor arm (auxiliary) one

real pendulum. As such it can be readily modeledby means of some sort of dynamic model of the formJ ·D(ψ, φ) = T , where T stands for applied torque,J is a matrix gain multiplier with proper dimen-sions and D is a place-holder for a common lineariza-tion operator providing different derivatives of ψand φ. Such a notation accounts for any linear modelthat is derived from it’s non-linear counterpart bymeans of first-order linearization. Because of re-taining barriers, limiting the arm angle, we can notfind a linear model of the plant upon all range ofthe angles, but only in the close vicinity of the up-per equilibrium point. Near this point an acceler-ation of the arm tip equals tangential base accel-eration, opposed in sign and linearly scaled. Be-cause of near uniform mass distribution upon anarm’s length, we cannot compute the accelerationgain for the tip only, but have to account for totalinertia of the arm (integrating the contributions ofthe finite mass elements at the number of points)and this demands great amount of effort (to obtaina reliable model). The evaluation of the tangentialacceleration of base also presents a problem. Thecontrol loop is closed by a torque providing DC mo-tor (ordinary type, carbon brushes) connected via

servo-amplifier to the digital-to-analog converter of the data acquisition board. This allows usto precisely set an armature voltage of the motor (disregarding the cases when amplifier over-load/saturation will prevent us from doing so), but the armature current can only be guessed.Despite the fact that main position feedback is obtained from angular position sensor, the absenceof the data on armature current (and torque) sabotages the evaluation of dynamic performance ofthe motor. This is the main reason for the lack of closed-form model.

There is also another class of the problems found in our pendulum. These are explicit non-

1-2

linearities provided by flexible axle joints and badly designed connections. All axle segments (mo-tor and sensor feeds) are connected by means of flexible joints (see figure 1.2) that are attachedto the hardened steel axles by means of soft copper bolts. Taking into account the big mass of thearm (more than 1kg) and sufficiently high power of the driving motor (more than 50W, that is stillinadequate for the stable tracking; it will be necessary to upgrade the plant with the higher powermotor, wishfully one with a 200W power output) violent jerks can occur, causing axle misalignmenton the different sides of the joint, because of inertia of the axle load (there is not enough frictionbetween copper bolt and steel axle). This can be fixed by etching the axle in the proper places toprovide a firm hold for the bolts, but we had no instruments to do such a task (axles were madefrom extremely hard steel). We had four problematic connections in our plant: arm to its axle(direct), arm position pick-up sensor to arm’s axle (via flexible joint; apparently sensor - full circlecarbon sliding - provided enough friction to cause a displacement), arm mounting to base/motoraxle (direct; this connection did the hardest work) and motor axle to base position sensor (viaflexible joint).

Designed to provide coupling betweenFigure 1.2: Flexible joint is an interesting example ofnon-linear transducer

nearly coaxial axle segments, flexible jointreliefs us from the need to exactly alignthe axle segments. It works fine when theonly need is to forward simple rotationalmotion through the joint. But when themain objective is to measure the rotationaltrajectory of the axle, the flexible joint ap-pears to be a non-linear transducer, mucha like passive, first order low pass filter.Some of the effects encountered with thejoint are:• Dead zone occurs when changes in

the angular position are too small inamplitude (thus preventing sufficientbending) or too fast, falling beyondthe cut-off frequency of the joint’s re-sponse (joint acts as capacitive load).

• Variable gain is a generalization of previous item. Because of not ideal frequency responseof the transducer, the relation between input and output amplitudes is dependent on theirvalues and frequencies.

• Variable phase lag is another inherent property of the filter. The spring within the jointacts as a mechanical capacitor, causing the time delay in the angular position propagationthrough the joint.

We found that in our case the contribution of the joints to the overall behavior was too big to bediscarded. From the other side, there was no way to predict, exactly, the filter parameters, becauseloose mounting of the axles caused the continuous variations in the bending angle, thus modifyingthe filter constants of the transducer.

All the aforementioned problems defied the development of the model, sufficient for the model-referenced controller, making fuzzy controller to be one of the few remaining alternatives.

1-3

1.3 Electronics of the control loop

The mechanics of the plant are integrated into the control loop by means of the interface electron-ics. These consists from the two parts: external (analog) electronics and computer-attached dataacquisition card. We used basic and old card - Data Translation DT2811. It will be useful to reviewit hardware features (the system interface will we reviewed in the next chapter).

The analog part of the card features one 12bit ADC (manufactured by Analog Devices) andtwo 16bit DACs. Each DAC has separate data port from the system side and current to voltageconverter/amplifier attached to the output port. The amplifier can be configured to provide groundto +5V , ground to ±2.5V and ground to ±5V modes of operation by means of jumpers in the cardinstallation time. We used ±5V scale in conjunction with 60W, ×3.2 voltage amplifier to drive apendulum’s motor, by means of one of the DACs. DAC’s conversion time is less then 10µs, so it isnot an issue.

The card’s ADC incorporates most of the sup-

Arm positiondependent

-Vcc

G r o u n d +Vcc

R s

R o 1

R o 2

R a 1

R a 2

J 1

S igna l p i ck -up

Figure 1.3: Mechanical and electrical equiva-lents of the arm position sensor

port electronics on die. It is equipped with the16-to-1 analog multiplexor, that can also be con-figured to work in the PAIRWISE 16-to-2 modefor differential measurements. In the other words,card can provide 16 ground referenced or 8 differ-ential ADC channels via this multiplexor. The de-sired mode (ground ref. of differential) must be set-up in the card installation time. Another usefulfeature of the ADC chip is the variable gain ampli-fier, that can be digitally (CPU) controlled , to pro-vide four different discrete gains (×1, ×2, ×4 and×8). ADC is capable of 20 KSpS3, so the usage ofmore than one channel will cause severe degrada-tion in the ADC productivity (multiplexor switch-ing time, amplifier adjust, etc.), allowing no morethan 7KSpS per channel when two channels are inuse (such as in our case).

Beside the listed features, the card has somemore, referred in the next chapter. We did notused them, because of the obvious lack of need.On the other hand, card lacks such vital functionssuch as I/O bufferization and flow control, that arepresently present in the any US$30 PC sound card.We did not used sound card because of it inade-quate low frequency response (DC rejection), butthe option certainly worth mentioning. The last in-teresting thing about the DT2811 is the fact thatthe card design remained unchanged for a last fif-teen years (the card was discontinued from mar-keting only recently).

As I already mentioned, we were forced to use 360 rotational potentiometers for the angularposition pick-up (figure 1.3). The base angle measurements did not presented much trouble, so theforthcoming description will be related to the arm angle pick-up.

In our pendulum, the arm swing is only about 30, allowing us to use only 1/12 of the total scale.3Kilo Samples per Second

1-4

The potentiometer is also badly mounted, so the center of arm’s trajectory is not coincide withthe center of the resistance scale. To make problem even worse, the potentiometer can slightlychange it alignment to the arm axle, because of the faulty axle joint. Because the joint problemcame out only in the advanced stages of the project, in the original design we dealt only with staticalignment problem.

The common way to tackle an alignment problem is to use resistor bridge. The rheostat inthe auxiliary branch of the bridge (Ra2 on the figure 1.3) can be used to adjust the zero point ofthe bridge, to compensate for the misalignment of the measuring potentiometer. Because of theconstruction problems, we wanted to avoid a bipolar operation needed by bridge (our card couldwork entirely in the bipolar or ground reference mode; there was not possible to configure only onechannel to work in the bipolar mode and we were in need for ground referenced operation). Thuswe decided to short-circuit the branches. Such a solution still provides a decent way to cancel theunwanted bias in the measuring potentiometer, while avoiding the need in the bipolar connection.

1.4 Insights to the future modification of the control loopelectronics

We complained a lot about various problems encountered by us. But what are the alternativesavailable to improve the situations? The answer is to decrease the measurement errors by meansof sophisticated electronics.

On the presented schematic (figure 1.44) one possible arrangement is presented. The primaryconcept beyond it is a direct digital evaluation of the potentiometer position. Let us to providesome explanations about the circuit.

The measuring potentiometer (Ro1 + Rs + Ro2) serves as current setting resistor for the inte-grator implemented by operational amplifier U1B and capacitor C1. All operational amplifiers arechosen to be of the type LF347, fast slew rate, high bandwidth (4MHz gain-bandwidth product),high impedance (JFET input stage) amplifier. The direction of charging current within integratoris commutated by means of two transistors, Q1 and Q2 (2N2222A and 2N2907A NPN/PNP com-plimentary matching transistors). These transistors serve as switches, controlled by the Schmidttrigger (U1A), that is driven by the integrator output. Given the full-scale resistance of the mea-suring potentiometer to be 2kΩ, the output of the Schmidt trigger produces the square wave of thenearly 1kHz frequency with variable duty cycle. Thus the sampling rate of the system is 1kHz, andthe potentiometer position is encoded by means of duty cycle.

The sampling frequency is derived from the sensor unit by means of the C2 - R7 differentiatorand then formed to the pulse train with an aid of diode D1 and fast CMOS Schmidt trigger (U2A- 74ACT14 or 74VHC14). This pulse train is used to synchronize the auxiliary pulse width mod-ulator composed from U1C and U1D operational amplifiers, discharge transistor Q3 (2N2907A),timing capacitor C3 and resistors R9, R10, R11 and R12. The frequency of this oscillator is manuallypreadjusted with the R12 tuning rheostat and synchronized to the sensor oscillator. The duty cycleof the auxiliary (reference) oscillator is controlled by the voltage at J1 port. This is normally pro-vided by the computer’s D/A converter. It is needed to dynamically readjust the center point of thesensor amplifier, to allow better resolution and dynamic range of measurement.

The outputs of both oscillators are feed to the difference extractor (U3A - fast XOR gate) andto the Lag/Lead detection logic (C4, R14 differentiator, U4A NAND gate and U8A fast acting one-shot). The output of U8A is directly connected to the binary interface latch and serves as reference

4All component values are given in first approximation; additional component tuning may be needed.

1-5

oscillator Lead/Lag bit. The difference pulse is further sampled by the high speed gate (gatingspeed of 57MHz is crystal generated by U4C - Y1) and the gate pulses are counted by the 12bitbinary counter U5 (74VHC4040). The special pulse shaping apparatus is available to providelatch and counter control signals (elements U2[D,E,F] and U9[A,B,C]) .

A

A

B

B

C

C

D

D

E

E

4 4

3 3

2 2

1 1

Sensor dataconverter(fixedfrequency PWMmodulator)

100 ns

Sensor data

Angular potentiometer

Referenceoscillatortuneradjust(manual)

Reference dutycycle set-up(microcomputercontrolled)

* 74[ACT][VHC]14 - fast cmos Schmidt trigger

50 ns

Samplingfrequencygenerator

500 us

Lag/lead indicator (H -sensor leads, L -reference leads)

50 ns

50 ns

Pulse shaping train(latch enable + counterreset)

High frequency oscillator

Reference oscillator

Counter

Latches

Phase comparator

A1 1

A d v a n c e d a n g u l a r p o t e n t iometer pos i t ion p ick-up sys tem

A3

1 1Tuesday , May 16, 2000

A l e x D u b o v & Alex Fa lkov ich Des ign

Title

Size Document Number Rev

Date: Sheet of

Output enable

+5V

-5V

+5V

-5V

+5V

Ro1

Rs

R1

50k

R2

50k

R6

10M

U1B

LF347

+

-

5

67

411

R5

50k

Ro2

Q2

2N2222A

3

2

1

C1

0.5u

U1A

LF347

+

-

3

21

411

R320k

R4

20k

Q1

2N2907A

3

2

1

C2

R7

D1

R8220

D2

U2A

74ACT14

12

U2B

74ACT14

3 4

R9

50k

Q3

2N2907A 3

2

1

R11

50

R10

10M

C3

0.5u

R12

Ro1+Rs+Ro2

U1C

LF347

+

-

10

98

411

U1D

LF347

+

-

12

1314

411

J1

R13220

D3

U2C

74ACT14

5 6

U3A

74ACT86

1

23

U4A

74ACT00

1

23

D4R14

C4

U4B

74ACT00

4

56

U4C

74ACT00

9

108

Y1

57MHz

R15

1M

R16220

C5

10p

C6

10p

U5

74VHC4040

CLK10

RST11

Q19

Q27

Q36

Q45

Q53

Q62

Q74

Q813

Q912

Q1014

Q1115

Q121

U7 74ACT373

D03

D14

D27

D38

D413

D514

D617

D718

OC1

G11

Q02

Q15

Q26

Q39

Q412

Q515

Q616

Q719

U6 74ACT373

D03

D14

D27

D38

D413

D514

D617

D718

OC1

G11

Q02

Q15

Q26

Q39

Q412

Q515

Q616

Q719

U2D

74ACT14

98

U9C74ACT14

56

U8A

74VHC123

CEXT14

REXT/CEXT15

A1

B2

CLR3

Q13

Q4

C7

R17

D5

C8

R18

D6

U9A

74ACT14

1 2

U9B

74ACT14

3 4

C9

R19

D7

U2E

74ACT14

1110

U2F

74ACT14

13 12

Microcomputer interface

Figure 1.4: The much improved way to measure the displacement of the position pick-up poten-tiometer (please view this figure with ×3 - ×4 magnification; it prints well on any device capableof more than 300dpi rasterization resolution)

The proposed arrangement can be made to be much more precise than the direct voltage mea-surement methods. It is a common industrial practice to use a similar circuits in measurement ofvarying resistances. The good example is an Apple/Macintoshr joystick controller, that is some-what simpler in design, because it has no need to maintain constant sampling frequency. In ourcase, the dynamic trajectory of the arm has to be reliably measured, thus demanding the fixedsampling rate.

Another interesting technique to use in the base trajectory evaluation is a tachometric method.This method is widely used in the continuously rotating DC motor assemblies, but may have aninteresting applications to the positioning systems. The idea behind the method is a fact thatin multi-pole DC motor the armature current is dependent on the shaft angle, because of the

1-6

changes in the magnetic flux caused by the changing magnetic cross-section of the stator gap.Because of the rotational symmetry of the rotor, the dependence is not one to one, but in caseof the common three-pole motor, there are three flux minimas spaced by 120. The shape of theminimas resembles the smooth Gaussian curves, so they can be used to reduce the complexity ofthe base positioning system. The loop closed between motor armature current and voltage withan aid of the first order filter or some simple fuzzy controller can then act as an independent baseposition controller, in order to reduce complexity of the main control loop. The obvious drawbackof this method is that there are only few discrete positions for the base to reside, when there is aninevitable desire to have an ability of positioning the base into the any of possible positions (nearly340 achievable with the measuring potentiometer).

The mechanical redesign of the plant will allow the optical position sensor (of the type withmulti-slot interrupter ring). It can be used easily for the base position evaluation, where the needfor high resolution is absent and deflection angles are spanning over the whole range. To measurethe arm angle with such device, the torque reducing (speed multiplying) gear arrangement will beneeded, to provide wider deflection ranges and higher resolution. But such an arrangement willfurther load the arm axle, donating to the already existing problems.

The problem of the mechanically coupled position sensor can be avoided at all, using some formof magnetic sensor. The constant flux, permanent magnet can be attached to the arm axle. Thetwo Hall effect devices can be attached to the base on the equal distances from the both sidesof the axle. Now, the difference of the currents, flowing through each Hall effect device will bedependent on the axle inclination, because of the magnetic flux changes. But the high noise figuresin the measured data can pose a problem, because of the high density magnetic field present inthe laboratory. The field generated by the driving motor and the surrounding equipment (CRTs,transformers, etc.) should then be properly shielded, thus claiming their price for the solution ofthe mechanical problems. Another similar approach to the problem is to use Termenvox5, a devicethat measures a spatial capacitance changes in the open space. It is essentially a super-heterodyneradio receiver tuned to sufficiently low frequency (to prevent a reception of the EM waves by theantenna), with an antenna placed close to the pendulum arm. The movement of the large metalpiece near the antenna will cause voltage variations in the receiver output, thus allowing theremote measurements of the positions. Of course, the main source of the errors in this case will beparasitic reception.

The sophistication can be continued by introducing the laser and radar equipment, but thecost-effectiveness of the measuring electronics is already very small (we do not want to equip theUS$50 plant with the US$500 sensors). Nevertheless, we wanted to provide this review, to exposethe reader to some of the opportunities in the measurement technologies.

5Invented by the Lev Termen of the Leningrad Institute of Technology

1-7

Chapter 2

The software side of the problem

2.1 Top-level considerations

The need in the high-level tools for the control systems design has been recognized for a longtime yet. Our project was supposed to use one of the newly developed tools: Quanser ConsultingWinConr software used with the Quanser MultyQ card and Mathworks Simulinkr. The ideabehind the WinCon software is to compile the Simulink models into the special form executables,that are then being run by the WinCon server. Special Simulink blocks are provided to allow ausage of the data acquisition cards, primary MultyQ card. By doing so, WinCon software claims toprovide a rapid prototyping platform for the development of control systems.

The WinCon software has enormous amount of limitations that rendered it’s use impossible.This limitations are mainly imposed by the Mathworks Matlab RT target compiler technology. Thecompiler is only able to accommodate fixed time step, deterministic models with fixed configurationwith no OS interactions, such as memory allocation requests or file I/O. As such, the compiler isunable to generate compiled fuzzy or neuron network controllers or even the variable precisionPID ones, because it has to know the precise calculation time (propagation delay) of the controllerto set-up a hardware sample time. This problem is characteristic for the all products now in useand may be it will be overcome in the future.

The project goal was to use fuzzy logic in the design of controller and we wanted to use aMathworks Matlab as a prototyping platform, so the only way was to design our own software. Todo this a number of steps was required:

• Because we do not want to devote a whole computer to the control work, a sufficient timesharing OS was needed.

• The mechanisms to access the hardware and to handle the hardware - software data inter-change must be present in the chosen OS.

• The way to implement the controller shall be decided. The options are: low-level program(say, C-language code), compiled high-level script (Matlab or Simulink compiled model) andpure high-level controller (Matlab or Simulink model being run-time parsed and interpretedby running Matlab).

• The latest option will require from high-level interpreter (Matlab) to possess a sufficientinter-process communication capabilities to handle a data streams to and from the hardware.Fortunately, Matlab has such capabilities.

2-1

The OS affects all other aspects of the software design. Because of the inherent problems inthe Microsoft Windows 9x/NT operation systems, they are not good choice, when designing stable,reliable and precision hungry software. There are no good way to achieve the precise timing in theMS Windows systems (the only way to do so, in fact, is to promote the RT task into the exclusivemode, suspending all other tasks; this is something we do not want to do), MacOS lacks manysystem level services (and very hardware limited), IBM OS/2 is extremely rare, so the virtuallyonly choice available is some flavor of UNIX. The data acquisition card we had, was an old ISAbus DT2811, so we must use the computer with ISA bus available, namely IBM PC (the PCI buscard could allow us to use an SGI O2 workstation, available in the laboratory). PC architecturehas many oddities (imposed by the years of backward compatible redesign), and our Intel PentiumII based machine was not an exception. Anyway, there are few different UNIX flavors for thePC: SCO UNIX, Sun Solaris x86, FreeBSD and GNU/Linux. Two former systems are proprietarycommercial systems, available for free under severe license limitations. Between the later choices,GNU/Linux is the best choice because of a large amount of development it experienced in the lastyears. Because of the laboratory policy restrictions we chose a RedHat v6.0 Linux distribution.

Linux offers many distinct interfaces to allow utilization of the hardware obtained data. Theymay be divided into the two main classes: user space drivers and kernel modules (system leveldrivers). In our work we tried many of the available ways, so their description is postponed to thefollowing sections .

We tried hard to implement a native high-level controller design, but the amount of develop-ment was so vast that this shall be probably done on the industrial or full-scale academic basis.The actually implemented controller was C-language program with still much work undone.

The work on the high-level controller definition by means of Matlab caused us to evaluate someof it’s built in communication interfaces, namely Matlab engine interface, M-file api and C-Mexcustom communication routines. Few of the Matlab interfaces are not available on Linux andsome are still in the development (Matlab Server).

2.2 Proof of concept design

2.2.1 Basics

The evolution of any software product has some basic steps. The one following the initial designis a proof of concept phase, where the initial code is written to test the possibilities of the targetplatform.

We starting from the development of the primitive interface function between the Matlab in-terpreter and the hardware. To access a hardware in a first time we used a dumb head approach,that is illustrated by the following code (the listing of the dt2811.h file, the one adapted from thereference header file provided by Data Translation; the original implementation was made for MSDOS, so this file preserves only guidelines that survived in UNIX environment):

/∗——dt2811.h——∗/#include < sys / timex . h>#include <asm/ io . h>

4

/∗Card data : dt2811∗/

#define BASE PORT 0x218 / / base adapter port ( FD)8

2-2

#define ADCSR BASE PORT+ 0 / / Control \Status register#define ADGCR BASE PORT+ 1 / / Gain\Channel se lect register#define ADDAT LO BASE PORT+ 2 / / ADC low byte

12 #define ADDAT HI BASE PORT+ 3 / / ADC high byte#define DADAT0 LO BASE PORT+ 2 / / DAC0 low byte#define DADAT0 HI BASE PORT+ 3 / / DAC0 high byte#define DADAT1 LO BASE PORT+ 4 / / DAC0 low byte

16 #define DADAT1 HI BASE PORT+ 5 / / DAC1 high byte#define DIOP1 BASE PORT+ 6 / / d ig i ta l IO port 1 ( low )#define DIOP2 BASE PORT+ 6 / / d ig i ta l IO port 2 ( high )#define TMRCTR BASE PORT+ 7 / / timer \ couter register

20

/∗Card reinitialization∗/void reset card ( void )

24 ioperm (BASE PORT, 8 , 1 ) ;outb (0 x10 ,ADCSR) ;usleep (110) ;inb (ADDAT LO) ;

28 inb (ADDAT HI) ;

/∗Get ADC value∗/32 bool get hard data ( const int8 t channel ,

const int8 t gain ,int16 t ∗ hardvalue ) / / read ADC

36 int8 t reg ADGCR=0, reg DAT HI=0, reg DAT LO=0;

/ / used to i n i t i a l i z e hardware registersint16 t tes t va l =0;

40 if ( ( ( channel <0) | | ( channel>15))| | ( ! ( ( gain ==1) | | ( gain ==2) | | ( gain ==4) | | ( gain ==8) ) ) )

return fa lse ; / / data validationelse / / continuing

44 / / Let us set the gain / channel register (ADGCR)reg ADGCR=channel ;switch ( gain )

48 case 1 :

break ;case 2 : reg ADGCR=reg ADGCR|0 x40 ;

52 break ;case 4 : reg ADGCR=reg ADGCR|0 x80 ;

break ;case 8 : reg ADGCR=reg ADGCR|0 xc0 ;

56

2-3

outb ( reg ADGCR,ADGCR) ;while ( ! ( 0 x80 & inb (ADCSR) ) ) ; / / busy wait

60 reg DAT LO=inb (ADDAT LO) ;reg DAT HI=inb (ADDAT HI) ;reg DAT HI=reg DAT HI & 0 x0f ;

64

tes t va l =(0 x00ff & reg DAT HI)<<8;tes t va l &=0xff00 ;tes t va l |=0 x00ff & reg DAT LO;

68 ∗hardvalue=test va l ;

return true ;

72

/∗Set DAC value∗/bool put hard data ( const int8 t channel ,

76 int16 t ∗ hardvalue ) / / write DAC

int8 t reg DAT HI=0, reg DAT LO=0;int16 t tes t va l =0;

80

if ( ( channel <0) | | ( channel>1)) return fa lse ;else

84 reg DAT LO=( int8 t ) (∗ hardvalue & 0 x00ff ) ;reg DAT HI=( int8 t ) (∗ hardvalue>>8);

tes t va l =(0 x00ff & reg DAT HI )<<8 ; / / monitor value88 tes t va l &=0xff00 ;

tes t va l |=0 x00ff & reg DAT LO;∗hardvalue=test va l ;

92 switch ( channel )case 0 :

outb ( reg DAT LO, DADAT0 LO) ;96 outb ( reg DAT HI , DADAT0 HI) ;

break ;case 1 :

outb ( reg DAT LO, DADAT1 LO) ;100 outb ( reg DAT HI , DADAT1 HI) ;

break ;return true ;

104

2-4

/∗Get system µs counter value∗/108 double get time stamp ( void )

struct timex time reading ;time reading . modes=0;

112 if (−1==adjtimex (&time reading ) ) return 0 ;else return ( double ) ( time reading . time . tv usec

+1000000.0∗ time reading . time . tv sec ) ;

This may require some explanations. We have no intention to describe here the functional in-terface of the data acquisition card (it is a very old card, lacking virtually all useful features: databuffer, DMA access, automating multi-channel sampling, etc.), but the ways of doing things muststill be explained. To avoid excess complications, we decided not to use interrupts from the cardtill all other issues will be smoothed out, so the ADC value reading routine, get_hard_data usespolling loop to wait for the conversion results. The usage of interrupts requires system-level pro-gramming that was done only on the much later stages of the project. Generally, one can see, thatall functionality of the card is accessible via the eight byte-wide registers, referenced by the basecard address (preset at the installation time) and the fixed offset (this can be seen at the beginningof the file). Such a scheme was very common within the old IBM PC hardware; as a matter offact, the IBM PC I/O address space is divided (informally) into the eight byte chunks, to simplifya construction of the address decoders of the I/O devices. It can only be noticed, that each of twoDACs has a separate access port (<DADAT0_LO, DADAT0_HI> and <DADAT1_LO, DADAT1_HI>), thecard’s digital I/O pins are wired via the bi-directional drivers to the <DIOP1, DIOP2> ports and thetimer/counter chip present on the card is controlled by the <TMRCTR> port. The ADC case is some-what more complicated, thus there is a data port (<ADDAT_LO, ADDAT_HI>) and an ADC controlport (<ADGCR>) used to control the analog amplifier gain and the analog multiplexor channel se-lection. The overall card operation mode and status can be communicated via the <ADCSR> port,that sits on the base card address (offset 0).

Card initialization is done by the reset_card function. The ioperm call is used to allow forthe user level process (with root permissions, of course) to gain access to the specified I/O addressregion. Then the outb and inb1 system macros are used to interact with the hardware ports.The value 0x10 written into the ADCSR card register sets the card into the single conversion pertrigger mode that is used for polling and multi-channel sampling . This implicitly triggers theADC conversion cycle, to clean-up the error registers and busy flags. After a pause of 110µs theADC data port is read and card initialization is completed.

The actual card polling and data acquisition is done by the function get_hard_data. It checksthe parameter values for validity and can reject them if they are not valid. Then an ADC con-trol register (<ADGCR>) is loaded with especially prepared value, starting an ADC conversioncycle. The Ready flag of the <ADCSR> register is polled, till the ADC value is obtained. The rawADC value is bit shuffled to accommodate the integer storage type and returned by reference tothe caller. No explicit error or live-lock detection provisions are made (the conversion error willcause the garbage value to be returned, when the live-lock condition , resulted, possibly from thehardware failure, will cause the process to exceed it CPU run-time limit and fail).

1inb and outb system macros allow compiler to inline the x86 CPU commands IN and OUT into the C code.

2-5

The data output routine, put_hard_data, is rather self-explanatory (the only fact worth men-tioning is the explicite bit field decomposition of the parameter value; generally, a compiler canbe trusted to deliver the 16 bit parameter into the I/O port pair in the right order. We shall alsomention the get_time_stamp function that is used to access the system clock via the adjtimexcall. The adjtimex call can be used to obtain and modify all system timer parameters, such ashardware clock precision, system tick duration, timer update delay, etc. We use it only to readout the microsecond and second counters, with time value round-up limited to the 1min. We usedthe timer only for differential time measurement, and there are extremely low probability thatdelay between two events will exceed the round-up interval (many things should go wrong, beforesomething like this will be able to occur).

2.2.2 Driver side

Although it is perfectly possible to embed the hardware access code directly into the Matlab usingthe Mex interface (we even did this to check things out), it is not a good idea. The Matlab isa heavy and not very stable application (at least on the lnx86 platform), heavily relying on thesystem services. For a user-space program to be able access the hardware it must be grantedan access rights via the ioperm system call. A process granted with such rights can take thesystem down with him in case of crash, and Matlab crashes are not very rare, unfortunately. Thiscalls for a proxy process, that will access the hardware and will provide the Matlab based controlscripts with the needed data. Such a proxy process is often called a user-space driver , unlike it’scounterpart, a kernel-space driver, that is normally referred simply as a driver. This approachprovides the decoupling between hardware and controller software, by means of abstract softwareinterface. Here we have the first attempt to create a user-space driver. It consists from three parts:

• matlabpipe.c - the body of the driver

• mpipe-support.c - the functionality of the driver

• matlabpipe.h - common header

Let us look on the source files now. The common header contains nothing unusual and greatlyresembles the beginning of the dt2811.h brought above.

/∗——matlabpipe.h——∗//∗Global includes ∗/#include < stdio . h>

4 #include < sys / types . h>#include < sys / stat . h>#include < f cnt l . h>#include <unistd . h>

8 #include < std l ib . h>#include < sys / timex . h>#include <asm/ io . h>

12 /∗Card data : dt2811∗/

#define BASE PORT 0x218 / / base adapter port ( FD)

16 #define ADCSR BASE PORT+ 0 / / Control \Status register

2-6

#define ADGCR BASE PORT+ 1 / / Gain\Channel se lect register#define ADDAT LO BASE PORT+ 2 / / ADC low byte#define ADDAT HI BASE PORT+ 3 / / ADC high byte

20 #define DADAT0 LO BASE PORT+ 2 / / DAC0 low bit#define DADAT0 HI BASE PORT+ 3 / / DAC0 high bit#define DADAT1 LO BASE PORT+ 4 / / DAC0 low bit#define DADAT1 HI BASE PORT+ 5 / / DAC0 high bit

24 #define DIOP1 BASE PORT+ 6 / / d ig i ta l IO port 1#define DIOP2 BASE PORT+ 6 / / d ig i ta l IO port 2#define TMRCTR BASE PORT+ 7 / / timer \ couter register

28 typedef enumFalse =0, True=1 Boolean ;

/∗Names of named pipes ∗//∗We will use / tmp to store them∗/

32

#define MATLAB DATA PIPE ” / tmp/ matinput ”#define MATLAB RESPONCE PIPE ” / tmp/ matoutput ”

36 void matlab interact ( void ) ;Boolean setcard ( void ) ;void closecard ( void ) ;

40 Boolean get hard data ( const int8 t channel , const int8 t gain ,int16 t ∗ hardvalue ) ;

Boolean put hard data ( const int8 t channel , int16 t ∗ hardvalue ) ;int64 t get time stamp ( void ) ;

Driver operation setup is provided by the main program module, matlabpipe.c, that opens thenamed pipes to support the bidirectional data exchange between controller process (Matlab) anddriver:

/∗——matlabpipe.h——∗/

#include ” matlabpipe . h”4

int main ( void )

8 pid t mine ;

/∗ First of al l − autofork ∗/mine=fork ( ) ;

12 if ( mine==−1)

printf ( ” Fork fa i led \n” ) ;return −1;

16

2-7

else if ( mine !=0 ) /∗Daddy process ∗/return 0 ;

20 /∗Child process body∗//∗I ’ l l check for presence of old pipe objec t to remove them∗/

if (0== access (MATLAB DATA PIPE, F OK) )24 if (−1==remove (MATLAB DATA PIPE) ) /∗ f i l e dele t ion fai led ∗/

printf ( ” Failed to remove %s f i l e \n” , MATLAB DATA PIPE) ;return −1;

28

if (0== access (MATLAB RESPONCE PIPE, F OK) )if (−1==remove (MATLAB RESPONCE PIPE) ) /∗ f i l e dele t ion fai led ∗/

32 printf ( ” Failed to remove %s f i l e \n” , MATLAB RESPONCE PIPE) ;return −1;

36

/∗I shall create new pipe set ∗/umask( 0 ) ;if ((−1==mknod(MATLAB DATA PIPE, S IFIFO |0 6 6 6 , 0 ) ) | |

40 (−1==mknod(MATLAB RESPONCE PIPE, S IFIFO |0666 ,0 ) ) )

printf ( ” Failed to create pipes \n” ) ;return −1;

44

/∗Set up hardware∗/if ( setcard ( ) )

48 /∗Call driver loop ∗/matlab interact ( ) ;

pr int f ( ”\n Program terminated \n” ) ;

52 /∗Try blind removal of pipe f i l e s ∗/remove (MATLAB RESPONCE PIPE) ;remove (MATLAB DATA PIPE) ;return 0 ;

56

The most of the driver’s functionality is hidden, however, in the forthcoming file:/∗——mpipe-support.c——∗/#include ” matlabpipe . h”/ / # define DEBUG 1

4

2-8

/∗Read from pipe helper routine∗/void block read ( int stream , void ∗ buffer , int length )

8 fd set read set ;int status ;struct timeval timeout ;

12 FD ZERO(&read set ) ;FD SET( stream,& read set ) ;timeout . tv sec =60 ; / / timeout after 60 secondstimeout . tv usec =0;

16 while (1)

status =se lect ( stream+1,& read set ,NULL,NULL,&timeout ) ;if ( status >0 ) / / data can be read

20 / / pr int f ( ”\n Select passed with status %d” , status ) ;read ( stream , buffer , length ) ;return ;

24 else

printf ( ”\n Timeout on pipe \n” ) ;28 exit (−1 ) ; / / crash exit

32

/∗Client conversation loop∗/void matlab interact ( void )

36 int out stream , in stream ;char state ;int8 t gain , channel ;int16 t hard value ;

40 int64 t time stamp ;

if (−1==( out stream =open (MATLAB DATA PIPE,O WRONLY) ) )

44 printf ( ” Failed to open OUT pipe \n” ) ;return ;

else ; / / pr int f ( ”\n Succesfully established MATLAB uplink ” ) ;

48

if (−1==( in stream =open (MATLAB RESPONCE PIPE,O RDONLY) ) )

printf ( ” Failed to open IN pipe \n” ) ;52 return ;

2-9

else ; / / pr int f ( ”\n Succesfully established MATLAB downlink ” ) ;

56 /∗Now − the protocol part :∗/

/∗ The protocol is as following :1 . Read 1 byte from MATLAB RESPONCE PIPE ( char ) .

60 2 . Check action : ’ a ’ − acquire ADC value dialog ; followed by :1a . Read 1 byte from MATLAB RESPONCE PIPE

( int8 ) − try to convert to channel number2a . Read 1 byte from MATLAB RESPONCE PIPE

64 ( int8 ) − try to convert to gain value3a . Try to acquire hardware reading ;

I f IO or data error −set error flag and zero data value

68 ’ s ’ − set DAC value dialog ; followed by :1a . Read 1 byte from MATLAB RESPONCE PIPE

( int8 ) − try to convert to channel number2a . Read 2 bytes from MATLAB RESPONCE PIPE

72 ( int16 ) − convert to DAC value bitstream3a . Try to set hardware ;

I f IO or data error −set error flag

76 ’ e ’ − end run ( c lose hardware and ex i t )3 . Response : write 1 byte to MATLAB DATA PIPE ( char ) .

Possible values : ’ s ’ − last operation succeededFollowed by 2 bytes ( int16 )

80 I f a f ter ADC operation − ADC valueIf af ter DAC operation − DAC valueand next by 8 bytes ( int64 )

current time stamp84 ’ f ’ − last operation fai led

∗/

while (1)88

block read ( in stream ,& state , 1 ) ;switch ( state )

92 case ’ a ’ :block read ( in stream ,&channel , 1 ) ;block read ( in stream ,&gain , 1 ) ;if ( ! get hard data ( channel , gain ,&hard value ) ) goto IO fai led ;

96 break ;case ’ s ’ :

block read ( in stream ,&channel , 1 ) ;block read ( in stream ,&hard value , 2 ) ;

100 if ( ! put hard data ( channel ,&hard value ) ) goto IO fai led ;

2-10

break ;case ’ e ’ :

c lose ( in stream ) ;104 close ( out stream ) ;

closecard ( ) ;return ;

108 state =’ s ’ ; / / success

write ( out stream ,& state , 1 ) ;write ( out stream ,&hard value , 2 ) ;time stamp=get time stamp ( ) ;

112 write ( out stream ,&time stamp , 8 ) ;continue ;

IO fai led : / / Eliminating boolean variable by means of jumps .print f ( ”\n Failure just detected ” ) ;

116 state =’ f ’ ;write ( out stream ,& state , 1 ) ;continue ;

120

/∗Reset card and grant access permissions∗/Boolean setcard ( void ) / / i n i t i a l i z e card

124 if (0!= ioperm (BASE PORT, 8 , 1 ) ) / / try to open device

printf ( ”\ nFailed to get IO ports for operation .\ n” ) ;128 return False ;

outb (0 x10 ,ADCSR) ;usleep (110) ;

132 inb (ADDAT LO) ;inb (ADDAT HI) ;return True ;

136

/∗Reset card and revoke access permissions∗/void closecard ( void ) / / lock card

140 outb (0 x10 ,ADCSR) ;usleep (110) ;inb (ADDAT LO) ;inb (ADDAT HI) ;

144 ioperm (BASE PORT, 8 , 0 ) ; / / c lose ports

/∗Read data from ADC∗/148 Boolean get hard data ( const int8 t channel , const int8 t gain ,

2-11

int16 t ∗ hardvalue ) / / read ADC

int8 t reg ADGCR=0, reg DAT HI=0, reg DAT LO=0;152 / / used to i n i t i a l i z e hardware registers

int16 t tes t va l =0;

if ( ( ( channel <0) | | ( channel>15))156 | | ( ! ( ( gain ==1) | | ( gain ==2) | | ( gain ==4) | | ( gain ==8) ) ) )

printf ( ”\n Unacceptable ADC parameters ” ) ;return False ; / / data validation

160 else / / continuing

/ / Let us set the gain / channel register (ADGCR)164 reg ADGCR=channel ;

switch ( gain )case 1 :

168 break ;case 2 : reg ADGCR=reg ADGCR|0 x40 ;

break ;case 4 : reg ADGCR=reg ADGCR|0 x80 ;

172 break ;case 8 : reg ADGCR=reg ADGCR|0 xc0 ;

/∗DEBUG SECTION 1∗/176 #ifdef DEBUG

reg DAT LO=1+( int8 t ) (255.0∗ rand ( ) / ( RAND MAX+ 1 . 0 ) ) ;reg DAT HI=1+( int8 t ) (255.0∗ rand ( ) / ( RAND MAX+ 1 . 0 ) ) ;/∗END OF DEBUG SECTION 1∗/

180 /∗True code :∗/#else

outb ( reg ADGCR,ADGCR) ;while ( ! ( 0 x80 & inb (ADCSR) ) ) ; / / busy wait

184 reg DAT LO=inb (ADDAT LO) ;reg DAT HI=inb (ADDAT HI) ;reg DAT HI=reg DAT HI & 0 x0f ;

#endif188

tes t va l =(0 x00ff & reg DAT HI)<<8;tes t va l &=0xff00 ;tes t va l |=0 x00ff & reg DAT LO;

192 ∗hardvalue=test va l ;/∗DEBUG SECTION 2∗/

#ifdef DEBUGprintf ( ”\n Requested : Channel %d \ t Gain %d” , channel , gain ) ;

196 printf ( ”\n ADGCR was set to %x” , reg ADGCR) ;

2-12

printf ( ”\n DAT HI was set to %x” , reg DAT HI ) ;pr int f ( ”\n DAT LO was set to %x” , reg DAT LO ) ;print f ( ”\n Report back value was set to %d” ,∗ hardvalue ) ;

200 #endif/∗END OF DEBUG SECTION 2∗/return True ;

204

/∗Write data to DAC∗/Boolean put hard data ( const int8 t channel ,

208 int16 t ∗ hardvalue ) / / write DAC

int8 t reg DAT HI=0, reg DAT LO=0;int16 t tes t va l =0;

212

if ( ( channel <0) | | ( channel>1)) return False ;else

216 reg DAT LO=( int8 t ) (∗ hardvalue & 0 x00ff ) ;reg DAT HI=( int8 t ) (∗ hardvalue>>8);/∗DEBUG SECTION 1∗/

#ifdef DEBUG220 printf ( ”\n Requested : Channel %d” , channel ) ;

pr int f ( ”\n Requested value for DAC: %d” ,∗ hardvalue ) ;pr int f ( ”\n DAT HI was set to %X” , reg DAT HI ) ;pr int f ( ”\n DAT LO was set to %X” , reg DAT LO ) ;

224 #endif/∗END OF DEBUG SECTION 1∗/tes t va l =(0 x00ff & reg DAT HI )<<8 ; / / monitor valuetes t va l &=0xff00 ;

228 tes t va l |=0 x00ff & reg DAT LO;(∗ hardvalue )= tes t va l ;/∗DEBUG SECTION 2∗/

#ifdef DEBUG232 printf ( ”\n Report back value was set to %d” ,∗ hardvalue ) ;

/∗END OF DEBUG SECTION 2∗/#else

switch ( channel )236

case 0 :outb ( reg DAT LO, DADAT0 LO) ;outb ( reg DAT HI , DADAT0 HI) ;

240 break ;case 1 :

outb ( reg DAT LO, DADAT1 LO) ;outb ( reg DAT HI , DADAT1 HI) ;

244 break ;

2-13

#endif

return True ;248

/∗Get system µs counter value∗/252 int64 t get time stamp ( void )

struct timex time reading ;time reading . modes=0;

256 if (−1==adjtimex (&time reading ) ) return 0 ;else return time reading . time . tv usec +

1000000∗ time reading . time . tv sec ;

The functionality of this driver is rather naive. The hardware is communicated by the meansof already defined reference functions: put_hard_data, get_hard_data and get_time_stamp thatare equivalent in functionality to the put_hard_data, get_hard_data and get_time_stamp func-tions of the dt2811.h file (the explanation is given in the previous subsection). General card accesscontrol is provided by two mini-functions, setcard and closecard. These functions perform thestandard card reset procedure and permission vector manipulation using the ioperm call. Thereis also another helper function, namely block_read that reads a given amount of bytes from thestream into the provided buffer. It also waits for the needed amount of bytes to accumulate inthe stream buffer, thus allowing consistent message passing through the stream. The stream isimplemented as a named pipe and created in the module matlabpipe.c. The implementation ofthis function conforms the reference implementation of the GNU/Linux system API definition.

As a whole, driver implements “serve by demand” policy, and it’s main service loop is imple-mented by the function matlab_interact. The protocol definitions are given in the code body as acomment and given for reference only (this implementation is unsuited for real job anyway).

2.2.3 Client side

The digital controller operates on temporally sorted vectors of data (it is a direct implication of thez-transform theorem). This calls for special software subsystem, a sort of queue, that will gathersingle data samples and arrange them into the fixed width temporal vectors, with the most resentsample as a first member and the sample from before N sample as a last (N is defined by the orderof controller). Such data structure is known as a “first in - first out” queue with parallel access(because data behaves in it like in the real FIFO, and can also be read simultaneously from allFIFO registers). Normally, it’s implementation is not very convenient in the regular procedurallanguages, such as C (in Scheme2, for example, the implementation of streams and queues is muchmore esthetic). However, the Matlab script is a fourth generation procedural language with veryclean handling of data (resembling an APL way), so the algorithms can be developed in Matlabin the much less time and with much smaller amount of bugs, than in C (the typical time gain is400% for difficult algorithms; the main reason is the automatic memory management and object

2 Proper functional dialect of Lisp developed as part of famous MAC project in MIT

2-14

handling available in Matlab). The algorithm, developed and tested in Matlab can then be auto-matically compiled (by Matlab to C translator, mcc) or manually ported in some small amount oftime. We also followed this approach, and first described all the functionality in the Matlab. Mat-lab relies on the underlying file system services to manage it’s function definition store, so eachfunction normally lives in the distinct file. Here we have a trial fuzzy controller as a Matlab basedclient.

The top-level script, fuzzy1 uses machine_control script to attach the external pipes to theMatlab process, making a driver communication possible. This script is also capable of issuing thecommands to the driver.

%——fuzzy1.m——% f i r s t try fuzzy base contro l l e r

4 global basef is pendfisbasef is =readfis ( ’ fuzzy1base2 ’ ) ;pendfis =readfis ( ’ fuzzy1arm ’ ) ;pause ( 1 ) ;

8

machine command= ’ start ’ ;machine control ;

if stat ==0,12 inputs =[ ’ read adc data ( 3 , 8 , ’ ’ arm normalizer ’ ’ ) ’ ; . . .

’ read adc data ( 4 , 4 , ’ ’ base normalizer ’ ’ ) ’ ] ;output= ’ write dac data (1 , responce , ’ ’ denormalize dac ’ ’ ) ; ’ ;

16 run control ler (5 , ’ contfuzzy1 ’ , inputs , output ) ;machine command= ’ stop ’ ;machine control ;

end;

%——machine control.m——global DATA IN DATA OUT

4

switch machine command ,case ’ start ’ ,

fclose ( ’ a l l ’ ) ;8 [ stat , res ]=unix ( ’ k i l l a l l mpipe ’ ) ;

stat =unix ( ’ . / mpipe ’ ) ;if stat ==0,fprintf (2 , ’ Passed app−start sequence \n ’ ) ;

12 pause ( 2 ) ;DATA IN=fopen ( ’ / tmp/ matinput ’ , ’ r ’ , ’ n ’ ) ;DATA OUT=fopen ( ’ / tmp/ matoutput ’ , ’ w’ , ’ n ’ ) ;fprintf (2 , ’ Passed in i t sequence \n ’ ) ;

16 write dac data (0,2047, ’ u n i t y f i l t e r ’ ) ;write dac data (1,2047, ’ u n i t y f i l t e r ’ ) ;

end

2-15

case ’ stop ’ ,20 write dac data (0,2047, ’ u n i t y f i l t e r ’ ) ;

write dac data (1,2047, ’ u n i t y f i l t e r ’ ) ;fwrite (DATA OUT, ’ e ’ , ’ char ’ ) ;fclose ( ’ a l l ’ ) ;

24 [ stat , res ]=unix ( ’ k i l l a l l mpipe ’ ) ;

otherwise ,

28 end;

The fuzzy1 script also loads fuzzy sets definitions. These given here, are not working sets. Theywere used for testing only .

Fuzzy sets loaded for controller (reference only)fuzzy1arm.fis fuzzy1base.fis[System]Name=’fuzzy1arm’Type=’mamdani’Version=2.0NumInputs=3NumOutputs=1NumRules=4AndMethod=’min’OrMethod=’max’ImpMethod=’min’AggMethod=’max’DefuzzMethod=’centroid’

[Input1]Name=’input1’Range=[-1 1]NumMFs=5MF1=’mf1’:’trimf’,[-1.5 -1 -0.5]MF2=’mf2’:’trimf’,[-1 -0.5 0]MF3=’mf3’:’trimf’,[-0.5 0 0.5]MF4=’mf4’:’trimf’,[0 0.5 1]MF5=’mf5’:’trimf’,[0.5 1 1.5]

[Input2]Name=’input2’Range=[-1 1]NumMFs=5MF1=’mf1’:’gaussmf’,[0.2123 -1]MF2=’mf2’:’gaussmf’,[0.2123 -0.5]MF3=’mf3’:’gaussmf’,[0.2123 0]MF4=’mf4’:’gaussmf’,[0.2123 0.5]MF5=’mf5’:’gaussmf’,[0.2123 1]

[Input3]Name=’input3’Range=[-1 1]NumMFs=5MF1=’mf1’:’gaussmf’,[0.2123 -1]MF2=’mf2’:’gaussmf’,[0.2123 -0.5]MF3=’mf3’:’gaussmf’,[0.2123 0]MF4=’mf4’:’gaussmf’,[0.2123 0.5]MF5=’mf5’:’gaussmf’,[0.2123 1]

[Output1]Name=’output1’Range=[-1 1]NumMFs=5MF1=’mf1’:’gbellmf’,[0.25 2.5 -1]MF2=’mf2’:’gbellmf’,[0.25 2.5 -0.5]MF3=’mf3’:’gbellmf’,[0.25 2.5 0]MF4=’mf4’:’gbellmf’,[0.25 2.5 0.5]MF5=’mf5’:’gbellmf’,[0.25 2.5 1]

[Rules]0 0 1, 1 (1) : 10 0 5, 5 (1) : 11 0 3, 1 (1) : 15 0 3, 5 (1) : 1

[System]Name=’fuzzy1base’Type=’mamdani’Version=2.0NumInputs=2NumOutputs=1NumRules=3AndMethod=’min’OrMethod=’max’ImpMethod=’min’AggMethod=’max’DefuzzMethod=’centroid’

[Input1]Name=’input1’Range=[-1 1]NumMFs=5MF1=’mf1’:’trimf’,[-1.5 -1 -0.5]MF2=’mf2’:’trimf’,[-1 -0.5 0]MF3=’mf3’:’trimf’,[-0.5 0 0.5]MF4=’mf4’:’trimf’,[0 0.5 1]MF5=’mf5’:’trimf’,[0.5 1 1.5]

[Input2]Name=’input2’Range=[-1 1]NumMFs=5MF1=’mf1’:’gaussmf’,[0.2123 -1]MF2=’mf2’:’gaussmf’,[0.2123 -0.5]MF3=’mf3’:’gaussmf’,[0.2123 0]MF4=’mf4’:’gaussmf’,[0.2123 0.5]MF5=’mf5’:’gaussmf’,[0.2123 1]

[Output1]Name=’output1’Range=[-1 1]NumMFs=5MF1=’mf1’:’trimf’,[-1.5 -1 -0.5]MF2=’mf2’:’trimf’,[-1 -0.5 0]MF3=’mf3’:’trimf’,[-0.5 0 0.5]MF4=’mf4’:’trimf’,[0 0.5 1]MF5=’mf5’:’trimf’,[0.5 1 1.5]

[Rules]1 0, 1 (1) : 13 0, 3 (1) : 15 0, 5 (1) : 1

The input and output procedures, read_adc_data and write_dac_data have the ability to usea specified function as a filter for data (take a look at the arm_normalizer and base_normalizer as

2-16

the examples of input shaping functions, one for arm and another for base position sensor; thereis also a denormalize_dac function that serves as output filter). They use an opened streams topipe data to and from the driver.

%——read adc data——function [ data out , time out ]= read adc data ( channel , gain , f i l t e r f c n )

global DATA OUT DATA IN4

fwrite (DATA OUT, ’ a ’ , ’ char ’ ) ;fwrite (DATA OUT, channel , ’ uint8 ’ ) ;fwrite (DATA OUT, gain , ’ uint8 ’ ) ;

8 state =fread ( DATA IN,1 , ’ char ’ ) ;if ( char ( state )== ’ f ’ )

data out =2047;time out =−1000000;

12 display ( ’ Failed here ’ ) ;else

data out =fread ( DATA IN,1 , ’ uint16 ’ ) ;time out =fread ( DATA IN,1 , ’ uint64 ’ ) ;

16 end;time out =time out ;data out =feval ( f i l t e r f c n , data out ) ;

%——write dac data——function [ data out , time out ]= write dac data ( channel , output , f i l t e r f c n )

global DATA OUT DATA IN4

data out =feval ( f i l t e r f c n , output ) ;fwrite (DATA OUT, ’ s ’ , ’ char ’ ) ;fwrite (DATA OUT, channel , ’ uint8 ’ ) ;

8 fwrite (DATA OUT, data out , ’ uint16 ’ ) ;state =fread ( DATA IN,1 , ’ char ’ ) ;

if ( char ( state )== ’ f ’ )data out =2047;

12 time out =0;display ( ’ Failed here ’ ) ;

elsedata out =fread ( DATA IN,1 , ’ uint16 ’ ) ;

16 time out =fread ( DATA IN,1 , ’ uint64 ’ ) ;end;

data out =data out ;20 time out =time out ;

%——arm normalizer——function data out =arm normalizer ( data in )% for read−out gain 8

4

2-17

l e f t p o s i t i o n =43;r ight pos i t i on =3970;center pos i t ion =1859;

8

data out =data in−center pos i t ion ;if data out>0,

data out =data out / ( l e f t p o s i t i o n−center pos i t ion ) ;12 elseif data out<0,

data out =data out / ( center pos i t ion−r ight pos i t i on ) ;end;

16 if data out >1, data out =1; end;if data out<−1, data out =−1; end;

%——base normalizer——function data out =base normalizer ( data in )% for read−out gain 4

4

l e f t p o s i t i o n =3900;r ight pos i t i on =160;center pos i t ion =2027;

8

data out =data in−center pos i t ion ;if data out>0,

data out =data out / ( l e f t p o s i t i o n−center pos i t ion ) ;12 elseif data out<0,

data out =data out / ( center pos i t ion−r ight pos i t i on ) ;end;

16 if data out >1, data out =1; end;if data out<−1, data out =−1; end;

%——denormalize dac——function data out =denormalize dac ( data in )

4 if data in >1, data in =1; end;if data in<−1, data in =−1; end;

data out =floor (4095∗( data in + 1 ) / 2 ) ;

The heart of the client is the queue manager, run_controller. This function arranges the filteredvalues from the inputs, forms a sample vectors from them and applies a controller (in this case -contfuzzy1, function that uses preloaded fuzzy sets to simulate a control surface).

%——run controller——function run control ler ( window size , contro l funct ion , ack functions , . . .

resp function )4

2-18

s l ide ptr =1; %sliding pointerresponce =0; %variable that holds the responce valueinputs =size ( ack functions , 1 ) ;

8

raw data vector =zeros ( inputs ,2∗ window size ) ;raw time vector =zeros ( inputs ,2∗ window size ) ;data vector =zeros ( inputs , window size ) ;

12 time vector =zeros ( inputs , window size ) ;

% get f i r s t time readingeval ( [ ’ [ data inp , time inp ]= ’ , ack functions ( 1 , : ) , ’ ; ’ ] ) ;

16 base time =time inp /1000000;

for l =1: window size ,for k=1: inputs , %f i r s t data f i l l

20 eval ( [ ’ [ data inp , time inp ]= ’ , ack functions ( k , : ) , ’ ; ’ ] ) ;raw data vector ( k, l )= data inp ;%convert absolute time to re la t i v e ( usec −> sec )raw time vector ( k, l )= time inp /1000000−base time ;

24 end;end;

s l ide ptr =window size +1;28 clear ( ’ l ’ ) ;

s top dialog =dialogs ( ’ create ’ , 0 , 0 ) ; %create dialog number 0

32 while 0== dialogs ( ’ ver i fy ’ , 0 , stop dialog ) ,for k=1: inputs ,

eval ( [ ’ [ data inp , time inp ]= ’ , ack functions ( k , : ) , ’ ; ’ ] ) ;raw data vector ( k, s l ide ptr )= data inp ;

36 raw time vector ( k, s l ide ptr )= time inp /1000000−base time ;if s l ide ptr>window size ,

data vector ( k , : ) = . . .raw data vector ( k , ( s l ide ptr−window size +1): s l ide ptr ) ;

40 time vector ( k , : ) = . . .raw time vector ( k , ( s l ide ptr−window size +1): s l ide ptr ) ;

elsedata vector ( k , ( window size−s l ide ptr +1): window size ) = . . .

44 raw data vector ( k ,1 : s l ide ptr ) ;t ime vector ( k , ( window size−s l ide ptr +1): window size ) = . . .

raw time vector ( k ,1 : s l ide ptr ) ;data vector ( k , 1 : ( window size−s l ide ptr ) ) = . . .

48 raw data vector ( k , ( window size +s l ide ptr +1) : (2∗ window size ) ) ;t ime vector ( k , 1 : ( window size−s l ide ptr ) ) = . . .

raw time vector ( k , ( window size +s l ide ptr +1) : (2∗ window size ) ) ;end;

52 end;

2-19

responce =feval ( contro l funct ion , inputs , data vector , time vector ) ;eval ( resp function ) ;s l ide ptr =s l ide ptr +1;

56 if s l ide ptr >2∗window size , s l ide ptr =1; end;enddialogs ( ’ delete ’ , 0 , stop dialog ) ;

%——contfuzzy1——function data out =contfuzzy1 ( input number , data vect , time vect )

global basef is pendfis4

base speed const =0.3;pend speed const =0.3;

8 last data =length ( data vect ( 2 , : ) ) ;

current base posit ion =mean( data vect ( 2 , : ) ) ;current base speed =mean( diff ( data vect ( 2 , : ) ) / mean( diff ( time vect ( 2 , : ) ) ) ) ;

12

current pend posit ion =mean( data vect ( 1 , : ) ) ;current pend speed =mean( diff ( data vect ( 1 , : ) ) / mean( diff ( time vect ( 1 , : ) ) ) ) ;

16 base out =eva l f i s ( [ current base posit ion , . . .base speed const ∗ current base speed ] , basef is ) ;

pend out=eva l f i s ( [ current pend posit ion , . . .20 pend speed const ∗ current pend speed , . . .

base out ] , pendfis ) ;

data out =1.5∗ base out ;

To fully clarify the things, we’ll decided to attach the source of the two utility functions that areused for creation of interface elements (graph windows, buttons, etc.), dialogs and button_messages.

%——dialogs——function reply =dialogs ( action , type , handle )

4 switch action ,case ’ create ’ ,

switch type ,case 0 ,

8 hroot =figure ( ’ MenuBar’ , ’ none ’ , . . .’ NumberTitle ’ , ’ o f f ’ , . . .’ Resize ’ , ’ o f f ’ , . . .’ Position ’ , [ 1 0 0 , 2 0 0 , 1 0 0 , 1 0 0 ] ) ;

12

hbutt=btngroup ( hroot , . . .

2-20

’ GroupID ’ , ’ req1 ’ , . . .’ ButtonID ’ , ’ stop ’ , . . .

16 ’ ButtonColor ’ , [ 1 , 0 , 0 ] , . . .’ IconFunctions ’ , ’ button messages ( 0 ) ’ ) ;

reply =hroot ;

20 case 1 ,hroot =figure ( ’ MenuBar’ , ’ none ’ , . . .

’ NumberTitle ’ , ’ o f f ’ , . . .’ Resize ’ , ’ o f f ’ , . . .

24 ’ Position ’ , [ 2 0 0 , 2 0 0 , 4 0 0 , 4 0 0 ] ) ;reply =hroot ;

otherwise ,28

end

case ’ ver i fy ’ ,32 switch type ,

case 0 ,reply =btnstate ( handle , ’ req1 ’ , ’ stop ’ ) ;

36 otherwise ,

end

40 case ’ delete ’switch type ,

case 0 ,close ( handle ) ;

44

otherwise ,

end48

endpause ( 0 ) ;

%——button messages——function hx=button messages ( type )

4 switch type ,case 0 ,hx=text ( ’ Position ’ , [ 0 . 5 , 0 . 5 , 0 ] , . . .

’ String ’ , ’ STOP’ , . . .8 ’ Color ’ , [ 1 , 1 , 1 ] , . . .

’ HorizontalAlignment ’ , ’ center ’ , . . .

2-21

’ VerticalAlignment ’ , ’ middle ’ , . . .’ FontSize ’ , 2 0 , . . .

12 ’ FontWeight ’ , ’ bold ’ ) ;otherwise ,

end;

Now, having a good design prototype we were ready for the next stage.

2.2.4 Going COur final goal was the development of the kernel space driver, but before that we had to moveall the functionality (with the exception of the controller script) from the client Matlab code tothe driver C code. Because it is extremely unsafe to code the kernel-space modules (major systemcorruption can occur), we chose first to replace the user space driver with another one, to test aC implementation of the controller framework. The driver-client transport was shared memoryregion and the data transfer functionality was distributed between Matlab and driver process.For shared memory to be useful, it’s structure and access policies must be defined and acceptedby all clients. The definition is done in the file shm-interface.h. The Matlab side is served bythe Mex module, dtclient.c. Mex is a special executable format (and API) that is used to writethe extensions to the Matlab. Mex files are loaded into the Matlab process in the runtime andare dynamically linked with the Matlab internals, providing the additional functionality to theMatlab environment. Our Mex file was made available in Matlab under the name dtclient andwas used in conjunction with few Matlab scripts, between them dttest, used to run the testbenchand dtfilter, used as a trial controller. The utility function dialogs remained unchanged, and thesystem timer was made available in the Matlab by means of Mex module telltime, which is similarto the original C function get_time_stamp.

/∗——shm-interface.h——∗/#include < stdio . h>#include < sys / types . h>

4 #include < sys / ipc . h>#include < sys / sem. h>

/∗ Misc data structures : ∗/8 typedef struct

int16 t data buffer [ 1 0 2 4 ] ; / / f ixed window size − 1k wordsint s l ide ptr ;int req gain ;

12 int update state ; / / how many times updated before read/ / requested sample time for channel ( usec − 32 bits )unsigned long req sample time ;/ / sample time actually obtained ( usec − 32 bits )

16 unsigned long obt sample time ;unsigned long last accessed ;

adc def block ;

20 / / Number of DAC channels

2-22

#define DAC NUM CHANNELS 2

typedef struct 24 char adc usage flag [16 ] ;

/ / ’ u ’ s lot shall be used , ’ n ’ s lot shall not be used

int p r e f i l l s i z e ; / / how many data shall we prepare for c l i ent28

adc def block adc contro l b lock [16 ] ;int16 t dac data [DAC NUM CHANNELS] ;

32 /∗ In each set : even id ’ s − server side , odd id ’ s − c l i en t side ∗/key t control sem set ; / / key of control set semaphore ( 1 )/∗ Semaphore set : sem #0 : control set semaphore

sems #1−#16 : ADC semaphores36 ∗/

char go f lag ; /∗ ’ r ’ − data loaded and act ive’ m’ − data was recent ly modified

40 ’ i ’ − data invalid − do nothing’ h ’ − c l i en t hard ex i t − purge all shm and ex i t

∗/ contro l b lock ;

44

/ / Key for shared resources#define BASE KEY 0 x0281100

48

int sem release ( int sem handle , int sem offset )

struct sembuf my semop;52

my semop. sem num=sem offset ;my semop. sem op =1;my semop. sem flg =0;

56

return semop( sem handle ,&my semop , 0 ) ;

60 int sem get ( int sem handle , int sem offset )

struct sembuf my semop;

64 my semop. sem num=sem offset ;my semop. sem op=−1;my semop. sem flg =0;

68 return semop( sem handle ,&my semop , 0 ) ;

2-23

/∗——dtclient.c——∗/#include < sys / ipc . h>#include < sys / shm. h>

4 #include < sys / sem. h>#include < errno . h>#include ”shm−inter face . h”#include < sys / types . h>

8 #include < sys / timex . h>

unsigned long get time stamp ( void )12

struct timex timevalue ;

timevalue . modes=0;16 if (−1==adjtimex (&timevalue ) )

return 0 ;else return ( timevalue . time . tv sec % 4096)

∗1000000+ timevalue . time . tv usec ;20

/ / Matlab s p e c i f i c includes :#include <mex. h>

24 #include <matrix . h>

#define MAX DATA SIZE 1000

28

/∗ MeX entry point ∗/void mexFunction ( int nlhs , mxArray ∗ plhs [ ] , int nrhs , const mxArray ∗ prhs [ ] )

32 /∗ This function rece ives as argument a name of contro l l e r function .It is called as a form [ b u f f e r s i z e s , channel gains , channel times ]=contro l l e r ( ’ t e l ldata ’ ) to rece ive run−time parameters , or as[ out values , s ta tus f lag ]= contro l l e r ( ’ evaluate ’ , in values ) ,

36 or possibly in the form [ status name , s ta tus f lag ]=contro l l e r ( ’ ident i fy ’ ) . < s ta tus f lag > can be of the form ’ r ’ toreconfigure , ’ a ’ to abort or ’ c ’ to continue running .

∗/40 mxArray ∗ contr arguments [ 5 ] ,∗ contr output [ 5 ] ;

int dims [ 5 ] ; / / integer array to use with mx functionschar ∗ contr name ,∗ common string ;int m, n, p r e f i l l v a l u e ;

44 int input buf fer s izes [16 ] ;int input gains [16 ] ;

2-24

unsigned long input sample times [16 ] ;unsigned long recovery time ;

48 / / Server s p e c i f i c data :int control shm handle ;int control sem handle ;contro l b lock ∗ current contro l set ;

52 void ∗ some pointer ;

if ( nrhs !=1)56

mexPrintf ( ”%s , % s ” , ” Call format must be : dtc l i ent ( contro l ler ) ” ,”< contro l ler > being a valid function name.\ n” ) ;

mexErrMsgTxt( ” Incorrect number of parameters supplied ” ) ;60

if ( ! mxIsChar( prhs [ 0 ] ) )mexErrMsgTxt( ” Invalid data supplied . ” ) ;

64 m=mxGetM( prhs [0 ] )∗mxGetN( prhs [0 ] )∗ sizeof ( mxChar)+1;contr name=mxCalloc (m, sizeof ( char ) ) ;/ / Now I know what to ca l l !mxGetString ( prhs [ 0 ] , contr name ,m) ;

68

/ / I shall ver i fy supplied contro l ler function :/ / First of a l l − parameters for ’ ident i fy ’ ca l l :contr arguments [0]= mxCreateString ( ” ident i fy ” ) ;

72 mexCallMATLAB(2 , contr output ,1 , contr arguments , contr name ) ;mxDestroyArray ( contr arguments [ 0 ] ) ;/ / The contro l l er must return i de nt i f i ca t i on string and/ / ’ c ’ as the status value :

76 if ( ! mxIsChar( contr output [ 0 ] ) | | ! mxIsChar( contr output [ 1 ] ) )mexErrMsgTxt( ” Not sure I ’ ve got a good contro l ler function . ” ) ;

if ( ’ c ’ ! = ( char ) mxGetScalar ( contr output [ 1 ] ) )mexErrMsgTxt( ” Not sure I ’ ve got a good contro l ler function . ” ) ;

80

m=mxGetM( contr output [0 ] )∗mxGetN( contr output [0 ] )∗ sizeof ( mxChar)+1;common string =(char∗) mxCalloc (m, sizeof ( char ) ) ;mxGetString ( contr output [ 0 ] , common string ,m) ;

84 mexPrintf ( ” Controller says that he is %s .\ n” , common string ) ;mxFree( common string ) ;

mxDestroyArray ( contr output [ 0 ] ) ;88 mxDestroyArray ( contr output [ 1 ] ) ;

/ / I need now to seek for the server :if (−1==( control shm handle =

92 shmget (BASE KEY, sizeof ( contro l b lock ) ,0 x01b6 ) ) )/ / Mode : ” rw rw rw ”

2-25

mxErrMsgTxt( ” Failed to connect to Shared Memory transport . ” ) ;

96 current contro l set =( contro l b lock ∗) shmat( control shm handle ,NULL, 0 ) ;

if (−1==( control sem handle =semget ( current contro l set−>control sem set ,17,0 x01b6 ) ) )

100 shmdt( current contro l set ) ;mxErrMsgTxt( ” Failed to obtain Semaphore set ” ) ;

104

re in i t ia l i ze run t ime data :/ / I shall try now to i n i t i a l i z e run−time data :

108 contr arguments [0]= mxCreateString ( ” te l ldata ” ) ;mexCallMATLAB(3 , contr output ,1 , contr arguments , contr name ) ;mxDestroyArray ( contr arguments [ 0 ] ) ;/ / First argument − buffer sizes :

112 memcpy( input buf fer s izes ,( int ∗) mxGetData( contr output [ 0 ] ) ,sizeof ( int )∗16) ;

/ / Second argument − channel gains :116 memcpy( input gains , ( int ∗) mxGetData( contr output [ 1 ] ) , sizeof ( int )∗16) ;

/ / Third argument − smaple times :memcpy( input sample times ,

( unsigned long∗) mxGetData( contr output [ 2 ] ) ,120 sizeof ( unsigned long )∗16) ;

/ / Let me f i l t e r out de f in i t ion data :n=0;

124 for (m=0;m<16;m++)

if ( input buf fer s izes [m]<0)input buf fer s izes [m]=0;

128 else if ( input buf fer s izes [m]>MAX DATA SIZE)input buf fer s izes [m]=MAX DATA SIZE;

if ( input buf fer s izes [m]>n ) n=input buf fer s izes [m] ;

132 if ( input gains [m]<2) input gains [m]=1;else if ( input gains [m]<4) input gains [m]=2;else if ( input gains [m]<8) input gains [m]=4;else input gains [m]=8;

136

/ / Prepare to server act ivat ion :sem get ( control sem handle , 0 ) ; / / Lock control block

140 current contro l set−>p r e f i l l s i z e =n;p r e f i l l v a l u e =n;

2-26

n=0;for (m=0;m<16;m++)

144 if ( input buf fer s izes [m]<1)

current contro l set−>adc usage flag [m]= ’ n ’ ;else

148 sem get ( control sem handle ,m+1);current contro l set−>adc contro l b lock [m] . req gain =

input gains [m] ;152 current contro l set−>adc contro l b lock [m] . req sample time =

input sample times [m] ;sem release ( control sem handle ,m+1);current contro l set−>adc usage flag [m]= ’ u ’ ;

156 n++;

current contro l set−>go f lag =’m’ ;

160 sem release ( control sem handle , 0 ) ;usleep (1000) ;/ / Let us give a chance to server to pick up values/ / ( server does pol l ing of the control block )

164 / / Few retr ies wil l indicate us that server has gone ” kaput ” !for (m=0;m<10;m++)

sem get ( control sem handle , 0 ) ;168 mexPrintf ( ” Status : % c\n” , current contro l set−>go f lag ) ;

if ( current contro l set−>go f lag ==’ r ’ ) goto normal execution ;else

172 sem release ( control sem handle , 0 ) ;usleep (10000);

176 / / Here is my space−time to die !shmdt( current contro l set ) ;mexErrMsgTxt( ” The server don ’ t want to work ! ” ) ;/ / Abnormal exit by now!

180 //−−−−−−−−−−−−−−−−−−−−−−−−−

/ / The l ight side of l i f e − everything seems working :normal execution :

184 / / I need to obtain data from contro l ler and to package i t into the/ / c e l l array/ / as channel index 1 − obt . sample time[ −−data−− ]

188 / / Command for contro l ler :contr arguments [0]= mxCreateString ( ” evaluate ” ) ;

2-27

/ / We have channel count in ’ n ’ after last count :contr arguments [1]= mxCreateCellMatrix (1 , n ) ; / / Container for arguments

192 / / I shall populate the argument container with data :n=0;recovery time =0;for (m=0;m<16;m++)

196 / / Four arguments per ce l l ( channel ) :/ / ’ update count ’ ( int ) ,/ / ’ obt . sample time ’ ( int32 scalar ) ,

200 / / ’ data vector ’ ( buffer size int16 vector ) ,/ / ’ transport delay ’ ( int32 scalar ) .if ( input buf fer s izes [m] ! = 0 ) / / channel in use

204 / / ADC values semaphore range (1−16)sem get ( control sem handle ,m+1);

/ / I ’ l l use contr arguments [ 2 ] as temporary holder :208 contr arguments [2]= mxCreateCellMatrix ( 1 , 4 ) ;

/ / Time and update values :/ / I ’ l l need one more temporary mxArray :dims [0]=1; dims [1]=1;

212 contr arguments [3]=mxCreateNumericArray (2 , dims , mxINT32 CLASS,mxREAL) ;

some pointer =mxCalloc (1 , sizeof ( int ) ) ;∗( int ∗) some pointer =

216 current contro l set−>adc contro l b lock [m] . update state ;mxSetData( contr arguments [ 3 ] , ( int ∗) some pointer ) ;/ / First f i e ld of ce l l array is setmxSetCell ( contr arguments [ 2 ] , 0 , contr arguments [ 3 ] ) ;

220

contr arguments [3]=mxCreateNumericArray (2 , dims , mxUINT32 CLASS,mxREAL) ;

some pointer =mxCalloc (1 , sizeof ( unsigned long ) ) ;224 ∗( unsigned long∗) some pointer =

current contro l set−>adc contro l b lock [m] . obt sample time ;recovery time +=∗(unsigned long∗) some pointer ∗ p r e f i l l v a l u e ;mxSetData( contr arguments [ 3 ] ,

228 ( unsigned long∗) some pointer ) ;/ / Second f i e ldmxSetCell ( contr arguments [ 2 ] , 1 , contr arguments [ 3 ] ) ;

232 / / Data values :some pointer =mxCalloc ( input buf fer s izes [m] , sizeof ( int16 t ) ) ;if ( current contro l set−>adc contro l b lock [m] . s l ide ptr<

input buf fer s izes [m] )236

memcpy(

2-28

( int16 t ∗) some pointer ,current contro l set−>adc contro l b lock [m] . data buffer ,

240 current contro l set−>adc contro l b lock [m] . s l ide ptr ∗sizeof ( int16 t ) ) ;

memcpy(244 ( int16 t ∗) some pointer +

current contro l set−>adc contro l b lock [m] . s l ide ptr ,current contro l set−>adc contro l b lock [m] . data buffer +1023− input buf fer s izes [m]+

248 current contro l set−>adc contro l b lock [m] . s l ide ptr ,( input buf fer s izes [m]−

current contro l set−>adc contro l b lock [m] . s l ide ptr )∗sizeof ( int16 t ) ) ;

252 else

memcpy(( int16 t ∗) some pointer ,

256 current contro l set−>adc contro l b lock [m] . data buffer +current contro l set−>adc contro l b lock [m] . s l ide ptr−input buf fer s izes [m] ,input buf fer s izes [m]∗ sizeof ( int16 t ) ) ;

260

dims [0]=1; dims [1]= input buf fer s izes [m] ;contr arguments [3]= mxCreateNumericArray (2 , dims , mxINT16 CLASS,

mxREAL) ;264 mxSetData( contr arguments [ 3 ] , ( int16 t ∗) some pointer ) ;

/ / Third f i e ldmxSetCell ( contr arguments [ 2 ] , 2 , contr arguments [ 3 ] ) ;dims [0]=1; dims [1]=1;

268 contr arguments [3]= mxCreateNumericArray (2 , dims , mxUINT32 CLASS,mxREAL) ;

some pointer =mxCalloc (1 , sizeof ( unsigned long ) ) ;∗( unsigned long∗) some pointer =

272 get time stamp ()−current contro l set−>adc contro l b lock [m] . last accessed ;

mxSetData( contr arguments [ 3 ] , ( unsigned long∗) some pointer ) ;/ / Last argument

276 mxSetCell ( contr arguments [ 2 ] , 3 , contr arguments [ 3 ] ) ;/ / Data preparedmxSetCell ( contr arguments [ 1 ] , n, contr arguments [ 2 ] ) ;n++;

280 current contro l set−>adc contro l b lock [m] . update state =0;sem release ( control sem handle ,m+1);

284 / / I can now perform a contro l l er ca l l :mexCallMATLAB(2 , contr output ,2 , contr arguments , contr name ) ;

2-29

mxDestroyArray ( contr arguments [ 1 ] ) ;mxDestroyArray ( contr arguments [ 0 ] ) ;

288

/ / Now it ’ s a time to analize a contro l ler ’ s output :/ / Output 0 is a two element vector to put into dac ’ s/ / (−1 being invariant ) ;

292 / / Output 1 is a status f lag to determine the future of run .

/ / First of a l l I ’ m concerned with contro l ler status :switch ( ( char ) mxGetScalar ( contr output [ 1 ] ) )

296 case ’ r ’ :

/ / contro l l er asks for new arrangementsgoto re in i t ia l i ze run t ime data ;

300 case ’ c ’ :/ / Normal state of a f fa i r s : I ’ l l send the output to the server .some pointer =mxCalloc (2 , sizeof ( int16 t ) ) ;some pointer =mxGetData( contr output [ 0 ] ) ;

304 for (m=0;m<DAC NUM CHANNELS;m++)if ( ∗ ( ( int16 t ∗) some pointer +m)>−1)

current contro l set−>dac data [m]=∗ ( ( int16 t ∗) some pointer +m) ;

308 / / Allow recovery time for server :usleep ( recovery time ) ;

goto normal execution ; / / proceed as usual312 case ’ a ’ : / / abort service loop and exit

sem get ( control sem handle , 0 ) ;current contro l set−>go f lag =’ h ’ ; / / Ki l l serversem release ( control sem handle , 0 ) ;

316 break ;default :

mexWarnMsgTxt(” Unexpected output from contro l ler − I shall leave at once ! ” ) ;

320 sem get ( control sem handle , 0 ) ;/ / Invalidate contro l ler data ( makes i t to go to idle loop )current contro l set−>go f lag =’ i ’ ;sem release ( control sem handle , 0 ) ;

324 / / Exit procedures :shmdt( current contro l set ) ;return ;

328

%——dttest——global dialog handle ;global sample time ;

2-30

4 global plot handle ;global total t ime ;global resultant ;

8 total t ime =1;

dialog handle =dialogs ( ’ create ’ , 0 , 0 ) ;%plot handle =dialogs ( ’ create ’ , 1 , 0 ) ;

12 %hold on ;

dtc l ient ( ’ d t f i l t e r ’ ) ;%dialogs ( ’ de le te ’ , 0 , plot handle ) ;

16 dialogs ( ’ delete ’ , 0 , dialog handle ) ;

%——dtfilter——function [ varargout ] = d t f i l t e r ( varargin )% d t f i l t e r − An exapmle of f i l t e r for my DT−Matlab link .

4

% There is only a few ways to cal l this function :% One or two input arguments ;% Two or three output arguments ;

8 % 3 allowed cal l forms .global sample time ;

persistent channel buf fer s ize ;12 persistent channel gain ;

persistent channel times ;persistent run info ;persistent output ;

16 global total t ime ;global dialog handle ;global resultant ;

20 if ( size ( channel buf fer s ize )==0 )%I n i t i a l values ( no working cont ro l l e r s ) :channel buf fer s ize =zeros ( 1 , 1 6 ) ;channel buf fer s ize (5)=500;

24 channel buf fer s ize (4)=500;channel gain =ones ( 1 , 1 6 ) ;channel times =ones (1,16)∗50000;% Assumint int32 machine data size :

28 channel buf fer s ize =int32 ( channel buf fer s ize ) ;channel gain =int32 ( channel gain ) ;channel times =uint32 ( channel times ) ;% Not running : ’ n ’ − not ready , ’ r ’ − ready

32 run info = ’ n ’ ;end

2-31

k=0;36 time used =0;

% For ’ t e l ldata ’ and ’ ident i fy ’ forms :if ( nargin==1)

40 if ( nargout==2)% ’ ident i fy ’ formif strcmp( ’ ident i fy ’ , varargin 1)

varargout 1= ’ t r i a l contro l ler , version 1 ’ ;44 varargout 2= ’ c ’ ;

return ;elsereturn ;

48 end;else if ( nargout==3)

% ’ te l ldata ’ formif strcmp( ’ te l ldata ’ , varargin 1)

52 varargout 1= channel buf fer s ize ;varargout 2= channel gain ;varargout 3= channel times ;return ;

56 elsereturn ;

end;end;

60 return ;end;

else if ( nargin==2)if ( nargout==2)

64 if strcmp( ’ evaluate ’ , varargin 1)if ( run info == ’ n ’ )% Was not i n i t i a l i z e d by now:% I ’ l l request channels 3 & 4 as inputs to forward them as sum

68 % to 1 ( with 3 order f i l t e r ) ;% Matlab indexing goes from 1 ( channels in C notation ) .% Sample rate will be 100 sps for each channel :varargout 1= output ;

72 varargout 2= ’ r ’ ;run info = ’ r ’ ;return ;

else if ( run info == ’ r ’ )76 fprintf (2 , ’ Update count − %d\n ’ , double ( varargin 211 ) ) ;

fprintf (2 , ’ S. time : %d\n ’ , double ( varargin 212 ) ) ;fprintf (2 , ’ T. delay : %d\n ’ , double ( varargin 214 ) ) ;if ( double ( varargin 211)>0)

80 resultant (1 , total t ime )= tel l t ime−double ( varargin 214 ) ;resultant (2 , total t ime )= double ( varargin 213 ( . . .

2-32

double ( channel buf fer s ize ( 5 ) ) ) ) ;pause ( 30 ) ;

84

%endvarargout 1=[ int16 (−1) , int16 (−1)] ;varargout 2= ’ c ’ ;

88 if ( or ( dialogs ( ’ ver i fy ’ , 0 , dialog handle ) , total t ime >2000))varargout 2= ’ a ’ ;resultant 1= double ( varargin 213 ) ;resultant 2= double ( varargin 223 ) ;

92 end;total t ime =2001;

return ;end

96 endend

endend

100 end

/∗——telltime.c——∗/#include < sys / types . h>#include < sys / time . h>

4

/ / Matlab s p e c i f i c includes :#include <mex. h>#include <matrix . h>

8

unsigned long get time stamp ( void )

12 struct timex timevalue ;

timevalue . modes=0;if (−1==gettimeofday (&timevalue ,NULL) )

16 return 0 ;else return timevalue . time . tv sec ∗1000000L+timevalue . time . tv usec ;

20

/∗ MeX entry point ∗/void mexFunction ( int nlhs , mxArray ∗ plhs [ ] , int nrhs , const mxArray ∗ prhs [ ] )

24 double ∗ time var ;

time var =(double ∗ ) mxCalloc (1 , sizeof ( double ) ) ;

2-33

28 ∗ time var =(double ) (1 .0∗ get time stamp ( ) ) ;plhs [0]= mxCreateDoubleMatrix ( 1 , 1 , mxREAL) ;mxSetPr( plhs [ 0 ] , time var ) ;

The driver was rewritten to utilize some more advanced access policies. Precise sampling wasintroduced, by means of interpolation of data sampled at random time. The ADC access routinesbecame smarter because of utilization of latching policy of the card (the routine first instructsthe card to start conversion with the new gain/channel settings and only then reads the alreadyobtained value from the ADC latch, reducing a minimal achievable sampling time). The utilityfunctions are defined in the dt2811.h (second revision) and the main driver body is given in thedtserver.c.

/∗——dt2811.h(v2)——∗/#include < std l ib . h>#include < stdio . h>

4 #include < sys / types . h>#include <asm/ io . h>

/ / Boolean type :8 #undef TRUE

#undef FALSE

#if defined ( cplusplus )12 #define TRUE ( ( bool )TRUE)

#define FALSE ( ( bool )FALSE)#elsetypedef int bool ;

16 #define TRUE ( ( bool )1)#define FALSE ( ( bool )0)#endif

20

/∗Card data : dt2811∗/

24 #define BASE PORT 0x218 / / base adapter port ( FD)

#define ADCSR BASE PORT+ 0 / / Control \Status register#define ADGCR BASE PORT+ 1 / / Gain\Channel se lect register

28 #define ADDAT LO BASE PORT+ 2 / / ADC low byte#define ADDAT HI BASE PORT+ 3 / / ADC high byte#define DADAT0 LO BASE PORT+ 2 / / DAC0 low bit#define DADAT0 HI BASE PORT+ 3 / / DAC0 high bit

32 #define DADAT1 LO BASE PORT+ 4 / / DAC0 low bit#define DADAT1 HI BASE PORT+ 5 / / DAC0 high bit#define DIOP1 BASE PORT+ 6 / / d ig i ta l IO port 1#define DIOP2 BASE PORT+ 6 / / d ig i ta l IO port 2

2-34

36 #define TMRCTR BASE PORT+ 7 / / timer \ couter register

/ / Minimal sampling period allowed by card ( usec )#define MIN SAMPLE TIME 40

40

int reset card ( void )

if (−1==ioperm (BASE PORT, 8 , 1 ) )44 return −1;

outb (0 x10 ,ADCSR) ;usleep (110) ;inb (ADDAT LO) ;

48 inb (ADDAT HI) ;/ / Make a card busy :outb (0 x00 ,ADGCR) ;return 0 ;

52

int8 t normalize gain ( int rgain ) / / Allowed gain values .

56 if ( rgain <2) return 1 ;else if ( rgain <4) return 2 ;else if ( rgain <8) return 4 ;else return 8 ;

60

bool get hard data ( const int8 t channel ,const int8 t gain ,

64 int16 t ∗ hardvalue ) / / read ADC

/ / used to i n i t i a l i z e hardware registersint8 t reg ADGCR=0, reg DAT HI=0, reg DAT LO=0;

68 int16 t tes t va l =0;/ / This function returns values of PREVIOUS/ / requested gain / channel to reduce idle/ / looping time .

72

if ( ( ( channel <0) | | ( channel>15))| | ( ! ( ( gain ==1) | | ( gain ==2) | | ( gain ==4) | | ( gain ==8) ) ) )

return FALSE; / / data validation76 else / / continuing

/ / Let us set the gain / channel register (ADGCR)reg ADGCR=channel ;

80 switch ( gain )case 1 :

break ;

2-35

84 case 2 : reg ADGCR=reg ADGCR|0 x40 ;break ;

case 4 : reg ADGCR=reg ADGCR|0 x80 ;break ;

88 case 8 : reg ADGCR=reg ADGCR|0 xc0 ;

while ( ! ( 0 x80 & inb (ADCSR) ) ) ; / / busy wait92 reg DAT LO=inb (ADDAT LO) ;

reg DAT HI=inb (ADDAT HI) ;reg DAT HI=reg DAT HI & 0 x0f ;

96

tes t va l =(0 x00ff & reg DAT HI)<<8;tes t va l &=0xff00 ;tes t va l |=0 x00ff & reg DAT LO;

100 ∗hardvalue=test va l ;/ / Restart acquis i t ion :outb ( reg ADGCR,ADGCR) ;return TRUE;

104

bool put hard data ( const int8 t channel , int16 t hardvalue ) / / write DAC108

int8 t reg DAT HI=0, reg DAT LO=0;int16 t tes t va l =0;

112 if ( ( channel <0) | | ( channel>1)) return FALSE;else

reg DAT LO=( int8 t ) ( hardvalue & 0 x00ff ) ;116 reg DAT HI=( int8 t ) ( hardvalue>>8);

tes t va l =(0 x00ff & reg DAT HI )<<8 ; / / monitor valuetes t va l &=0xff00 ;

120 tes t va l |=0 x00ff & reg DAT LO;if ( hardvalue != tes t va l ) return FALSE; / / Endianness test

switch ( channel )124

case 0 :outb ( reg DAT LO, DADAT0 LO) ;outb ( reg DAT HI , DADAT0 HI) ;

128 break ;case 1 :

outb ( reg DAT LO, DADAT1 LO) ;outb ( reg DAT HI , DADAT1 HI) ;

2-36

132 break ;return TRUE;

136

unsigned long get time stamp ( void )140

struct timex timevalue ;

timevalue . modes=0;144 if (−1==adjtimex (&timevalue ) )

return 0 ;else return ( timevalue . time . tv sec % 4096)∗1000000+

timevalue . time . tv usec ;148

/∗——dtserver.c——∗/#include < sys / timex . h>#include < sys / ipc . h>

4 #include < sys / shm. h>#include < sys / sem. h>#include < errno . h>#include < std l ib . h>

8 #include < signal . h>#include <math. h>#include ” dt2811 . h”#include ”shm−inter face . h”

12

char termination status ; / / ’ n ’ − normal , ’ t ’ − terminatevoid s igna l ex i t ( int sig )

16 printf ( ” Signal %d caught !\ n” , sig ) ;termination status =’ t ’ ;

20

int16 t interp foh ( f i r s t va lue , f i r s t t ime , second value , second time )int16 t f i r s t va lue , second value ;unsigned long f i r s t t ime , second time ;

24 int16 t temp str ;temp str =( int16 t ) rint ( f i r s t va lue +1.0∗ f i r s t t ime ∗

( second value−f i r s t va lue ) /28 ( f i r s t t ime +second time ) ) ;

2-37

/ / pr intf ( ” Interpolating − %d\n” , temp str ) ;return temp str ;

32

int main( void )36

/∗ What to do :1 . Get shared memory for control block data .2 . Request semaphore set .

40 3 . I n i t i a l i z e control block data .4 . Wait for syncronization with c l i en t .5 . On c l i en t attach − enter main interact ion loop .

∗/44

contro l b lock ∗ current contro l set ;struct sembuf my semop;int control shm handle ;

48 int control sem handle ;int8 t adc channel gains [16 ] ;

char adc channel usage [ 1 6 ] ; / / ’ u ’ − channel shall be used ,52 / / ’ n ’ − channel shall not be used

unsigned long ref t ime value ;long norm time value ;int16 t dac log [DAC NUM CHANNELS] ; / / DAC reference data

56 int p r e f i l l s i z e ;int16 t some value ;int k, l , last used , f i r s t used ;char f lag value ;

60

struct unsigned long last ack time ;long last check time ;

64 unsigned long req sample time ;unsigned long obt sample time ;int16 t last check value ;int16 t last ack value ;

68 adc wdata [16 ] ;

/ / Check for card access :72 if (−1==reset card ( ) )

return −1;/ / Set DACs to zero :for ( k=0; k<DAC NUM CHANNELS; k++)

76 if ( ! put hard data ( k ,2047))return −1 ; / / Endianness test fa i led

2-38

80

if (−1==( control shm handle =shmget (BASE KEY,

sizeof ( contro l b lock ) ,84 IPC CREAT| IPC EXCL|0 x01b6 ) ) )

/ / Mode : ” rw rw rw ”

printf ( ”\ nShared memory request fa i led \n” ) ;88 return −1;

if (−1==( control sem handle =

semget (BASE KEY,92 17,

IPC CREAT| IPC EXCL|0 x01b6 ) ) )

printf ( ”\nSemaphore request fa i led .\ n” ) ;96 shmctl ( control shm handle , IPC RMID,NULL) ;

return −1;

100 current contro l set =( contro l b lock ∗) shmat( control shm handle ,NULL, 0 ) ;

termination status =’ n ’ ;/ / Handling of terminating signals :

104 signal (SIGHUP, s igna l ex i t ) ;signal ( SIGINT, s igna l ex i t ) ;signal ( SIGQUIT, s igna l ex i t ) ;

108

restart now :/ / Here we shall restart in case of tota l re−i n i t i a l i z a t i o n

112 / / ( such as c l ient detach ) .

/∗ I n i t i a l i z a t i o n ∗// / Unlock al l semaphores ( to get rid of possible stuck c l i ent )

116 semctl ( control sem handle ,0 , SETALL, 1 ) ;/ / Lock al l semaphores :semctl ( control sem handle ,0 , SETALL, 0 ) ;

120 current contro l set−>p r e f i l l s i z e =1;

for ( k=0; k<16;k++)

124 current contro l set−>adc usage flag [ k ]= ’ n ’ ;current contro l set−>adc contro l b lock [ k ] . s l ide ptr =0;

2-39

current contro l set−>adc contro l b lock [ k ] . req sample time =0;current contro l set−>adc contro l b lock [ k ] . obt sample time =0;

128 current contro l set−>adc contro l b lock [ k ] . update state =0;current contro l set−>adc contro l b lock [ k ] . last accessed =0;

132 for ( k=0; k<DAC NUM CHANNELS; k++)

current contro l set−>dac data [ k]=2047;dac log [ k]=2047;

136

current contro l set−>control sem set =BASE KEY;140 current contro l set−>go f lag =’ i ’ ; / / data invalid

/ / Prepare to go into service loop :/ / Unlock al l semaphores :semctl ( control sem handle ,0 , SETALL, 1 ) ;

144

/∗ We are now in the ’ invalide ’ s tate . Await entering to ’ modified ’ or∗ ’ hard ex i t ’ s tate

148 ∗/while (1)

/∗ Check for connection each 10 msec ∗/152 usleep (10000);

sem get ( control sem handle , 0 ) ; / / Lock control blockf lag value =current contro l set−>go f lag ;if ( termination status ==’ t ’ ) f lag value =’ h ’ ; / / Signal override

156 if ( f lag value ==’ h ’ ) / / Drop server

sem release ( control sem handle , 0 ) ;/ / Unlock control block and proceed to exit

160 goto clean up and exit ;

else if ( f lag value ==’m’ ) / / Data was modified/ / Go to the main service loop − data is loaded

164 goto s t a r t o f s e r v i c e l o o p ;else / / Do nothing

sem release ( control sem handle , 0 ) ;168 continue ;

172 /∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗ End of in i t sec t ion .

2-40

∗ Start of main body .∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

176 ∗/s t a r t o f s e r v i c e l o o p :/ / Control block shall be locked now. Read it and activate the server ./∗ Activation procedure : for each adc channel requested mark−up buffer

180 ∗ as act ive ; f i l l buffer preamble with data to the in i t length . Write∗ down time s t a t i s t i c s . Update dac channels . Go into serv ice loop .∗/

/ / pr int f ( ” Starting update .\ n” ) ;184 memcpy( adc channel usage ,

current contro l set−>adc usage flag ,16∗ sizeof ( char ) ) ;

last used =−1; / / no channel was in use yet188 for ( k=0; k<16;k++)

if ( adc channel usage [ k]==’ u ’ ) / / channel wil l be needed in the next run

sem get ( control sem handle , k+1);192 / / reset buffer

current contro l set−>adc contro l b lock [ k ] . s l ide ptr =0;adc channel gains [ k]=

normalize gain ( current contro l set−>adc contro l b lock [ k ] .196 req gain ) ;

adc wdata [ k ] . req sample time =current contro l set−>adc contro l b lock [ k ] . req sample time ;

adc wdata [ k ] . obt sample time =adc wdata [ k ] . req sample time ;200 sem release ( control sem handle , k+1);

if ( last used ==−1) last used =k;

204 if ( last used ==−1)

current contro l set−>go f lag =’ i ’ ;sem release ( control sem handle , 0 ) ;

208 goto restart now ;

/ / pr int f ( ” S t i l l al ive \n” ) ;/ / last used is an index of f i r s t i n i t i a l i z e d channel :

212 get hard data ( last used , adc channel gains [ last used ],& some value ) ;

/ / DAC update :for ( k=0; k<DAC NUM CHANNELS; k++)

216 if ( current contro l set−>dac data [ k] != dac log [ k ] )

dac log [ k]= current contro l set−>dac data [ k ] ;put hard data ( k, dac log [ k ] ) ;

220 / / pr int f ( ” Got here .\ n” ) ;

2-41

224 / / Normalize p r e f i l l value :p r e f i l l s i z e =current contro l set−>p r e f i l l s i z e ;if ( p r e f i l l s i z e <1)

228 current contro l set−>p r e f i l l s i z e =1;p r e f i l l s i z e =1;

232 if ( p r e f i l l s i z e >999)

current contro l set−>p r e f i l l s i z e =999;p r e f i l l s i z e =999;

236

/ / pr int f ( ” P r e f i l l value : %d” , p r e f i l l s i z e ) ;f i r s t used =last used ;

240 / / Buffer p r e f i l l :for ( l =0; l<=p r e f i l l s i z e ; l ++)

k=last used +1;244 if ( k>15) k=0;

while ( adc channel usage [ k ] ! = ’ u ’ )

k++;248 if ( k>15) k=0;

get hard data ( k, adc channel gains [ k],& some value ) ;ref t ime value =get time stamp ( ) ;

252 sem get ( control sem handle , last used +1);current contro l set−>adc contro l b lock [ last used ] . data buffer [ l ]=

some value ;current contro l set−>adc contro l b lock [ last used ] . update state ++;

256 current contro l set−>adc contro l b lock [ last used ] . s l ide ptr ++;current contro l set−>adc contro l b lock [ last used ] . last accessed =

ref t ime value ;sem release ( control sem handle , last used +1);

260 adc wdata [ last used ] . last ack time =ref t ime value ;last used =k;

264 / / pr int f ( ”End of update\n” ) ;

268

current contro l set−>go f lag =’ r ’ ;

2-42

/ / Open control block :sem release ( control sem handle , 0 ) ;

272 / / Go to running loop :last used =f i rs t used ;get hard data ( last used , adc channel gains [ last used ],& some value ) ;

276 while (1)

k=last used +1;if ( k>15) k=0;

280 while ( adc channel usage [ k ] ! = ’ u ’ )

k++;if ( k>15) k=0;

284 get hard data ( k, adc channel gains [ k],& some value ) ;ref t ime value =get time stamp ( ) ;

288 norm time value =ref t ime value−adc wdata [ last used ] . last ack time−adc wdata [ last used ] . obt sample time ;

/ / I f i t is time to write data out , norm time value wil l be pos i t ive

292 if ( norm time value<0)

/ / Consider re−adaptation :if ( ( adc wdata [ last used ] . obt sample time>

296 ( adc wdata [ last used ] . req sample time +MIN SAMPLE TIME)) &&( ( ref t ime value−adc wdata [ last used ] . last ack time )<( adc wdata [ last used ] . obt sample time +MIN SAMPLE TIME) ) )

adc wdata [ last used ] . obt sample time−=MIN SAMPLE TIME;300 adc wdata [ last used ] . last check time =norm time value ;

adc wdata [ last used ] . last check value =some value ;

else304

/ / Lock buffer of interestsem get ( control sem handle , last used +1);

308 current contro l set−>adc contro l b lock [ last used ] .data buffer [ current contro l set−>

adc contro l b lock [ last used ] .s l ide ptr ]=

312 interp foh ( adc wdata [ last used ] . last check value ,labs ( adc wdata [ last used ] . last check time ) ,some value , norm time value ) ;

316 / / pr int f ( ” Passing updated value of %d , old of %d\n” ,/ / some value ,

2-43

/ / adc wdata [ last used ] . last check value ) ;/ / pr int f ( ”New time of %d , old of %d\n” ,

320 / / labs ( adc wdata [ last used ] . last check time ) ,/ / norm time value ) ;/ / pr int f (”−−−−−−−−−−−−−−−−\n” ) ;current contro l set−>adc contro l b lock [ last used ] . s l ide ptr ++;

324 current contro l set−>adc contro l b lock [ last used ] . update state ++;if ( current contro l set−>adc contro l b lock [ last used ] . s l ide ptr>

1023)current contro l set−>adc contro l b lock [ last used ] . s l ide ptr =0;

328

current contro l set−>adc contro l b lock [ last used ] .obt sample time =adc wdata [ last used ] . obt sample time ;

current contro l set−>adc contro l b lock [ last used ] .332 last accessed =ref t ime value−norm time value ;

sem release ( control sem handle , last used +1);

/ / Cycle skip ?336 if ( norm time value +labs ( adc wdata [ last used ] . last check time )>

adc wdata [ last used ] . obt sample time )/ / Add one periodadc wdata [ last used ] . obt sample time +=MIN SAMPLE TIME;

340

adc wdata [ last used ] . last ack time =ref t ime value ;adc wdata [ last used ] . last check time =norm time value ;adc wdata [ last used ] . last check value =some value ;

344 / / Touch al l DAC’ s for each accessed ADCfor ( l =0; l<DAC NUM CHANNELS; l ++)

if ( current contro l set−>dac data [ l ] != dac log [ l ] )348 / / Data modified

put hard data ( l , current contro l set−>dac data [ l ] ) ;dac log [ l ]= current contro l set−>dac data [ l ] ;

352 last used =k;

/ / Check the current status of control block :356 sem get ( control sem handle , 0 ) ;

f lag value =current contro l set−>go f lag ;sem release ( control sem handle , 0 ) ;if ( termination status ==’ t ’ ) f lag value =’ h ’ ; / / Signal override

360 switch ( f lag value )case ’ i ’ : / / Data was invalidated − return to f i r s t level loop :

goto restart now ;364 case ’ m’ : / / Client ins i s t of changes

goto s t a r t o f s e r v i c e l o o p ; / / Start from the beginning

2-44

case ’ h ’ : / / Client asks us to commit suicidegoto clean up and exit ;

368 default :continue ; / / Else − keep going

372 clean up and exit : / / Last label in this program/ / We shall delete al l used shm and al l used semaphores and leave :/ / Destroying semaphore :semctl ( control sem handle ,0 , IPC RMID,NULL) ;

376 / / Detaching shm blocksshmdt( current contro l set ) ;shmctl ( control shm handle , IPC RMID,NULL) ;

380 return 0 ;

2.3 An accomplished system

2.3.1 General description

The study of the conceptual designs allowed us to develop a final version of the card interface (andof the whole system in general). But before that, we had to agree on guidelines that will governthe software development.

The GNU/Linux environment offers enormous number of ways to do a given task. This is par-tially due to the inherent flexibility of the descendants of the Unix operation system and partiallydue to the a large number of people involved in the development of the GNU/FSF3 projects. Thechoice shall be made between all available alternatives on the basis of software stability and easeof implementation.

The DT2811 data acquisition card lacks nearly all auxiliary features that are needed for thecontrol system design. Thus all this features shall be emulated by software running on the CPU.To allow clean programming interface and speed of reaction, the software extension of the cardfunctions shall be programmed as a kernel module. The client software will then communicatewith the driver via the system interface, being totally unaware of the hardware limitations. Thereare two primary way to do this. First of them is to use custom code and standard module interfaceof the Linux kernel. The second way is to use RTLinux environment and the Comedy hardwareinterface. The Comedy framework is an attempt to provide a standard API for the different DAQcards. Programs are unaware of the real hardware, because Comedy module takes care to emulatethose supported features that are not present in hardware. However, the software interface ofthe Comedy cannot support some useful features (such as automatic channel set scanning), soit has to be aided by the user supplied code via the RTLinux interface. RTLinux is really anoperating system that lives within a Linux kernel and works rather independently of it. It comeswith a distinct set of the features to aid the development of the RT control programs and it’s ownprocess scheduler. Unfortunately, it is not very portable, because it needs a specially patched and

3Free Software Foundation - a non-profit organization of the developers of open source software

2-45

recompiled Linux kernel, so the software products developed for RTLinux are different to transferto another machines (or even to the other versions of Linux kernel; contemporary RTLinux is verysensitive to the minor differences in the different versions of the Linux kernel code).

Because of all these reasons we chose to implement our software on the basis of the standardLinux kernel API to allow binary and source level portability to the all Linux 2.2 platforms. Theinterface between kernel mode driver and user mode clients we chose to be filesystem based (itprovides a native concurrency synchronization, unlike a shared memory mechanisms). To avoid apossible major number4 conflicts, we decided not to use dev 5 interface, but to create an endpointson the procfs. Procfs is a virtual filesystem, mounted normally on the /proc directory, that is usedfor communication with the kernel internals. File operations upon a procfs do not cause real diskaccess and, normally, cannot do much harm to the system. The read/write operations on the procfsfiles are very fast, because they are memory to memory transfers, with no disk involvement.

The final program was build in a modular fashion. There is a common kernel interface block,that registers the driver and seeks for the working card. The different card interfaces are pre-sented to the client by the different endpoints and are managed by the distinct program blocks.Here we have a small table that presents an implemented features.

Driver endpoint Hardware features Software features Shall be implementeddt2811.adc Gain set, Channel set,

Read ADC, Externaltrigger, Continuoussampling6

Read buffer (FIFO), Timestamping (softwaretimer), First orderinterpolation, Continuoussampling, Channel subsetinterleaving, Optimizedcard control

Higher order interleavingand data estimation,Finer timer access (directto hardware), Externalevent handling andprocessing

dt2811.dac Write DAC DAC read-back, Timestamping

Write buffer (advancedFIFO), Fixed rateconversion

dt2811.dig Data output, Data input,Timer/Counterfunctionality7

Time stamping Data I/O buffer, Fixed ratedata transfer

The features in the last column were not implemented in the current release of the driverbecause of lack of time. We devoted very little to the design of digital interface, because it was notneeded in our hardware setup (but it is required for the more advanced transducers, such as thosedescribed in the section 1.4 above. The DAC access routines were left somewhat primitive too andlater we were forced to revert to the simpler data interpolation algorithm for the ADC data thanwas planned initially. Another important feature that was not implemented is a synchronizedclient scheduling. It is needed to reduce the bad effects of the random sampling that is caused bythe task rescheduling by the kernel. Because of the high complexity of this feature we decided todiscard it’s development.

The program component dependencies (just like in the Makefile) are given on the Figure 2.1.4The dev kernel interface requires that all endpoints (special files) will have a so called major number that tells the

kernel what driver shall be used when the endpoint is accessed.5A somewhat archaic way to access hardware drivers under Unix.6Inadequate - implemented in software7System timer is used instead

2-46

Figure 2.1: Driver component dependencies

dt2811main.o

xx'& %$ ! "#dt2811.o dt2811proc.ooo dt2811main.c

kk

dt2811dac.o

OO

dt2811hw.o

kk

dt2811proc.c

jj

dt2811adc.o

88

dt2811adc.coo dt2811dig.o

ccGGGGGGGGGGGGGGGGGGGGGG

dt2811hw.c

kk

dtadc.c

OO

adcint.c

ee

dt2811dac.c

ccGGGGGGGGGGGGGGGGGGGGGGG

dt2811dig.c

kk

2.3.2 Linux kernel interface

The Linux kernel is a complex piece of software, undergoing constant development and evolution.It is coded by the big and variegated community of the loosely related people and it still not reachedit’s maturity. However it is sufficiently stable (certain versions of kernel on certain hardwarepresent a rock-solid stability; such were a 2.0.3x kernels on Intel Pentium Pro hardware) to afforda long term usage and it’s fully open source is a great aid for the system programming.

The entry point of the driver is defined by the dt2811main.c. However to do it’s work it relieson the primary header file, dt2811ports.h, which is similar to the reference include file for DT2811card (just like a dt2811.h include file). The only minor differences are the standard header filesincluded (because current driver is a kernel module and not a user program) and a fact that short,card control functions were converted to macros (of course, card control register, ADCSR now ac-cepts a different value, because interrupt delivery from the card is enabled). This file also includesa prototypes.h header file that defines all external function prototypes to support inter-componentlinkage.

/∗——dt2811ports.h——∗/#define DEFAULT BASE PORT 0x218 / / base adapter port ( FD)#define DEFAULT CARD IRQ 0x07 / / default adapter IRQ

4

#define ADCSR ( card conf ig . base addr + 0 ) / / Control \Status#define ADGCR ( card conf ig . base addr + 1 ) / / Gain\Channel se lect#define ADDAT LO ( card conf ig . base addr + 2 ) / / ADC low byte

8 #define ADDAT HI ( card conf ig . base addr + 3 ) / / ADC high byte#define DADAT0 LO ( card conf ig . base addr + 2 ) / / DAC0 low bit#define DADAT0 HI ( card conf ig . base addr + 3 ) / / DAC0 high bit#define DADAT1 LO ( card conf ig . base addr + 4 ) / / DAC1 low bit

12 #define DADAT1 HI ( card conf ig . base addr + 5 ) / / DAC1 high bit#define DIOP1 ( card conf ig . base addr + 6 ) / / d ig i ta l IO port 1#define DIOP2 ( card conf ig . base addr + 6 ) / / d ig i ta l IO port 2#define TMRCTR ( card conf ig . base addr + 7 ) / / timer \ couter

16

2-47

#define MIN SAMPLE TIME 40 / / Minimal sampling period allowed by/ / card ( usec )

20

/∗ In time of writing card was configured with :∗ 1 . ADC range of +/−5V∗ 2 . ADC mode set to ” Single ended ” .

24 ∗ 3 . DAC0/1 range of +/−5V∗/

28 /∗ Includes for module compilation : ∗/

#include < linux / sched . h>#include < linux / tqueue . h>

32 #include < linux / interrupt . h>#include < linux / time . h>#include < linux / delay . h>#include < linux / proc fs . h>

36 #include < linux / ioport . h>#include <asm/ io . h>#include <asm/ types . h>#include <asm/ spinlock . h>

40 #include <asm/ uaccess . h>

/ / Special card actions :44 /∗ Reset / i n i t i a l i z e card ∗/

#define CARD RESET outb (0 x10 ,ADCSR) ; udelay (110) ; inb (ADDAT LO) ; \inb (ADDAT HI)

/∗ Enable card interrupt signall ing ∗/48 #define ACTIVATE CARD outb (0 x04 ,ADCSR) ; udelay (110)

#include ” prototypes . h”

52 #define ADC IFACE 0#define DAC IFACE 1#define DIG IFACE 2

/∗——prototypes.h——∗// / Provided by dt2811adc :int in i t adc ( void ) ;

4 int adc f i l e a t tach ( struct inode ∗ , struct f i l e ∗ ) ;int adc f i l e detach ( struct inode ∗ , struct f i l e ∗ ) ;s s i ze t adc te l l back ( struct f i l e ∗ , char ∗ , s i ze t , l o f f t ∗ ) ;s s i ze t adc get conf ig ( struct f i l e ∗ , const char ∗ , s i ze t , l o f f t ∗ ) ;

8

2-48

/ / Provided by dt2811dac :int in i t dac ( void ) ;int dac f i l e a t tach ( struct inode ∗ , struct f i l e ∗ ) ;

12 int dac f i l e detach ( struct inode ∗ , struct f i l e ∗ ) ;s s i ze t dac te l l back ( struct f i l e ∗ , char ∗ , s i ze t , l o f f t ∗ ) ;s s i ze t dac get conf ig ( struct f i l e ∗ , const char ∗ , s i ze t , l o f f t ∗ ) ;

16 / / Provided by dt2811dig :int in i t d ig ( void ) ;int d i g f i l e a t t a c h ( struct inode ∗ , struct f i l e ∗ ) ;int d ig f i l e de tach ( struct inode ∗ , struct f i l e ∗ ) ;

20 ss i ze t d ig te l l back ( struct f i l e ∗ , char ∗ , s i ze t , l o f f t ∗ ) ;s s i ze t d ig get conf ig ( struct f i l e ∗ , const char ∗ , s i ze t , l o f f t ∗ ) ;

/ / Provided by dt2811proc :24 void proc clean up ( void ) ;

int proc setup ( void ) ;

28 / / Provided by dt2811hw :unsigned long kget time stamp ( void ) ;int ver i fy card ( void ) ;

32 / / Provided by dt2811main :void inc use count ( void ) ;void dec use count ( void ) ;

/∗——dt2811main.c——∗// / Revision 2 .

4 /∗∗ This program is a dt2811 driver to be used for analog data∗ inter fac ing using aforementioned card .∗/

8

#include ” dt2811ports . h”12

#include < linux / kernel . h>#include < linux / module . h>

16

/∗∗ Primary sce le ton :∗/

20

2-49

/ / I allow two parameters to be supplied to module :int io = −1 ; / / Base IO portint irq = −1 ; / / irq of card

24

MODULEPARM( io , ” i ” ) ;MODULEPARM( irq , ” i ” ) ;

28 static struct

int base addr ;int irq ;

32 card conf ig ;

static struct semaphore use count m=MUTEX;36 void inc use count ( void )

down(&use count m ) ;MOD INC USE COUNT;

40 up(&use count m ) ;

void dec use count ( void )44

down(&use count m ) ;MOD DEC USE COUNT;up(&use count m ) ;

48

void hw clean up ( void )

52 CARD RESET;f ree i rq ( card conf ig . irq ,NULL) ;release region ( card conf ig . base addr , 8 ) ;

56

/∗ Module i n i t i a l i z a t i o n routine : ∗/int init module ( void )

60

printk (KERN INFO ” Driver for Data Translation DT2811 D/ A.\ n” ) ;64 if ( io ==−1) io =DEFAULT BASE PORT;

if ( irq ==−1) irq =DEFAULT CARD IRQ;

card conf ig . base addr =io ;68 card conf ig . irq =irq ;

2-50

printk (KERN INFO ” dt2811 : Using base I / O port %x , IRQ %x .\ n” ,card conf ig . base addr , card conf ig . irq =irq ) ;

72

if ( check region ( card conf ig . base addr , 8 ) )

printk (KERN ERR ” dt2811 : I / O port %x already in use .\ n” ,76 card conf ig . base addr ) ;

return −ENODEV;

80 if ( ! ver i fy card ( ) )

printk (KERN ERR ” dt2811 : Cannot use IRQ %x or card not found .\ n” ,card conf ig . irq ) ;

84 return −ENODEV;

request region ( card conf ig . base addr ,8 , ”DT2811” ) ;88

/ / PROC interface for control interract ion :if ( ! proc setup ( ) )

92 printk (KERN ERR ” dt2811 : Failed to attach a PROC interface .\ n” ) ;hw clean up ( ) ;return −EINVAL;

96 return 0 ;

100 /∗ Module throw−away routine : ∗/extern int d e s t r o y f i l e s t r u c t ( void ) ;

void cleanup module ( void )104

hw clean up ( ) ;proc clean up ( ) ;

108 d e s t r o y f i l e s t r u c t ( ) ;

The dt2811main.c program component is responsible for the registration of the driver in thekernel registry (if it can be called so; in the Linux drivers are managed trough some special datastructure). The driver have to allocate interrupt request slot and I/O slot for hardware access andit shall also take care of it endpoints. File dt2811hw.c supposed to be used to settle the hard-

2-51

ware issues. In these release it provides the familiar (somewhat rewritten) timer access functionkget_time_stamp and the card check function, verify_card. The check is done as following: usersupplied or default I/O configuration is assumed to be correct and used to program the card (towrite the certain data to some I/O ports); it the card is really there it will rise an interrupt requestin a foreseeable time. Interrupt handler can then examine the needed I/O ports to reduce theprobability of error to minimum (this is a maximum we can do to detect an old card; modern PnPdevices have a special facilities to aid detection and recognition of hardware).

When card existence is verified, driver endpoints are then created by means of calling theproc_setup function from the dt2811proc.c file. This will cause three special files to appear:/proc/dt2811.adc for ADC access, /proc/dt2811.dac for DAC access and /proc/dt2811.dig for ac-cess to card’s digital port. Also the last file is somewhat unclear, the explanation of it functionalitywould take a distinct article, so we will skip it.

/∗——dt2811hw.c——∗/#include ” dt2811ports . h”

4 extern struct

int base addr ;int irq ;

8 card conf ig ;

/ / A flag to indicate proper interrupt handling ( ’ s ’ − success ,/ / ’ f ’ − fa i lure ) ;

12 static char success f lag ;

/∗ Test irq handler for card checking ∗/void t es t card i rq ( int irq , void ∗ dev id , struct pt regs ∗ regs )

16

/ / I was cal led by interrupt of dt2811 :

20 if (0 x80 & inb (ADCSR) ) success f lag =’ s ’ ;

24

int ver i f y i rq ( void )

if ( card conf ig . irq ==2)28

card conf ig . irq = 9 ; / / Is real ly irq2 tied to irq9 ? I bel ieve so .return 1 ;

32

if ( card conf ig . irq ==3 | |card conf ig . irq ==5 | |card conf ig . irq ==7) return 1 ;

2-52

36 else return 0 ;

/∗Check I/O configuration for validity∗/40 int ver i fy card ( void )

int k;44

if ( ! ve r i f y i rq ( ) ) / / Check for valid IRQreturn 0 ;

48

/ / Try to al locate an interrupt for test handler :if (0> request irq ( card conf ig . irq , t es t card i rq ,0 ,

52 ” test handler for dt2811 ” ,NULL) )return 0 ;

CARD RESET;56

/ / Set−up t r i a l conversion :success f lag =’ f ’ ; / / Preset fa i lure valueACTIVATE CARD;

60 outb (0 x00 ,ADGCR) ; / / Start convertion now.udelay (45 ) ;for ( k=0; k<10;k + + ) / / Poll for end−of−conversion

64 if (0 x80 & inb (ADCSR) ) break ;udelay ( 5 ) ;

68 f r ee i rq ( card conf ig . irq ,NULL) ;inb (ADDAT LO) ;inb (ADDAT HI) ;

72 if ( k>9 ) / / All re tr ies fa i led − something is very wrong .return 0 ;

CARD RESET;76

if ( success f lag != ’ s ’ ) / / handler was not invoked properlyreturn 0 ;

else / / everything works al l−right80 return 1 ; / / Card is found − allowed to proceed

/∗Read system µs timer∗/

2-53

84 unsigned long kget time stamp ( void )

struct timeval ktime ;get fast t ime (&ktime ) ;

88 return ktime . tv sec ∗1000000L+ktime . tv usec ;

/∗——dt2811proc.h——∗/#include ” dt2811ports . h”

4 #define NO VERSION

static struct f i l e opera t i ons d t d r i v e r i n t e r f a c e f i l e [ 3 ] ;static struct inode operations dtdr iver inter face inode [ 3 ] ;

8 static struct proc dir entry d t f i l e s [ 3 ] ;

void setup proc data ( void )

12 d t d r i v e r i n t e r f a c e f i l e [ ADC IFACE] . read=adc te l l back ;d t d r i v e r i n t e r f a c e f i l e [ ADC IFACE] . write =adc get conf ig ;d t d r i v e r i n t e r f a c e f i l e [ ADC IFACE] . open=adc f i l e a t tach ;d t d r i v e r i n t e r f a c e f i l e [ ADC IFACE] . release =adc f i l e detach ;

16

d t d r i v e r i n t e r f a c e f i l e [ DAC IFACE] . read=dac te l l back ;d t d r i v e r i n t e r f a c e f i l e [ DAC IFACE] . write =dac get conf ig ;d t d r i v e r i n t e r f a c e f i l e [ DAC IFACE] . open=dac f i l e a t tach ;

20 d t d r i v e r i n t e r f a c e f i l e [ DAC IFACE] . release =dac f i l e detach ;

d t d r i v e r i n t e r f a c e f i l e [ DIG IFACE] . read=dig te l l back ;d t d r i v e r i n t e r f a c e f i l e [ DIG IFACE] . write =d ig get conf ig ;

24 d t d r i v e r i n t e r f a c e f i l e [ DIG IFACE] . open= d i g f i l e a t t a c h ;d t d r i v e r i n t e r f a c e f i l e [ DIG IFACE] . release =d ig f i l e de tach ;

28 dtdr iver inter face inode [ ADC IFACE] . d e f a u l t f i l e o p s =&d t d r i v e r i n t e r f a c e f i l e [ADC IFACE] ;

dtdr iver inter face inode [ DAC IFACE] . d e f a u l t f i l e o p s =&d t d r i v e r i n t e r f a c e f i l e [DAC IFACE] ;

32 dtdr iver inter face inode [ DIG IFACE] . d e f a u l t f i l e o p s =&d t d r i v e r i n t e r f a c e f i l e [ DIG IFACE] ;

d t f i l e s [ADC IFACE] . low ino =0;36 d t f i l e s [ADC IFACE] . namelen=10;

d t f i l e s [ADC IFACE] . name=” dt2811 . adc ” ;d t f i l e s [ADC IFACE] . mode=S IFREG|S IRUGO|S IWUGO;d t f i l e s [ADC IFACE] . nlink =1;

40 d t f i l e s [ADC IFACE] . uid =0;

2-54

d t f i l e s [ADC IFACE] . gid =0;d t f i l e s [ADC IFACE] . size =0;d t f i l e s [ADC IFACE] . ops=&dtdr iver inter face inode [ ADC IFACE] ;

44

d t f i l e s [DAC IFACE] . low ino =0;d t f i l e s [DAC IFACE] . namelen=10;d t f i l e s [DAC IFACE] . name=” dt2811 . dac ” ;

48 d t f i l e s [DAC IFACE] . mode=S IFREG|S IRUGO|S IWUGO;d t f i l e s [DAC IFACE] . nlink =1;d t f i l e s [DAC IFACE] . uid =0;d t f i l e s [DAC IFACE] . gid =0;

52 d t f i l e s [DAC IFACE] . size =0;d t f i l e s [DAC IFACE] . ops=&dtdr iver inter face inode [ DAC IFACE] ;

d t f i l e s [ DIG IFACE] . low ino =0;56 d t f i l e s [ DIG IFACE] . namelen=10;

d t f i l e s [ DIG IFACE] . name=” dt2811 . dig ” ;d t f i l e s [ DIG IFACE] . mode=S IFREG|S IRUGO|S IWUGO;d t f i l e s [ DIG IFACE] . nlink =1;

60 d t f i l e s [ DIG IFACE] . uid =0;d t f i l e s [ DIG IFACE] . gid =0;d t f i l e s [ DIG IFACE] . size =0;d t f i l e s [ DIG IFACE] . ops=&dtdr iver inter face inode [ DIG IFACE] ;

64

void proc clean up ( void )

68 proc unregister (&proc root , d t f i l e s [ADC IFACE] . low ino ) ;proc unregister (&proc root , d t f i l e s [DAC IFACE] . low ino ) ;proc unregister (&proc root , d t f i l e s [ DIG IFACE] . low ino ) ;

72

int i n i t s p e c i a l ( void )

setup proc data ( ) ;76

if ( ! ( in i t adc () && ini t dac () && in i t d i g ) )return 0 ;

return 1 ;80

/∗Register driver endpoints∗/int proc setup ( void )

84

if ( ! i n i t s p e c i a l ( ) ) return 0 ;

88 if (0> proc reg is ter (&proc root ,& d t f i l e s [ADC IFACE] ) )

2-55

return 0 ;if (0> proc reg is ter (&proc root ,& d t f i l e s [DAC IFACE] ) )

92 proc unregister (&proc root , d t f i l e s [ADC IFACE] . low ino ) ;return 0 ;

if (0> proc reg is ter (&proc root ,& d t f i l e s [ DIG IFACE] ) )

96 proc unregister (&proc root , d t f i l e s [ADC IFACE] . low ino ) ;proc unregister (&proc root , d t f i l e s [DAC IFACE] . low ino ) ;return 0 ;

100

return 1 ;

2.3.3 Simpler card subsystems

The DAC access and digital port operations has been supplied with only the basic set of features.Time stamping of transactions and status read-back were implemented and thats it. Some abso-lute positions in the files /proc/dt2811.dac and dt2811.dig are tied to the internal driver variables(for both data values and time stamps) and reading or writing them will cause a certain hardwareoperations to be initiated (say, writing into the first word8 of file /proc/dt2811.dac will cause achange in the voltage at DAC channel 0 of the card; in the same time the second dword of thisfile will accept the current µs timer value to be used as time-stamp). Here we have a softwareresponsible for this functionality - files dt2811dac.c and dt2811dig.c.

/∗——dt2811dac.c——∗/#include ” dt2811ports . h”

4

extern struct f i l e opera t i ons d t d r i v e r i n t e r f a c e f i l e [ 3 ] ;extern struct inode operations dtdr iver inter face inode [ 3 ] ;extern struct proc dir entry d t f i l e s [ 3 ] ;

8 extern struct

int base addr ;int irq ;

12 card conf ig ;

#define DAC NUM CHANNELS 2

16 void dac set value ( u8 , u16 ) ;

static struct

8word = 2 bytes, dword = 4 bytes

2-56

20 u16 current dac data ;unsigned long last access t ime ; / / When DAC was last written ?

dac data [DAC NUM CHANNELS] ;

24 #define RECORD SIZE ( sizeof ( u16)+ sizeof ( unsigned long ) )

static struct semaphore dac access m =MUTEX;

28 int in i t dac ( void )

d t f i l e s [DAC IFACE] . size =DAC NUM CHANNELS∗RECORD SIZE;return 1 ;

32

int dac f i l e a t tach ( struct inode ∗ inode , struct f i l e ∗ f i l e )

36 inc use count ( ) ;

return 0 ;

40

int dac f i l e detach ( struct inode ∗ inode , struct f i l e ∗ f i l e )

dec use count ( ) ;44

return 0 ;

48

static ss i ze t dac te l l back ( struct f i l e ∗ f i l e ,char∗ buffer ,s i ze t length ,

52 l o f f t ∗ l o f f s e t )

int o f f se t , base rec , inc rec , k, i n o f f , l in c , l o f f , m off ;

56

o f f se t =( long )∗ l o f f s e t ;

if ( o f f se t>=DAC NUM CHANNELS∗RECORD SIZE) / / signal EOF and exit60 return 0 ;

if ( o f f se t +length>=DAC NUM CHANNELS∗RECORD SIZE)/ / requested more data than availablelength =DAC NUM CHANNELS∗RECORD SIZE−o f f se t ;

64

base rec =o f f se t / RECORD SIZE;inc rec =( o f f se t +length ) / RECORD SIZE;i n o f f =0;

2-57

68 m off =o f f se t ;

down(&dac access m ) ; / / to prevent consistency fault upon re−entrant/ / ca l l

72

for ( k=base rec ; k<=inc rec ; k++)

if ( k==0)76

l in c =RECORD SIZE−m off ;l o f f =m off ;

80 else

l in c =RECORD SIZE−m off %(k∗RECORD SIZE) ;l o f f =m off %(k∗RECORD SIZE) ;

84 if ( l in c +m off>length +o f f se t )

l inc =length +o f f se t−m off ;copy to user ( buffer + i n o f f , ( ( char∗)& dac data [ k])+ l o f f ,

88 l in c ) ;i n o f f +=l inc ;m off +=l inc ;

92

up(&dac access m ) ;/ / o f f se t =k ; / / New of f se t in f i l e/ / I need no real need to advance a f i l e o f f se t , because most

96 / / read / writes are essent ia l ly from the same place .return length ;

100

static ss i ze t dac get conf ig ( struct f i l e ∗ f i l e ,const char∗ buffer ,s i ze t length ,

104 l o f f t ∗ l o f f s e t )

int o f f se t , base rec , inc rec , k, i n o f f , l in c , l o f f , m off ;u16 test value ;

108

o f f se t =( long )∗ l o f f s e t ;

if ( o f f se t>=DAC NUM CHANNELS∗RECORD SIZE) / / signal EOF and exit112 return 0 ;

if ( o f f se t +length>=DAC NUM CHANNELS∗RECORD SIZE)/ / requested more data than availablelength =DAC NUM CHANNELS∗RECORD SIZE−o f f se t ;

2-58

116

base rec =o f f se t / RECORD SIZE;inc rec =( o f f se t +length ) / RECORD SIZE;i n o f f =0;

120 m off =o f f se t ;

down(&dac access m ) ; / / to prevent consistency fault upon re−entrant/ / ca l l

124

for ( k=base rec ; k<=inc rec ; k++)

test value =dac data [ k ] . current dac data ;128 if ( k==0)

l in c =RECORD SIZE−m off ;l o f f =m off ;

132 else

l in c =RECORD SIZE−m off %(k∗RECORD SIZE) ;136 l o f f =m off %(k∗RECORD SIZE) ;

if ( l in c +m off>length +o f f se t )

l inc =length +o f f se t−m off ;140 copy from user ( ( ( char∗)& dac data [ k])+ l o f f ,

buffer + i n o f f ,l in c ) ;

i n o f f +=l inc ;144 m off +=l inc ;

if ( test value != dac data [ k ] . current dac data )

dac set value ( ( u8) k, dac data [ k ] . current dac data ) ;148 dac data [ k ] . last access t ime =kget time stamp ( ) ;

152

up(&dac access m ) ;/ / o f f se t =k ; / / New of f se t in f i l e/ / I need no real need to advance a f i l e o f f se t , because most

156 / / read / writes are essent ia l ly from the same place .return length ;

160

void dac set value ( u8 channel , u16 value )

u8 reg DAT HI=0, reg DAT LO=0;

2-59

164

reg DAT LO=(u8 ) ( value & 0 x00ff ) ;reg DAT HI=(u8 ) ( value>>8);

168 switch ( channel )case 0 :

outb ( reg DAT LO, DADAT0 LO) ;172 outb ( reg DAT HI , DADAT0 HI) ;

break ;case 1 :

outb ( reg DAT LO, DADAT1 LO) ;176 outb ( reg DAT HI , DADAT1 HI) ;

break ;default :

return ;180

return ;

/∗——dt2811dig.c——∗/#include ” dt2811ports . h”

4

static struct semaphore dig access m =MUTEX;

extern struct f i l e opera t i ons d t d r i v e r i n t e r f a c e f i l e [ 3 ] ;8 extern struct inode operations dtdr iver inter face inode [ 3 ] ;

extern struct proc dir entry d t f i l e s [ 3 ] ;extern struct

12 int base addr ;int irq ;

card conf ig ;

16 #define BUFFER SIZE 2∗ ( sizeof ( u8)+ sizeof ( unsigned long ) )

/ / Register 0 used only for input ( last access t ime set on read ,/ / write ignored )

20 / / Register 1 used only for output ( last acces t ime set on write )static union

struct24

u8 current reg data ;unsigned long last access t ime ; / / When channel was last accessed

dig reg is ters [ 2 ] ;

2-60

28 char data buffer [ BUFFER SIZE] ; dig inter face ;

int in i t d ig ( void )32

d t f i l e s [ DIG IFACE] . size =BUFFER SIZE;return 1 ;

36

int d i g f i l e a t t a c h ( struct inode ∗ inode , struct f i l e ∗ f i l e )

40 inc use count ( ) ;

return 0 ;

44

int d ig f i l e de tach ( struct inode ∗ inode , struct f i l e ∗ f i l e )

dec use count ( ) ;48

return 0 ;

52 static ss i ze t d ig te l l back ( struct f i l e ∗ f i l e ,char∗ buffer ,s i ze t length ,l o f f t ∗ l o f f s e t )

56 long o f f se t ;

o f f se t =( long )∗ l o f f s e t ;60

if ( o f f se t>BUFFER SIZE) return 0 ;if ( o f f se t +length>BUFFER SIZE)

length =BUFFER SIZE−o f f se t ;64

down(&dig access m ) ;

if ( o f f se t <1)68

dig inter face . d ig reg is ters [ 0 ] . current reg data =inb ( DIOP1) ;d ig inter face . d ig reg is ters [ 0 ] . last access t ime =kget time stamp ( ) ;

72

copy to user ( buffer ,d ig inter face . data buffer +o f f se t ,

2-61

76 length ) ;

up(&dig access m ) ;return length ;

80

static ss i ze t d ig get conf ig ( struct f i l e ∗ f i l e ,84 const char∗ buffer ,

s i ze t length ,l o f f t ∗ l o f f s e t )

88 long o f f se t ;

u8 test values [ 2 ] ;

92 o f f se t =( long )∗ l o f f s e t ;

if ( o f f se t>BUFFER SIZE) return 0 ;if ( o f f se t +length>BUFFER SIZE)

96 length =BUFFER SIZE−o f f se t ;

down(&dig access m ) ;test values [0]= dig inter face . d ig reg is ters [ 0 ] . current reg data ;

100 test values [1]= dig inter face . d ig reg is ters [ 1 ] . current reg data ;

copy from user ( d ig inter face . data buffer +o f f se t ,buffer , length ) ;

104

if ( test values [0 ] != d ig inter face . d ig reg is ters [ 0 ] . current reg data )d ig inter face . d ig reg is ters [ 0 ] . current reg data =test values [ 0 ] ;

if ( test values [1 ] != d ig inter face . d ig reg is ters [ 1 ] . current reg data )108

outb ( d ig inter face . d ig reg is ters [ 1 ] . current reg data , DIOP2) ;d ig inter face . d ig reg is ters [ 1 ] . last access t ime =kget time stamp ( ) ;

112 up(&dig access m ) ;

return length ;

2.3.4 ADC subsystem

The ADC subsystem is the one most loaded with features, because it is a critical link in the com-puter based controller. The user interface follows the same approach that other component do -internal data structures are mapped to the fixed positions in the /proc/dt2811.adc file. Howeverthe complexity of internal structures is much greater. The data must always be presented to the

2-62

user in the ordered, FIFO order and block reads/writes must be supported. The ADC subsystemrepeats the whole driver in miniature, because it has it’s own, internal prototypes file - adcsupp.h,special hardware back-end, including interrupt handler, quick value cache and filter (here givenin the reduced form; we were unable to debug a better version in the given time) - adcint.c andthe endpoint management module, dtadc.c that is capable of supporting block transactions andconsistent data ordering. The functionality is integrated in the dt2811adc.c file, that exports it’stop-level definitions to the main driver component.

/∗——adcsupp.h——∗/typedef enum UP WR, UP RD, NO UP, CH AC item types ;#define NUMPARAMS 5

4 #define NUM DATAS 128#define ADC NUM CHANNELS 16#include ” dt2811ports . h”

8

int chan sync ( int channel , int value ) ;int up cnt ( int channel , int value ) ;

12 s i ze t f i l e t o u s e r ( o f f t o f f se t , s i ze t length , char∗ u buf ) ;s i ze t u s e r t o f i l e ( o f f t o f f se t , s i ze t length , const char∗ u buf ) ;int push data ( int channel , u int16 t data ) ;int b u i l d f i l e s t r u c t ( void ) ;

16 int d e s t r o y f i l e s t r u c t ( void ) ;

#define GAIN POS 020 #define REQ S TIME 1

#define UP COUNT 2#define OBT S TIME 3#define LAST STAMP 4

24

void irq handler ( int irq , void ∗ dev id , struct pt regs ∗ regs ) ;int modify parameter ( int channel , int posit ion , unsigned long data ) ;unsigned long read parameter ( int channel , int posit ion ) ;

28 int chan sync ( int channel , int t val ) ;int reset sampler ( void ) ;

/∗——adcint.c——∗/#include ” adcsupp . h”#define VICT BUF 20

4 #define BH INT 5#define TIME DEC CONST 10#define WATCHDOGCOUNT 20#define TIME ERR LIMIT 10000

8

static void got new data ( void ∗ new value ) ;int update f i le ( int channel , unsigned long obt sample time ,

2-63

unsigned long time stamp , u int16 t data out ) ;12

inl ine u int16 t interp foh ( u int16 t f i r s t va lue ,u int16 t second value ,unsigned long f i r s t t ime ,

16 unsigned long second time )

unsigned long a1 , a2 ;

20 a1=f i r s t t ime ∗ second value +second time ∗ f i r s t va lue ;a2=f i r s t t ime +second time ;if ( a2==0) return 2 0 4 7 ; / / You got what you wanted !

24 if ( a1%a2<a2 /2 )return ( u int16 t ) ( a1 / a2 ) ;

elsereturn ( u int16 t ) ( a1 / a2 +1);

28

extern struct

32 int base addr ;int irq ;

card conf ig ;

36 static struct

u int8 t n gain ;unsigned long req sample time ;

40 unsigned long obt sample time ;unsigned long last time stamp ;unsigned long t o ta l va l ;unsigned long total count ;

44 unsigned long var value ;int s skips ;

gear data [ADC NUM CHANNELS] ;

48 static item types s state ;static item types c state [ADC NUM CHANNELS] ;spinlock t in lock ;

52 static struct

int channel ;u int16 t data ;

56 in buf fer [ VICT BUF] ;static int b ptr ;static int w channel ;

2-64

static int ov f lag ;60 static int w flag ;

void irq handler ( int irq , void ∗ dev id , struct pt regs ∗ regs )

64 static struct tq struct bh task =NULL,0 , got new data ,NULL ;u int8 t reg DAT HI=0, reg DAT LO=0, chan mask ;u int16 t tes t va l ;int k, l ;

68

if ( s state ==NO UP) return ; / / Stop command obtainedw flag = 0 ; / / watchdog flag/ / Get current card data :

72 reg DAT LO=inb (ADDAT LO) ;reg DAT HI=inb (ADDAT HI) ;tes t va l =(0 x00ff & reg DAT HI)<<8;tes t va l &=0xff00 ;

76 tes t va l |=0 x00ff & reg DAT LO;/ / Submit data to processing queue :in buf fer [ b ptr ] . channel=w channel ;in buf fer [ b ptr ] . data=test va l ;

80 b ptr ++;if ( b ptr>VICT BUF−1)

b ptr =0;84 ov f lag +=1;

/ / Seek for next active channel and trigger a conversion :k=w channel +1;

88 for ( l =0; l<ADC NUM CHANNELS; l ++)

if ( k>ADC NUM CHANNELS−1) k=0;if ( c state [ k]==CH AC)

92 w channel=k;goto new channel found ;

96 else

k++;

return ; / / No new channels100 new channel found :

/ / Build a new gain mask and trigger a conversion :chan mask =(( u int8 t ) w channel ) | gear data [ w channel ] . n gain ;outb ( chan mask ,ADGCR) ;

104 / / Schedule BH for execution ( if needed ) :if ( b ptr>BH INT)

2-65

queue task (&bh task , & tq immediate ) ;108 mark bh(IMMEDIATE BH) ;

112 static void got new data ( void ∗ new value )

int k, ptr t ;unsigned long time stamp , t norm , temp1 ;

116 u int16 t temp2 ;

spin lock (& in lock ) ;ptr t =( ov f lag >0)?VICT BUF: b ptr ;

120

if ( ptr t >0)

/ / Init run :124 for ( k=0; k<ptr t ; k++)

gear data [ in buf fer [ k ] . channel ] . t o ta l va l +=in buf fer [ k ] . data ;gear data [ in buf fer [ k ] . channel ] . total count ++;

128 time stamp=kget time stamp ( ) ;t norm =( gear data [ k ] . obt sample time !=0)?

time stamp%gear data [ k ] . obt sample time : time stamp ;132 ov f lag =0;

b ptr =0;

for ( k=0; k<ADC NUM CHANNELS; k++)136 if ( c state [ k]==CH AC)

140 if ( labs ( time stamp−gear data [ k ] . last time stamp )>0.9∗ gear data [ k ] . obt sample time )

if ( gear data [ k ] . total count >0)144 temp2=( u int16 t ) ( gear data [ k ] . t o ta l va l /

gear data [ k ] . total count ) ;else

temp2=( u int16 t ) gear data [ k ] . t o ta l va l ;148 gear data [ k ] . t o ta l va l =0;

gear data [ k ] . total count =0;

gear data [ k ] . last time stamp =time stamp ;152 update f i le ( k,

gear data [ k ] . obt sample time ,time stamp , temp2 ) ;

2-66

156

160 spin unlock (& in lock ) ;

int reset sampler ( void )164

int k;

for ( k=0; k<ADC NUM CHANNELS; k++)168

gear data [ k ] . n gain =1;gear data [ k ] . req sample time =0;gear data [ k ] . obt sample time =0;

172 gear data [ k ] . last time stamp =0;gear data [ k ] . t o ta l va l =0;gear data [ k ] . total count =0;

176 c state [ k]=NO UP;

s state =NO UP;b ptr =0;

180 w channel =0;ov f lag =0;w flag =0;s p i n l o c k i n i t (& in lock ) ;

184 CARD RESET;return 1 ;

188 inl ine int update f i le ( int channel ,unsigned long obt sample time ,unsigned long time stamp ,u int16 t data out )

192 modify parameter ( channel , OBT S TIME, obt sample time ) ;push data ( channel , data out ) ;up cnt ( channel , 1 ) ;

196 modify parameter ( channel , LAST STAMP, time stamp ) ;return 1 ;

200 inl ine u int8 t normalize gain ( int gain in )

if ( gain in <1.5)

2-67

return 0 ;204 else if ( gain in <3)

return 0 x40 ;else if ( gain in <6)

return 0 x80 ;208 else return 0 xc0 ;

static int chan sync ( int channel , int t val )212

int k=0, l =0;unsigned long incoming time ;

216 if ( channel <0 | | channel>ADC NUM CHANNELS−1)return 0 ;

spin lock (& in lock ) ;220 w flag ++;

if ( t val ==0)

gear data [ channel ] . n gain =224 normalize gain ( ( int ) read parameter ( channel , GAIN POS) ) ;

incoming time =read parameter ( channel , REQ S TIME) ;if ( gear data [ channel ] . req sample time ==0 && incoming time>0)

228 c state [ channel ]=CH AC;else if ( incoming time ==0) c state [ channel ]=NO UP;

gear data [ channel ] . req sample time =incoming time ;232 gear data [ channel ] . obt sample time =incoming time ;

for ( k=0; k<ADC NUM CHANNELS; k++)if ( c state [ k]==CH AC) l ++;

236

if ( l>0 && s state ==NO UP)

s state =CH AC;240 goto restart card ;

if ( l ==0 && s state ==CH AC) s state =NO UP;if ( w flag>WATCHDOGCOUNT&& s state ==CH AC)

244 w flag−=WATCHDOGCOUNT/2 ;goto restart card ;

248

spin unlock (& in lock ) ;return 1 ;

2-68

restart card :252 for ( k=0; k<ADC NUM CHANNELS; k++)

if ( c state [ k]==CH AC) break ;w channel=k;spin unlock (& in lock ) ;

256 u int8 t chan mask ;chan mask =(( u int8 t ) w channel ) | ( gear data [ w channel ] . n gain<<6);CARD RESET;

260 ACTIVATE CARD;outb ( chan mask ,ADGCR) ;

return 2 ;

264

/∗——dtadc.c——∗/#include < linux / malloc . h>#include < linux / stddef . h>

4 #include ” adcsupp . h”

extern long a c t i v e f i l e l e n g t h ;

8 static struct

struct

12 unsigned long pdata ;item types item type ;int (∗ update ) ( int , int ) ;

params[NUMPARAMS] ;16 u int16 t chan data [NUM DATAS] ;

int d ptr ;unsigned int rec s ize ; / / Size of channel data

rec anchors [ADC NUM CHANNELS] ;20

int up cnt ( int channel , int value ) ;int chan sync ( int channel , int t val ) ;

24

int b u i l d f i l e s t r u c t ( void )

int k, l ;28

a c t i v e f i l e l e n g t h =0;for ( k=0; k<ADC NUM CHANNELS; k++)

32 rec anchors [ k ] . d ptr =0;

2-69

for ( l =0; l<NUMPARAMS; l ++)

rec anchors [ k ] . params[ l ] . pdata =0;36 switch ( l )

case GAIN POS:

rec anchors [ k ] . params[ l ] . item type =UP WR;40 rec anchors [ k ] . params[ l ] . update=chan sync ;

break ;case UP COUNT:

rec anchors [ k ] . params[ l ] . item type =UP RD;44 rec anchors [ k ] . params[ l ] . update=up cnt ;

break ;case REQ S TIME:

rec anchors [ k ] . params[ l ] . item type =UP WR;48 rec anchors [ k ] . params[ l ] . update=chan sync ;

break ;default :

rec anchors [ k ] . params[ l ] . item type =NO UP;52 rec anchors [ k ] . params[ l ] . update=NULL;

for ( l =0; l<NUM DATAS; l ++)56 rec anchors [ k ] . chan data [ l ]=0;

rec anchors [ k ] . rec s i ze =sizeof ( unsigned long )∗NUMPARAMS+sizeof ( u int16 t )∗NUM DATAS;

a c t i v e f i l e l e n g t h +=rec anchors [ k ] . rec s i ze ;60

return 1 ;

64

int d e s t r o y f i l e s t r u c t ( void )

return 1 ;68

int push data ( int channel , u int16 t data )

72 if ( channel<0 && channel>ADC NUM CHANNELS−1)return 0 ;

if ( rec anchors [ channel ] . d ptr>NUM DATAS−1 | |rec anchors [ channel ] . d ptr<0)

76 rec anchors [ channel ] . d ptr =0;

rec anchors [ channel ] . chan data [ rec anchors [ channel ] . d ptr ]= data ;rec anchors [ channel ] . d ptr ++;

80 if ( rec anchors [ channel ] . d ptr>NUM DATAS−1)

2-70

rec anchors [ channel ] . d ptr =0;

return 1 ;84

int modify parameter ( int channel , int posit ion , unsigned long data )

88

if ( channel<0 && channel>ADC NUM CHANNELS−1)return 0 ;

if ( posit ion>LAST STAMP | | posit ion <0)92 return 0 ;

96 rec anchors [ channel ] . params[ posit ion ] . pdata=data ;

return 1 ;

100

unsigned long read parameter ( int channel , int posit ion )

if ( channel<0 && channel>ADC NUM CHANNELS−1)104 return 0 ;

if ( posit ion>LAST STAMP | | posit ion <0)return 0 ;

108 return rec anchors [ channel ] . params[ posit ion ] . pdata ;

typedef struct rec112

char∗ data ;int length ;item types item type ;

116 int (∗ update ) ( int , int ) ; rec ;inl ine rec get next ( int channel , int act )

120 static unsigned int pos ;rec temp rec ;int temp pos ; ;

124 if ( channel <0 | | channel>ADC NUM CHANNELS−1)

temp rec . data=NULL;temp rec . length =0;

128 return temp rec ;

2-71

switch ( act )132

case 0 :pos =0;temp rec . data =(char∗)(& rec anchors [ channel ] . params[ pos ] . pdata ) ;

136 temp rec . length =sizeof ( unsigned long ) ;temp rec . item type =rec anchors [ channel ] . params[ pos ] . item type ;temp rec . update=rec anchors [ channel ] . params[ pos ] . update ;break ;

140 case 1 :pos ++;if ( pos>NUMPARAMS+NUM DATAS−1)

144 pos =0;temp rec . data=NULL;temp rec . length =0;temp rec . item type =NO UP;

148 temp rec . update=NULL;

else if ( pos<NUMPARAMS)

152 temp rec . data=( char∗)(& rec anchors [ channel ] . params[ pos ] . pdata ) ;

temp rec . length =sizeof ( unsigned long ) ;temp rec . item type =rec anchors [ channel ] . params[ pos ] . item type ;

156 temp rec . update=rec anchors [ channel ] . params[ pos ] . update ;

else

160 temp pos=rec anchors [ channel ] . d ptr−1−pos+NUMPARAMS;if ( temp pos<0) temp pos=NUM DATAS+temp pos ;if ( temp pos<0) temp pos =0;temp rec . data=

164 ( char∗)(& rec anchors [ channel ] . chan data [ temp pos ] ) ;temp rec . length =sizeof ( u int16 t ) ;temp rec . item type =NO UP;temp rec . update=NULL;

168 break ;

default :temp rec . data=NULL;

172 temp rec . length =0;temp rec . item type =NO UP;temp rec . update=NULL;return temp rec ;

176

2-72

return temp rec ;

180

184 static s i ze t f i l e t o u s e r ( o f f t o f f se t , s i ze t length , char∗ u buf )

o f f t cur pos ;s i ze t d length ;

188 int c rec , i n o f f , in length ;rec th is rec ;

if ( o f f se t>a c t i v e f i l e l e n g t h −1) return 0 ; / / EOF condition192 if ( o f f se t +length>a c t i v e f i l e l e n g t h )

length = a c t i v e f i l e l e n g t h−o f f se t ;

c rec =0;196 cur pos =0;

d length =0;while ( c rec<ADC NUM CHANNELS)

200 if ( cur pos +rec anchors [ c rec ] . rec s i ze<=o f f se t )cur pos +=rec anchors [ c rec ] . rec s i ze ;

else

204 th is rec =get next ( c rec , 0 ) ;while ( th is rec . data !=NULL)

if ( th is rec . length +cur pos<o f f se t )208 cur pos +=this rec . length ;

else

i n o f f =o f f se t−cur pos ;212 in length =this rec . length−i n o f f ;

if ( in length>length−d length )in length =length−d length ;

copy to user ( u buf +d length ,216 th is rec . data+ i n o f f ,

in length ) ;d length +=in length ;o f f se t +=in length ;

220 cur pos += i n o f f +in length ;if ( th is rec . item type ==UP RD)

th is rec . update ( c rec , 0 ) ;if ( d length ==length ) goto end data copy ;

224

2-73

th is rec =get next ( c rec , 1 ) ;

228 c rec ++;

return d length ;

end data copy :232 return d length ;

static s i ze t u s e r t o f i l e ( o f f t o f f se t , s i ze t length , const char∗ u buf )236

o f f t cur pos ;s i ze t d length ;int c rec , i n o f f , in length ;

240 rec th is rec ;

if ( o f f se t>a c t i v e f i l e l e n g t h −1) return 0 ; / / EOF conditionif ( o f f se t +length>a c t i v e f i l e l e n g t h )

244 length = a c t i v e f i l e l e n g t h−o f f se t ;

c rec =0;cur pos =0;

248 d length =0;while ( c rec<ADC NUM CHANNELS)

if ( cur pos +rec anchors [ c rec ] . rec s i ze<=o f f se t )252 cur pos +=rec anchors [ c rec ] . rec s i ze ;

else

th is rec =get next ( c rec , 0 ) ;256 while ( th is rec . data !=NULL)

if ( th is rec . length +cur pos<o f f se t )

cur pos +=this rec . length ;260 else

i n o f f =o f f se t−cur pos ;in length =this rec . length−i n o f f ;

264 if ( in length>length−d length )in length =length−d length ;

copy from user ( th is rec . data+ i n o f f ,u buf +d length ,

268 in length ) ;d length +=in length ;o f f se t +=in length ;cur pos += i n o f f +in length ;

272 if ( th is rec . item type ==UP WR)

2-74

th is rec . update ( c rec , 0 ) ;if ( d length ==length ) goto end data copy ;

276 th is rec =get next ( c rec , 1 ) ;

c rec ++;280

return d length ;end data copy :return d length ;

284

static int up cnt ( int channel , int value )

288 if ( channel <0 | | channel>ADC NUM CHANNELS−1)return 0 ;

switch ( value )292

case 0 :modify parameter ( channel ,UP COUNT, 0 ) ;break ;

296 case 1 :modify parameter ( channel ,

UP COUNT,1+read parameter ( channel ,UP COUNT) ) ;

300 break ;default :

return 1 ;304

/∗——dt2811adc.c——∗/#include ” adcsupp . h”#include ” prototypes . h”

4

extern struct f i l e opera t i ons d t d r i v e r i n t e r f a c e f i l e [ 3 ] ;extern struct inode operations dtdr iver inter face inode [ 3 ] ;

8 extern struct proc dir entry d t f i l e s [ 3 ] ;static long a c t i v e f i l e l e n g t h ;

extern struct12

int base addr ;int irq ;

2-75

card conf ig ;16

int in i t adc ( void )

20 if ( ! b u i l d f i l e s t r u c t ( ) )return 0 ;

reset sampler ( ) ;

24 d t f i l e s [ADC IFACE] . size = a c t i v e f i l e l e n g t h ;if (0> request irq ( card conf ig . irq , irq handler ,0 , ” dt2811 adc ” ,NULL) )

d e s t r o y f i l e s t r u c t ( ) ;28 return 0 ;

return 1 ;

32

int adc f i l e a t tach ( struct inode ∗ inode , struct f i l e ∗ f i l e )

inc use count ( ) ;36

return 0 ;

40 int adc f i l e detach ( struct inode ∗ inode , struct f i l e ∗ f i l e )

dec use count ( ) ;

44 return 0 ;

static ss i ze t adc te l l back ( struct f i l e ∗ f i l e ,48 char∗ buffer ,

s i ze t length ,l o f f t ∗ l o f f s e t )

52 s i ze t rep length ;

rep length = f i l e t o u s e r ( ( o f f t )∗ l o f f s e t , length , buffer ) ;∗ l o f f s e t +=rep length ;

56 return rep length ;

static ss i ze t adc get conf ig ( struct f i l e ∗ f i l e ,60 const char∗ buffer ,

s i ze t length ,l o f f t ∗ l o f f s e t )

2-76

64 s i ze t rep length ;

rep length = u s e r t o f i l e ( ( o f f t )∗ l o f f s e t , length , buffer ) ;∗ l o f f s e t +=rep length ;

68 return rep length ;

2.3.5 Client software - the controller

Our main effort was to develop a system with an interpreted controller, the one that will make pos-sible the use of high level language directly for controller definition. We invested a large amount ofeffort into the development of high speed data interface between Matlab and outer world, but thechanges in the Matlab defied our attempts. The problem was that the lab policy required the mi-gration from Matlab Release 10 (v5.2) to Matlab Release 11 (v5.3) system, and the later was foundto be equipped with extremely unstable Mex interface on Linux platform. This was caused by thefact that Matlab R11 is linked with patched version of GNU libc v5, when the kernel and systemlibraries on the our system were GNU glibc v2.1 compatible. The curious fact is that Matlab R10works fine because it uses a standard libc v5 libraries, which are more or less compatible to the ourenvironment. The centralized license server at our installation prevented us from keeping MatlabR10 as working system.

Still we wanted to dive a reference client implementation so we wrote a program in C thatillustrates the usage of our driver and provides some reasonable control to the pendulum. Weadapted some Fuzzy control surface to become a fixed control surface and reached a satisfactoryresults. Of course, our system had a horrible tracking capabilities and was rather marginallystable (it could bring the arm to the unstable equilibrium point, but was unable to cope with smallarm displacements, so it took some extra minute to return the arm back to the desired position).

The controller was defined as a state machine that modifies the control surface using someadaptation rule. Figure 2.2 illustrates the controller implemented in the trial file runloop.c. Thenomenclature goes as following: Errb, Erra - base and arm normalized errors, Pm - normalizedmotor power.

Figure 2.2: The trial implementation of controller

start // ?>=<89:;1Pm=−0.8Errb

44|Errb|<0.5 // ?>=<89:;2

EDGF|Errb|>0.6

Pm=−0.5Errb

tt

|Errb|<0.2

%%KKKKKKKKKKKKKK

?>=<89:;3|Errb|>0.3

SS

Pm=

−0.8sgn(Erra)Erra2 |Erra| > 0.3

−0.4sgn(Erra)Erra2 otherwise

KK

Here we have a controller implemented in C:

2-77

/∗——runloop.c——∗/#include < stdio . h>#include < f cnt l . h>

4 #include <unistd . h>#include < signal . h>/ / # include <math. h>

8 #define REC SIZE A (5∗ sizeof ( unsigned long )+128∗ sizeof ( u int16 t ) )#define REC SIZE D ( sizeof ( unsigned long )+ sizeof ( u int16 t ) )#define CH P 3#define CH B 4

12 #define BASE 0#define PEND 1int finp , fout ;int l imits [2][3]=390,2046,3730 ,1100,1950,2950 ;

16

void s igna l ex i t ( int sig )

unsigned long ts =0;20 u int16 t ddat =2047;

print f ( ” Signal %d caught !\ n” , sig ) ;lseek ( finp , CH P∗REC SIZE A+sizeof ( unsigned long ) , SEEK SET) ;

24 write ( finp ,& ts , sizeof ( unsigned long ) ) ;lseek ( finp , CH B∗REC SIZE A+sizeof ( unsigned long ) , SEEK SET) ;write ( finp ,& ts , sizeof ( unsigned long ) ) ;lseek ( fout , REC SIZE D, SEEK SET) ;

28 write ( fout ,&ddat , sizeof ( u int16 t ) ) ;ex i t (−1);

32

inl ine void go right ( float p)

u int16 t ddat ;36

if ( p<0) p=0;if ( p>1) p=1;

40 ddat =( u int16 t )(2047+2047∗p ) ;write ( fout ,&ddat , sizeof ( u int16 t ) ) ;

44 inl ine void g o l e f t ( float p)

u int16 t ddat ;

48 if ( p<0) p=0;

2-78

if ( p>1) p=1;

ddat =( u int16 t )(2047∗(1−p ) ) ;52 write ( fout ,&ddat , sizeof ( u int16 t ) ) ;

void go on ( float p)56

if ( p>=0)go right ( p ) ;

else if ( p<0)60 g o l e f t (−p ) ;

float ev err ( u int16 t value , int inp )64

float p;

if ( inp <0 | | inp>1) return 0 ;68

if ( value>l imits [ inp ] [ 1 ] )

p=(1.0∗ ( l imits [ inp ][2]− value ) ) / ( l imits [ inp ][2]− l imits [ inp ] [ 1 ] ) ;72 p=1−p;

if ( p<0) p=1;if ( p>1) p=1;

76 else if ( value<l imits [ inp ] [ 1 ] )

p=(1.0∗ ( value−l imits [ inp ] [ 0 ] ) ) / ( l imits [ inp ][1]− l imits [ inp ] [ 0 ] ) ;if ( p<0) p=1;

80 if ( p>1) p=1;p=p−1;

else p=0;

84 return p;

float sign ( float inp )88

float tmp;

tmp=( inp<0)?−1.0:1.0;92 return tmp;

int main( void )96

2-79

unsigned long gain =8;unsigned long ts =2000;u int16 t db p [20 ] , db b [20 ] , dout ;

100 int k, l , data , state , l as t s ta te ;float mean p , l mean p =0, d i f f p ;float err , errp ;

104 printf ( ” Starting loop !\ n” ) ;signal (SIGHUP, s igna l ex i t ) ;signal ( SIGINT, s igna l ex i t ) ;signal ( SIGQUIT, s igna l ex i t ) ;

108

finp =open ( ” / proc / dt2811 . adc ” ,O RDWR|O NDELAY) ;fout =open ( ” / proc / dt2811 . dac ” ,O RDWR|O NDELAY) ;

112 if ( finp <0 | | fout <0) ex i t (−1);

signal (SIGHUP, s igna l ex i t ) ;signal ( SIGINT, s igna l ex i t ) ;

116 signal ( SIGQUIT, s igna l ex i t ) ;

lseek ( finp , CH P∗REC SIZE A, SEEK SET) ;write ( finp ,&gain , sizeof ( unsigned long ) ) ;

120 write ( finp ,& ts , sizeof ( unsigned long ) ) ;lseek ( finp , CH B∗REC SIZE A, SEEK SET) ;write ( finp ,&gain , sizeof ( unsigned long ) ) ;write ( finp ,& ts , sizeof ( unsigned long ) ) ;

124 lseek ( fout , REC SIZE D, SEEK SET) ;

l =0;print f ( ” Gain set to %d\n” , gain ) ;

128 state =1; las t s ta te =1;while (1)

l ++;132 lseek ( finp , CH P∗REC SIZE A+5∗sizeof ( unsigned long ) , SEEK SET) ;

read ( finp , db p ,5∗ sizeof ( u int16 t ) ) ;lseek ( finp , CH B∗REC SIZE A+5∗sizeof ( unsigned long ) , SEEK SET) ;read ( finp , db b ,5∗ sizeof ( u int16 t ) ) ;

136 err =ev err ( db b [ 0 ] , BASE) ;switch ( state )case 1 :

140 if ( err<0.5 && err>−0.5 && state ==las t s ta te )state =2;

else

144 go on (−0.8∗ sign ( err ) ) ;

2-80

l a s t s ta te =state ;

break ;148 case 2 :

if ( ( err >0 . 6 | | err<−0.6) && state ==las t s ta te )state =1;

else if ( err<0.2 && err>−0.2 && state ==las t s ta te )152 state =3;

else

go on (−0.5∗ err ) ;156 l a s t s ta te =state ;

break ;

case 3 :160 if ( ( err >0 . 3 | | err<−0.3) && state ==las t s ta te )

state =2;else

164 errp =ev err ( db p [ 0 ] , PEND) ;if ( errp >0 . 3 | | errp<−0.3)

go on (−0.8∗ sign ( errp )∗ errp ∗errp ) ;else

168 /∗ mean p=0; ∗/

/∗ for ( l =0; l<5; l ++) ∗//∗ mean p+=( f loa t ) db p [ l ] ; ∗/

172 /∗ mean p=mean p/5; ∗//∗ mean p=ev err ( ( u int16 t ) mean p ,PEND) ; ∗//∗ di f f p =mean p−l mean p ; ∗/

go on (−0.4∗ sign ( errp )∗ errp ∗errp ) ;176

l mean p=mean p ;

180 l a s t s ta te =state ;

usleep ( 0 ) ;184

return 1 ;188

2-81

2.4 Conclusions

Our main goal was not accomplished. We undertook a big job, but were only able to do it partially.However, if we could have a second try on this plant, we surely would be able to overcome most ofthe control (and some software) problems. The experience is needed to deal with such a systems,and it can only achieved by the trial and error. Dijkstra said once: “Much of the excitement we getof our work is that we don’t really know what we are doing.” This was our case and only now weare capable of seeing errors, made in design. We hope that the obtained experience will guide usin our future work.

We would also want to thank to the control laboratory engineering staff, Koby Kohai (the labengineer) and Orly Wigderson (the lab technician) for their support and apprehension of our needs.

2-82