]> rtime.felk.cvut.cz Git - mf6xx.git/blob - src/hudaqlib/MF614.c
QEMU mf624.c formatted to make QEMU checkpatch.pl mostly happy.
[mf6xx.git] / src / hudaqlib / MF614.c
1 /****************************************************************/\r
2 /**@file MF614.c:\r
3  * Description: API layer for MF614 card.                       *\r
4  * Dependency: Windows 32, Windows 64 or Linux                  *\r
5  *                Copyright 2006-2007 Humusoft s.r.o.           *\r
6  ****************************************************************/\r
7 \r
8 #if defined(_WIN32) || defined(_WIN64)\r
9 #include <windows.h>\r
10 #endif\r
11 #include <malloc.h>\r
12 #include <math.h>\r
13 \r
14 #include "hudaqlib.h"\r
15 #include "hudaq_internal.h"\r
16 \r
17 \r
18 #define MASTERFREQUENCY 20000000   ///< Master frequency of MF614 is 20MHz\r
19 \r
20 \r
21 /** Counter control register */\r
22 typedef struct\r
23 {\r
24         unsigned int OutputControl:3; ///< 000-Inactive, Output Low; 001-Active, high on TC; 010-TC toggled; 011-illegal\r
25         ///< 100-Inactive, Output High Z; 101-Active, low on TC; 110-illegal; 111-illegal\r
26         unsigned int Direction:1;     ///< 0-Down; 1-Up\r
27         unsigned int CountMode:1;     ///< 0-Binary; 1-BCD\r
28         unsigned int RepeatMode:1;    ///< 0-once; 1-repeat\r
29         unsigned int ReloadMode:1;    ///< 0-load; 1-both\r
30         unsigned int GateMode:1;      ///< 0-off; 1-on\r
31         unsigned int CountSource:4;   ///< 0000-TC(n-1)/F6; 0001-Source1/F7; 0010-Source2/FOUT1; 0011-Source3/FOUT2\r
32         ///< 0100-Source4/??; 0101-Source5/??; 0110-Gate1/??; 0111-Gate2/??\r
33         ///< 1000-Gate3/??; 1001-Gate4/??; 1010-Gate5/??; 1011-F1/??\r
34         ///< 1100-F2/??; 1101-F3/??; 1110-F4/??; 1111-F5/??\r
35         unsigned int EdgeMode:1;      ///< 0-disabled; 1-enabled\r
36         unsigned int GateControl:3;   ///< 00-1; 01-IN; 10-Out_n-1; 11-Out_n+1\r
37         unsigned int CountSourceEx:1; ///< extends one bit for Count source\r
38         unsigned int InterruptMode:1; ///< 0-pulse; 1-latch\r
39         unsigned int InterruptPolarity:1; ///< 0-low; 1-high\r
40 } Counter614Control;\r
41 \r
42 /** Configuration structure for one analog input. */\r
43 typedef struct\r
44 {\r
45         unsigned int Range:1;         ///< 0-5V; 1-10V\r
46         unsigned int Bipolar:1;       ///< 0 - <0;range>; 1 - <-range;+range>\r
47         unsigned int Raw:1;           ///< 0 - volts; 1 - raw\r
48 } ADSetup;\r
49 \r
50 \r
51 /** Configuration structure for one analog output. */\r
52 typedef struct\r
53 {\r
54         unsigned __int8 LoCache;\r
55         unsigned __int8 HiCache;\r
56 \r
57         unsigned int Raw:1;           ///< 0-volts; 1-raw\r
58 } DASetup;\r
59 \r
60 \r
61 /** Configuration structure for one encoder. */\r
62 typedef struct\r
63 {\r
64         unsigned __int8 Filter;\r
65 \r
66         unsigned int ResetOnRead:1;   ///< 0-no reset; 1-reset\r
67         unsigned int Polarity:1;      ///< 0-negative polarity; 1-positive polarity\r
68         unsigned int Index:1;         ///< 0-disable index; 1-enable index\r
69         unsigned int Level:1;         ///< 0-edge; 1-level\r
70         unsigned int Quadrature:2;    ///< NonQuadrature / Quadrature mode\r
71 } EncSetup;\r
72 \r
73 \r
74 /** This enum contains internal state info. */\r
75 typedef enum\r
76 {\r
77         Unspecified = 0,\r
78         PWM,                          ///< PWM is output from counter\r
79         OUT_0,                        ///< counter is generating 0 permanently\r
80         OUT_1,                        ///< counter is generating 1 permanently\r
81         Counting,                     ///< counting on external signal\r
82         StepperPWM,                   ///< Part of stepper motor that generates output\r
83         StepperMaster,                ///< Part of stepper motors that counts pulses\r
84         custom                        ///< counter contains externally defined value\r
85 } Internal614CtrMode;\r
86 \r
87 \r
88 /** One record related to counter. */\r
89 typedef struct\r
90 {\r
91         __int16 ACache;               ///< Counter A register cache; Load register\r
92         __int16 BCache;               ///< Counter B register cache; Hold register\r
93         Internal614CtrMode Mode;      ///< Flag of subsystem that uses counter\r
94         Counter614Control CTC;        ///< Counter mode regiser\r
95         unsigned int ResetOnRead:1;   ///< 0-no reset; 1-reset\r
96 } Counter614;\r
97 \r
98 \r
99 /** One record related to stepper motor. */\r
100 typedef struct\r
101 {\r
102         double Fmin;                  ///< Minimal frequency of stepper motor\r
103         double Fmax;                  ///< Maximal frequency of stepper motor\r
104         signed char Direction;        ///< Direction of stepping motor\r
105         __int32 LastPosition;         ///< Stepper motor target position\r
106         __int32 ChainedPosition;      ///< Stepper motor position\r
107         DWORD TimeStamp;              ///< Last refresh time mark call of HudaqStepout\r
108         double Frequency;             ///< Current speed (frequency) of stepper motor\r
109         double Acc;                   ///< Acceleration of stepping motor in steps/s^2\r
110 } Stepper614;\r
111 \r
112 \r
113 /** Ranges for both Analog Inputs and Analog Outputs - only one item for MF624. */\r
114 static const HudaqRange MF614RangeAIO[4] =\r
115 {{-10,+10},{-5,+5},{0,+10},{0,+5}};\r
116 \r
117 #define MAXIMAL_STEP_INCREMENT 500\r
118 \r
119 #define DA_CHANNELS   4         ///< Amount of Analog Outputs\r
120 #define AD_CHANNELS   8         ///< Amount of Analog Inputs\r
121 #define ENC_CHANNELS  4         ///< Amount of Encoders\r
122 #define CTR_CHANNELS  5         ///< Amount of counters\r
123 #define STEP_CHANNELS 2         ///< Amount of steppers\r
124 \r
125 \r
126 \r
127 /** Cache of MF614 state */\r
128 typedef struct\r
129 {\r
130         UserDataHeader Hdr;                   ///< General device setup\r
131 \r
132         /* Digital inputs/outputs */\r
133         __int8 DoutCache;                     ///< digital outputs to be cached\r
134 \r
135         /* Analog inputs/outputs */\r
136 \r
137         ADSetup AIOptions[AD_CHANNELS];       ///< Options for analog inputs\r
138 \r
139         DASetup DAOptions[DA_CHANNELS];       ///< DA values to be cached\r
140 \r
141         /* Counters and encoders */\r
142         EncSetup EncOptions[ENC_CHANNELS];    ///< Encoder (IRC counter) state register\r
143 \r
144         Counter614 counter[CTR_CHANNELS];     ///< Structure that holds state of one counter\r
145 \r
146         Stepper614 step[STEP_CHANNELS];       ///< Stepper motor table\r
147 } MF614_Private;\r
148 \r
149 \r
150 #define LOOP_AD_TIMEOUT 0xFFFF  ///< timeout per loop for endless AD conversion\r
151 \r
152 \r
153 /** Symbolic aliases for available registers inside BADR0 read. */\r
154 typedef enum\r
155 {\r
156         AIN        = 0,\r
157         TDPIN      = 2,\r
158         DIN        = 0x06,\r
159         DALATCHEN  = 0x08,\r
160         IRC0DATARD = 0x10,\r
161         IRC0CMDRD  = 0x11\r
162 } BADR0IN;\r
163 \r
164 typedef enum\r
165 {\r
166         DATAREAD = 16\r
167 } BADR2IN;\r
168 \r
169 /** Symbolic aliases for available registers inside BADR1 write. */\r
170 typedef enum\r
171 {\r
172         ADCTRL     = 0,\r
173         TDP        = 2,\r
174         TCP        = 3,\r
175         DOUT       = 0x06,\r
176         DA0LO      = 0x08,\r
177         DA0HI      = 0x09,\r
178         IRC0DATAWR = 0x10,\r
179         IRC0CMDWR  = 0x11\r
180 } BADR0OUT;\r
181 \r
182 \r
183 \r
184 /** MF614 specific conversion values from A/D convertor to a voltage. */\r
185 static __inline double MF614_AD2VOLT(signed __int16 x, char Gain)\r
186 {\r
187         if(Gain & 8)          // bipolar range\r
188         {\r
189                 if(Gain & 16)       // <-10V;+10V>\r
190                         return 10.0*((signed __int16)(x<<4))/(double)0x8000;\r
191                 else                // <-5V;+5V>\r
192                         return 5.0*((signed __int16)(x<<4))/(double)0x8000;\r
193         }\r
194         else                  // unipolar range\r
195         {\r
196                 if(Gain & 16)       // <0V;+10V>\r
197                         return 10.0*((signed __int16)(x & 0xFFF))/(double)0x1000;\r
198                 else                // <0V;+5V>\r
199                         return 5.0*((signed __int16)(x & 0xFFF))/(double)0x1000;\r
200         }\r
201 }\r
202 \r
203 \r
204 /** Convert voltage to a raw values for D/A convertor. */\r
205 static __inline __int16 MF614_VOLT2DA(double y)\r
206 {\r
207         y = 0x1000*(y+10)/20;\r
208         if (y>=0x0FFF) return(0x0FFF);\r
209         if (y<=0) return(0x0000);\r
210         return( (__int16)y );\r
211 }\r
212 \r
213 \r
214 \r
215 /** write to memory mapped device byte wise, bytes are stored on every 4th position. */\r
216 static __inline void StoreByte(size_t Ptr, int Offset, unsigned __int8 value)\r
217 {\r
218         ((volatile unsigned __int8 *)(Ptr))[4*Offset] = value;\r
219 }\r
220 \r
221 /** write to memory mapped device byte wise through cache. */\r
222 static __inline void StoreCachedByte(size_t Ptr, int Offset, unsigned __int8 value, unsigned __int8 *CachedValue)\r
223 {\r
224         if (*CachedValue != value)\r
225         {\r
226                 *CachedValue = value;\r
227                 StoreByte(Ptr,Offset,value);\r
228         }\r
229 }\r
230 \r
231 /** Read from memory mapped device byte wise, bytes are stored on every 4th position.\r
232   This is specific to OX9162 board registers. */\r
233 static __inline unsigned __int8 GetByte(size_t Ptr, int Offset)\r
234 {\r
235         return ((volatile unsigned __int8 *)(Ptr))[4*Offset];\r
236 }\r
237 \r
238 \r
239 /** Packed word from two 8bit reads. */\r
240 static __inline unsigned __int16 GetPackedWord(size_t Ptr, int Offset)\r
241 {\r
242         __int16 RetVal;\r
243         RetVal = GetByte(Ptr,Offset);\r
244         RetVal += 256*GetByte(Ptr,Offset);\r
245         return RetVal;\r
246 }\r
247 \r
248 \r
249 /** This inline is used for reading tightly packed OX9162 local configuration registers. */\r
250 static __inline unsigned __int8 GetBytePlain(size_t Ptr, int Offset)\r
251 {\r
252         return ((volatile unsigned __int8 *)(Ptr))[Offset];\r
253 }\r
254 \r
255 \r
256 /****************************************************************\r
257  *                                                              *\r
258  *                 DIGITAL INPUTS & OUTPUTS                     *\r
259  *                                                              *\r
260  ****************************************************************/\r
261 \r
262 \r
263 /** Get data from digital input. */\r
264 static int MF614DIRead(const DeviceRecord *DevRecord, unsigned channel)\r
265 {\r
266         if (channel!=0) return 0;\r
267         return GetByte(DevRecord->DrvRes.Resources.MemResources[1].Base,DIN);\r
268 }\r
269 \r
270 \r
271 /** Write data to digital output. */\r
272 static HUDAQSTATUS MF614DOWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned value)\r
273 {\r
274         if (channel!=0) return HUDAQBADARG;\r
275         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, value,\r
276                         & ((MF614_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );\r
277         return HUDAQSUCCESS;\r
278 }\r
279 \r
280 \r
281 /** Write one bit to digital output. */\r
282 static void MF614DOWriteBit(const DeviceRecord *DevRecord, unsigned channel, unsigned bit, int value)\r
283 {\r
284         __int8 DoutByte;\r
285 \r
286         if (channel!=0) return;\r
287         if (bit>=8) return;   //there are only 8 bits available in MF624\r
288 \r
289         DoutByte = ((MF614_Private *)(DevRecord->DrvRes.DriverData))->DoutCache;\r
290         if (value) DoutByte |= (1<<bit);\r
291         else DoutByte &= ~(1<<bit);\r
292 \r
293         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, DoutByte,\r
294                         & ((MF614_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );\r
295 }\r
296 \r
297 \r
298 static void MF614DOWriteMultipleBits(const DeviceRecord *DevRecord, unsigned channel, unsigned mask, unsigned value)\r
299 {\r
300         __int8 DoutByte;\r
301 \r
302         if (channel!=0) return;\r
303 \r
304         DoutByte = ((MF614_Private *)(DevRecord->DrvRes.DriverData))->DoutCache;\r
305         DoutByte = (DoutByte & ~mask) | (value & mask);\r
306 \r
307         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, DoutByte,\r
308                         &((MF614_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );\r
309 }\r
310 \r
311 \r
312 static double MF614DIOGetParameter(unsigned channel, HudaqParameter param)\r
313 {\r
314         switch(param)\r
315         {\r
316                 case HudaqDINUMBITS:\r
317                         return (channel==0) ? 8 : WRONG_VALUE;\r
318                 case HudaqDONUMBITS:\r
319                         return (channel==0) ? 8 : WRONG_VALUE;\r
320                 case HudaqDINUMCHANNELS:\r
321                         return 1;\r
322                 case HudaqDONUMCHANNELS:\r
323                         return 1;\r
324         }\r
325 \r
326         return WRONG_VALUE;\r
327 }\r
328 \r
329 \r
330 /****************************************************************\r
331  *                                                              *\r
332  *                  ANALOG INPUTS & OUTPUTS                     *\r
333  *                                                              *\r
334  ****************************************************************/\r
335 \r
336 static HUDAQSTATUS MF614AISetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
337 {\r
338         MF614_Private *Cache;\r
339 \r
340         if (channel>=AD_CHANNELS) return HUDAQBADARG;\r
341         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
342 \r
343         switch(param)\r
344         {\r
345                 case HudaqAIUNITS:\r
346                         if (value<0 || value>=2) return HUDAQBADARG;\r
347                         Cache->AIOptions[channel].Raw = (int)value;\r
348                         return HUDAQSUCCESS;\r
349 \r
350                 case HudaqAIRange:\r
351                         switch((int)value)\r
352                         {\r
353                                 case 0: Cache->AIOptions[channel].Bipolar = 1;\r
354                                         Cache->AIOptions[channel].Range = 1;\r
355                                         break;\r
356                                 case 1: Cache->AIOptions[channel].Bipolar = 1;\r
357                                         Cache->AIOptions[channel].Range = 0;\r
358                                         break;\r
359                                 case 2: Cache->AIOptions[channel].Bipolar = 0;\r
360                                         Cache->AIOptions[channel].Range = 1;\r
361                                         break;\r
362                                 case 3: Cache->AIOptions[channel].Bipolar = 0;\r
363                                         Cache->AIOptions[channel].Range = 0;\r
364                                         break;\r
365                                 default:return HUDAQBADARG;\r
366                         }\r
367                         return HUDAQSUCCESS;\r
368         }\r
369 \r
370         return HUDAQNOTSUPPORTED;\r
371 }\r
372 \r
373 \r
374 static double MF614AIGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
375 {\r
376         MF614_Private *Cache;\r
377 \r
378         if (channel>=AD_CHANNELS) return WRONG_VALUE;\r
379         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
380 \r
381         switch(param)\r
382         {\r
383                 case HudaqAIUNITS:\r
384                         return(Cache->AIOptions[channel].Raw);\r
385 \r
386                 case HudaqAIRange:                                     // see ::MF614RangeAIO for range indices\r
387                         if(Cache->AIOptions[channel].Bipolar==1)            // bipolar ranges\r
388                         {\r
389                                 if(Cache->AIOptions[channel].Range==1) return 0;  // <-10V;+10V>\r
390                                 return 1;                                         // <-5V;+5V>\r
391                         }\r
392                         else                                                // unipolar ranges\r
393                         {\r
394                                 if(Cache->AIOptions[channel].Range==1) return 2;  // <0V;+10V>\r
395                                 return 3;                                         // <0V;+5V>\r
396                         }\r
397 \r
398                 case HudaqAINUMCHANNELS:\r
399                         return AD_CHANNELS;\r
400         }\r
401 \r
402         return WRONG_VALUE;\r
403 }\r
404 \r
405 \r
406 /** Get data from analog input MF614 specific. */\r
407 static double MF614AIRead(const DeviceRecord *DevRecord, unsigned channel)\r
408 {\r
409         size_t Ptr0,Ptr2;\r
410         short ad;\r
411         unsigned TimeoutCounter;\r
412         char Gain;\r
413         ADSetup *ChanAiCache;\r
414 \r
415         if (channel>=AD_CHANNELS) return UNDEFINED_VALUE;\r
416         ChanAiCache = &((MF614_Private *)DevRecord->DrvRes.DriverData)->AIOptions[channel];\r
417 \r
418         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
419         Ptr2 = DevRecord->DrvRes.Resources.MemResources[0].Base;\r
420 \r
421         Gain = channel | 0x40;\r
422         if(ChanAiCache->Range) Gain |= 16;\r
423         if(ChanAiCache->Bipolar) Gain |= 8;\r
424         StoreByte(Ptr0, ADCTRL, Gain);  // select channel and start conversion\r
425 \r
426         TimeoutCounter=0;\r
427 \r
428         while ( (GetBytePlain(Ptr2,DATAREAD) & 0x04) != 0 )   // wait for conversion complete\r
429         {\r
430                 if (TimeoutCounter++>LOOP_AD_TIMEOUT)\r
431                         return UNDEFINED_VALUE;           /*timeout*/\r
432         }\r
433 \r
434         ad = GetByte(Ptr0,AIN) + 256*GetByte(Ptr0,AIN+1);   // read the value\r
435 \r
436         if(ChanAiCache->Raw)\r
437                 return ad;\r
438         else\r
439                 return(MF614_AD2VOLT(ad,Gain));\r
440 }\r
441 \r
442 \r
443 static double MF614AOGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
444 {\r
445         MF614_Private *Cache;\r
446 \r
447         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
448         if (channel>=DA_CHANNELS) return WRONG_VALUE;\r
449 \r
450         switch(param)\r
451         {\r
452                 case HudaqAOUNITS:\r
453                         return(Cache->DAOptions[channel].Raw);\r
454                 case HudaqAORange:\r
455                         return(0);                   // only bipolar range -10:10V is supported\r
456                 case HudaqAONUMCHANNELS:\r
457                         return DA_CHANNELS;\r
458         }\r
459 \r
460         return WRONG_VALUE;\r
461 }\r
462 \r
463 \r
464 /** Setup for MF614 analog outputs. */\r
465 static HUDAQSTATUS MF614AOSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
466 {\r
467         MF614_Private *Cache;\r
468 \r
469         if (channel>=DA_CHANNELS) return HUDAQBADARG;\r
470         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
471 \r
472         switch(param)\r
473         {\r
474                 case HudaqAOUNITS:\r
475                         if (value<0 || value>=2) return HUDAQBADARG;\r
476                         Cache->DAOptions[channel].Raw = (int)value;\r
477                         return HUDAQSUCCESS;\r
478 \r
479                 case HudaqAORange:\r
480                         if((int)value == 0) return HUDAQSUCCESS;   // only one range "0" -10:10V is supported\r
481                         return HUDAQBADARG;\r
482         }\r
483         return HUDAQNOTSUPPORTED;\r
484 }\r
485 \r
486 \r
487 /** Write data to analog output. */\r
488 static void MF614AOWrite(const DeviceRecord *DevRecord, unsigned channel, double value)\r
489 {\r
490         size_t Ptr0;\r
491         unsigned __int16 RawValue;\r
492         MF614_Private *Cache;\r
493 \r
494 \r
495         if (channel>=DA_CHANNELS) return;\r
496         Cache = DevRecord->DrvRes.DriverData;\r
497 \r
498         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
499 \r
500         if(Cache->DAOptions[channel].Raw)\r
501                 RawValue = (int)value;\r
502         else\r
503                 RawValue = MF614_VOLT2DA(value);\r
504 \r
505         StoreCachedByte(Ptr0, DA0LO+2*channel, RawValue & 0xFF, &Cache->DAOptions[channel].LoCache);\r
506         StoreCachedByte(Ptr0, DA0HI+2*channel, (RawValue>>8) & 0x0F, &Cache->DAOptions[channel].HiCache);\r
507 \r
508         RawValue=GetByte(Ptr0, DALATCHEN);               // update the output\r
509 }\r
510 \r
511 \r
512 /** Write multiple data to analog output synchronized. */\r
513 static HUDAQSTATUS MF614AOWriteMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned* channels, const double* values)\r
514 {\r
515         size_t Ptr0;\r
516         unsigned __int16 RawValue;\r
517         MF614_Private *Cache;\r
518         int RetVal = HUDAQSUCCESS;\r
519 \r
520 \r
521         if (channels==NULL || values==NULL || number<=0) return HUDAQBADARG;\r
522         Cache = DevRecord->DrvRes.DriverData;\r
523 \r
524         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
525 \r
526         while(number-->0)\r
527         {\r
528                 if (*channels>=DA_CHANNELS)\r
529                         RetVal = HUDAQPARTIAL;\r
530                 else\r
531                 {\r
532                         if(Cache->DAOptions[*channels].Raw)\r
533                                 RawValue = (unsigned __int16)*values;\r
534                         else\r
535                                 RawValue = MF614_VOLT2DA(*values);\r
536 \r
537                         StoreCachedByte(Ptr0, DA0LO + 2 * *channels, RawValue & 0xFF, &Cache->DAOptions[*channels].LoCache);\r
538                         StoreCachedByte(Ptr0, DA0HI + 2 * *channels, (RawValue>>8) & 0x0F, &Cache->DAOptions[*channels].HiCache);\r
539                 }\r
540                 values++;\r
541                 channels++;\r
542         }\r
543         RawValue = GetByte(Ptr0, DALATCHEN);               // update the output\r
544         return RetVal;\r
545 }\r
546 \r
547 \r
548 /****************************************************************\r
549  *                                                              *\r
550  *                          ENCODERS                            *\r
551  *                                                              *\r
552  ****************************************************************/\r
553 \r
554 \r
555 static double MF614EncGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
556 {\r
557         MF614_Private *Cache;\r
558         EncSetup *xEncOptions;\r
559 \r
560         if (channel>=ENC_CHANNELS) return WRONG_VALUE;\r
561         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
562         xEncOptions = &Cache->EncOptions[channel];\r
563 \r
564         switch(param)\r
565         {\r
566                 case HudaqEncFILTER:\r
567                         return((xEncOptions->Filter>0) ? 1 : 0);\r
568 \r
569                 case HudaqEncRESETONREAD:\r
570                         return(xEncOptions->ResetOnRead);\r
571 \r
572                 case HudaqEncRESETMODE:\r
573                         if(xEncOptions->Index == 0)\r
574                                 return(HudaqEncRESNONE);\r
575                         if(xEncOptions->Level == 1)\r
576                                 return(HudaqEncRESI0);\r
577 \r
578                         if(xEncOptions->Polarity == 1)\r
579                                 return(HudaqEncRESIRISING);\r
580                         else\r
581                                 return(HudaqEncRESIFALLING);\r
582 \r
583                 case HudaqEncCOUNTCONTROL:\r
584                         if(xEncOptions->Index==1) return(HudaqEncCOUNTENABLE);\r
585                         return(HudaqEncCOUNTI1);\r
586 \r
587                 case HudaqEncI:\r
588                         return(GetByte(DevRecord->DrvRes.Resources.MemResources[1].Base, IRC0CMDRD)>>7);\r
589 \r
590                 case HudaqEncNUMCHANNELS:\r
591                         return ENC_CHANNELS;\r
592         }\r
593 \r
594         return WRONG_VALUE;\r
595 }\r
596 \r
597 \r
598 static HUDAQSTATUS MF614EncSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
599 {\r
600         size_t Ptr0;\r
601         MF614_Private *Cache;\r
602         __int8 Register;\r
603         EncSetup *xEncOptions;\r
604 \r
605         if (channel>=ENC_CHANNELS) return HUDAQBADARG;\r
606         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
607         xEncOptions = &Cache->EncOptions[channel];\r
608         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
609 \r
610         switch(param)\r
611         {\r
612                 case HudaqEncFILTER:\r
613                         if (value == 0)\r
614                         {\r
615                                 if(xEncOptions->Filter == 0) return HUDAQSUCCESS;\r
616                                 xEncOptions->Filter = 0;\r
617                         }\r
618                         else\r
619                         {\r
620                                 if(xEncOptions->Filter > 0) return HUDAQSUCCESS;\r
621                                 xEncOptions->Filter = 9;             //limit to 250kHz\r
622                         }\r
623                         StoreByte(Ptr0, IRC0CMDWR+2*channel, 0x01);  // reset BP\r
624                         StoreByte(Ptr0, IRC0DATAWR+2*channel, xEncOptions->Filter); // PR0; program filter to xxx\r
625                         StoreByte(Ptr0, IRC0CMDWR+2*channel, 0x18);  // PR0 -> PSC\r
626                         return HUDAQSUCCESS;\r
627 \r
628                 case HudaqEncRESETONREAD:\r
629                         xEncOptions->ResetOnRead = ((int)value==0) ? 0 : 1;\r
630                         return HUDAQSUCCESS;\r
631 \r
632                 case HudaqEncRESETMODE:\r
633                         {\r
634                                 switch((int)value)\r
635                                 {\r
636                                         case HudaqEncRESNONE:\r
637                                                 xEncOptions->Index = 0;\r
638                                                 break;\r
639                                         case HudaqEncRESPERMANENT:\r
640                                                 return HUDAQFAILURE;\r
641                                         case HudaqEncRESI0:          ///< Reset Encoder when I=0\r
642                                                 xEncOptions->Index = 1;\r
643                                                 xEncOptions->Polarity = 0;\r
644                                                 xEncOptions->Level = 1;\r
645                                                 break;\r
646                                         case HudaqEncRESIRISING:\r
647                                                 xEncOptions->Index = 1;\r
648                                                 xEncOptions->Polarity = 1;\r
649                                                 xEncOptions->Level = 0;\r
650                                                 break;\r
651                                         case HudaqEncRESIFALLING:\r
652                                                 xEncOptions->Index = 1;\r
653                                                 xEncOptions->Polarity = 0;\r
654                                                 xEncOptions->Level = 0;\r
655                                                 break;\r
656                                         case HudaqEncRESI1:          ///< Reset Encoder when I=1\r
657                                         case HudaqEncRESIEITHER:\r
658                                                 return HUDAQNOTSUPPORTED; // these modes are not supported by MF614\r
659                                         default:\r
660                                                 return HUDAQBADARG;\r
661                                 }\r
662 \r
663                                 Register = 0x60;\r
664                                 if(xEncOptions->Index)\r
665                                 {\r
666                                         StoreByte(Ptr0,IRC0CMDWR+2*channel,0x41);     // IOR 0 10 00 0 0 1\r
667                                         Register|=4;\r
668                                         if(xEncOptions->Polarity) Register|=2;\r
669                                         if(!xEncOptions->Level) Register|=1;\r
670                                 }\r
671                                 else            //no index mode, gate must be active\r
672                                 {\r
673                                         StoreByte(Ptr0,IRC0CMDWR+2*channel,0x45);     // IOR 0 10 00 1 0 1\r
674                                 }\r
675                                 StoreByte(Ptr0,IRC0CMDWR+2*channel,Register);   // IDR\r
676 \r
677                                 return HUDAQSUCCESS;\r
678                         }\r
679 \r
680                 case HudaqEncMODE:\r
681                         switch((int)value)\r
682                         {\r
683                                 case HudaqEncMODEIRC:        ///< Decode IRC connected to inputa A and B (default)\r
684                                         xEncOptions->Quadrature = 3;\r
685                                         break;\r
686                                 case HudaqEncMODERISING:     ///< Count up on A rising edge\r
687                                         xEncOptions->Quadrature = 0;\r
688                                         break;\r
689 \r
690                                 case HudaqEncMODEFALLING:\r
691                                 case HudaqEncMODEEITHER:\r
692                                         return HUDAQNOTSUPPORTED; // these modes are not supported by MF614\r
693                                 default:\r
694                                         return HUDAQBADARG;\r
695                         }\r
696                         Register = 0x20 | (xEncOptions->Quadrature<<3);  // CMR 0 01 QQ 0 0 0\r
697                         StoreByte(Ptr0,IRC0CMDWR+2*channel,Register);    // CMR\r
698                         return HUDAQSUCCESS;\r
699 \r
700                 case HudaqEncCOUNTCONTROL:\r
701                         if(HudaqEncCOUNTENABLE || HudaqEncCOUNTI1) return HUDAQSUCCESS;\r
702                         return HUDAQFAILURE;\r
703 \r
704         }\r
705 \r
706         return HUDAQNOTSUPPORTED;\r
707 }\r
708 \r
709 \r
710 static HUDAQSTATUS MF614EncReset(const DeviceRecord *DevRecord, unsigned channel)\r
711 {\r
712         if (channel>=ENC_CHANNELS) return HUDAQBADARG;\r
713 \r
714         StoreByte(DevRecord->DrvRes.Resources.MemResources[1].Base,\r
715                         IRC0CMDWR+2*channel,0x03);    // reset BP & CNTR  0 00 00 01 1\r
716         return HUDAQSUCCESS;\r
717 }\r
718 \r
719 \r
720 static int MF614EncRead(const DeviceRecord *DevRecord, unsigned channel)\r
721 {\r
722         size_t Ptr0;\r
723         int IrcRead;\r
724         int ircaddr;\r
725 \r
726 \r
727         if (channel>=ENC_CHANNELS) return 0;\r
728 \r
729         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
730 \r
731         StoreByte(Ptr0, IRC0CMDWR + 2*channel, 0x11);    // CNTR -> OL, reset BP\r
732 \r
733         ircaddr = IRC0DATARD + 2*channel;\r
734         IrcRead = GetByte(Ptr0,ircaddr);\r
735         IrcRead += GetByte(Ptr0,ircaddr)<<8;\r
736         IrcRead += GetByte(Ptr0,ircaddr)<<16;\r
737         if (IrcRead>8388607) IrcRead -= 16777216;\r
738 \r
739         if (((MF614_Private *)DevRecord->DrvRes.DriverData)->EncOptions[channel].ResetOnRead)\r
740         {\r
741                 StoreByte(Ptr0,IRC0CMDWR+2*channel,0x03);    // reset BP & CNTR  0 00 00 01 1\r
742         }\r
743 \r
744         return(IrcRead);\r
745 }\r
746 \r
747 \r
748 /****************************************************************\r
749  *                                                              *\r
750  *                COUNTERS PWM + Count + Step                   *\r
751  *                                                              *\r
752  ****************************************************************/\r
753 \r
754 /** This PWM procedure automatically sets prescallers. Change of a prescaller\r
755   causes glitch. But a glitch is never emitted when a duty cycle is changed. */\r
756 static HUDAQSTATUS MF614PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)\r
757 {\r
758         unsigned __int32 T1,T2;\r
759         double T;\r
760         size_t Ptr0;\r
761         int Prescaler;\r
762         Counter614 *pCtrCache;\r
763         HUDAQSTATUS RetCode = HUDAQSUCCESS;\r
764 \r
765 \r
766         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
767         pCtrCache = &((MF614_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
768 \r
769         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
770 \r
771         if (dutycycle>=1)\r
772         {\r
773                 if (pCtrCache->Mode != OUT_1)\r
774                 {\r
775                         if (pCtrCache->Mode != PWM)\r
776                         {       /* Counter Mode Register must be initialized before switching output to 1. */\r
777                                 *(int *)&pCtrCache->CTC = 0xB62;               // 000 0 1011 | 0 1 1 0 0 010\r
778                                 pCtrCache->CTC.CountSource = 0x0F;             // maximal acceptable prescaller\r
779                                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
780                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
781                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
782                         }\r
783                         pCtrCache->Mode = OUT_1;\r
784                         StoreByte(Ptr0, TCP, 0xC0|(1<<channel));   // disarm\r
785                         StoreByte(Ptr0, TCP, 0xE8|(channel+1));    // set toggle out\r
786                 }\r
787                 return HUDAQSUCCESS;\r
788         }\r
789         if (dutycycle<=0)\r
790         {\r
791                 if (pCtrCache->Mode != OUT_0)\r
792                 {\r
793                         pCtrCache->Mode = OUT_0;\r
794                         StoreByte(Ptr0, TCP, 0xC0|(1<<channel));  // disarm\r
795                         StoreByte(Ptr0, TCP, 0xE0|(channel+1));   // clear toggle out\r
796                 }\r
797                 return HUDAQSUCCESS;\r
798         }\r
799 \r
800         T = MASTERFREQUENCY / frequency;\r
801         if(T<1) RetCode = HUDAQPARTIAL;\r
802 \r
803         Prescaler=0xB;\r
804         while(T>65535)    // T1+T2\r
805         {\r
806                 T /= 10;\r
807                 Prescaler++;\r
808                 if(Prescaler>0x0F)          //prescaller overflow (0x0F;  0x11 when aux mode is introduced)\r
809                 {                         // 0x0F means 3mHz -> 5.5min\r
810                         T=65535;                  // maximal acceptable T; minimal frequency\r
811                         Prescaler=0x0F;           // maximal acceptable prescaller\r
812                         RetCode = HUDAQPARTIAL;\r
813                         break;\r
814                 }\r
815         }\r
816 \r
817         T1 = max(1, (long)floor(T*dutycycle + 0.5));\r
818         T2 = max(1, (long)(T-T1) );\r
819 \r
820         /* A counter must be disarmed before reloading when prescaler is changed. */\r
821         if(Prescaler != pCtrCache->CTC.CountSource)\r
822         {\r
823                 if (pCtrCache->Mode!=OUT_0 && pCtrCache->Mode!=OUT_1)\r
824                         StoreByte(Ptr0, TCP, 0xC0|(1<<channel));   // disarm (OUT_0 and OUT_1 modes are already disarmed)\r
825         }\r
826 \r
827         if(pCtrCache->ACache!=T2)     // Load register\r
828         {\r
829                 StoreByte(Ptr0, TCP, (0x09+channel));\r
830                 StoreByte(Ptr0, TDP, T2);\r
831                 StoreByte(Ptr0, TDP, ((T2)>>8));\r
832                 pCtrCache->ACache=T2;\r
833         }\r
834 \r
835         if(pCtrCache->BCache!=T1)     // Hold register\r
836         {\r
837                 StoreByte(Ptr0, TCP, (0x11+channel));\r
838                 StoreByte(Ptr0, TDP, T1);\r
839                 StoreByte(Ptr0, TDP, ((T1)>>8));\r
840                 pCtrCache->BCache=T1;\r
841         }\r
842 \r
843         if (pCtrCache->Mode != PWM || Prescaler != pCtrCache->CTC.CountSource)\r
844         {\r
845                 *(int *)&pCtrCache->CTC = 0xB62;  // 000 0 1011 | 0 1 1 0 0 010\r
846                 pCtrCache->CTC.CountSource = Prescaler;\r
847                 if(Prescaler>0xF) pCtrCache->CTC.CountSourceEx = 1;\r
848 \r
849                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
850                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
851                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
852 \r
853                 //I am too lazy to set aux mode register\r
854                 //StoreByte(Ptr0, TCP, AUX_TABLE[channel])\r
855                 //StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>16);\r
856                 //StoreByte(Ptr0, TDP, 0);\r
857 \r
858                 StoreByte(Ptr0, TCP, 0xE0|(channel+1));        // clear toggle out\r
859                 StoreByte(Ptr0, TCP, 0x60 | (1<<channel));     // load & arm the selected counter\r
860 \r
861                 pCtrCache->Mode = PWM;\r
862         }\r
863 \r
864         return RetCode;\r
865 }\r
866 \r
867 \r
868 static HUDAQSTATUS MF614CtrReset(const DeviceRecord *DevRecord, unsigned channel)\r
869 {\r
870         size_t Ptr0;\r
871         Counter614 *pCtrCache;\r
872 \r
873         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
874         pCtrCache = &((MF614_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
875 \r
876         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
877 \r
878         if (pCtrCache->Mode != Counting)\r
879         {\r
880                 StoreByte(Ptr0, TCP, 0xC0| (1<<channel));      // disarm\r
881 \r
882                 if(pCtrCache->ACache!=0)                       // store 0 into A register\r
883                 {\r
884                         StoreByte(Ptr0, TCP, (0x9+channel));         // Load Register\r
885                         StoreByte(Ptr0, TDP, 0);\r
886                         StoreByte(Ptr0, TDP, 0);\r
887                         pCtrCache->ACache=0;\r
888                 }\r
889 \r
890                 *(int *)&pCtrCache->CTC = 0x12A;               // 000 0 0001 | 0 0 1 0 1 010\r
891                 pCtrCache->CTC.CountSource = channel+1;\r
892 \r
893                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
894                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
895                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
896 \r
897                 StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
898                 pCtrCache->Mode = Counting;\r
899         }\r
900 \r
901         StoreByte(Ptr0, TCP, 0x40 | (1<<channel));     // load the selected counter, A register contains zero\r
902 \r
903         return HUDAQSUCCESS;\r
904 }\r
905 \r
906 \r
907 static double MF614CtrGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
908 {\r
909         size_t Ptr0;\r
910         Counter614 *pCtrCache;\r
911 \r
912         if (channel>=CTR_CHANNELS) return WRONG_VALUE;\r
913         pCtrCache = &((MF614_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
914 \r
915         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
916 \r
917         switch(param)\r
918         {\r
919                 case HudaqCtrRESETONREAD:\r
920                         return(pCtrCache->ResetOnRead);\r
921 \r
922                 case HudaqPwmCLOCKSOURCE:\r
923                 case HudaqCtrCLOCKSOURCE:\r
924                         if (pCtrCache->CTC.CountSource == channel+1)\r
925                         {\r
926                                 if (pCtrCache->CTC.EdgeMode==1)\r
927                                         return HudaqCtrCLOCKINFALLING;\r
928                                 else\r
929                                         return HudaqCtrCLOCKINRISING;\r
930                         }\r
931                         switch(pCtrCache->CTC.CountSource)\r
932                         {\r
933                                 case 0:  if (pCtrCache->CTC.EdgeMode==1)\r
934                                                  return HudaqCtrCLOCKPREVFALLING;\r
935                                          else\r
936                                                  return HudaqCtrCLOCKPREVRISING;\r
937 \r
938                                 case 11: return HudaqCtrCLOCK20MHz;   //1011-F1\r
939                                 case 12: return HudaqCtrCLOCK2MHz;    //1100-F2\r
940                                 case 13: return HudaqCtrCLOCK200kHz;  //1101-F3\r
941                                 case 14: return HudaqCtrCLOCK20kHz;   //1110-F4\r
942                                 case 15: return HudaqCtrCLOCK2kHz;    //1111-F5\r
943                         }\r
944                         return -1;  /*Unknown mode*/\r
945 \r
946                 case HudaqPwmGATESOURCE:\r
947                 case HudaqCtrGATESOURCE:\r
948                         if (pCtrCache->CTC.GateMode==0) return HudaqCtrGATEHIGH;\r
949                         return HudaqCtrGATEINPUT;\r
950 \r
951                 case HudaqPwmGATEPOLARITY:\r
952                 case HudaqCtrGATEPOLARITY:\r
953                         switch (pCtrCache->CTC.GateControl)\r
954                         {\r
955                                 case 0:\r
956                                 case 5: return 0;       // 0 - low level gate disables counting;\r
957                                 case 4: return 1;   // 1 - high level gate disables counting.\r
958                                 default: return 0;      // ??? Hudaqlib does not support these modes.\r
959                         }\r
960 \r
961                 case HudaqCtrDIRECTION:\r
962                         return pCtrCache->CTC.Direction;\r
963 \r
964                 case HudaqCtrLOADTOGGLE:\r
965                         return pCtrCache->CTC.ReloadMode;\r
966 \r
967                 case HudaqCtrREPETITION:\r
968                         return pCtrCache->CTC.RepeatMode;\r
969 \r
970                 case HudaqPwmOUTPUTCONTROL:\r
971                 case HudaqCtrOUTPUTCONTROL:\r
972                         return HudaqCtrOUTPUTNORMAL;\r
973 \r
974                 case HudaqCtrTRIGSOURCE:\r
975                         return HudaqCtrTRIGDISABLE;\r
976 \r
977                 case HudaqPwmFILTER:\r
978                 case HudaqCtrFILTER:\r
979                         return(0);\r
980 \r
981                 case HudaqPwmNUMCHANNELS:\r
982                 case HudaqCtrNUMCHANNELS:\r
983                         return CTR_CHANNELS;\r
984         }\r
985 \r
986         return WRONG_VALUE;\r
987 }\r
988 \r
989 \r
990 static HUDAQSTATUS MF614CtrSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
991 {\r
992         size_t Ptr0;\r
993         Counter614 *pCtrCache;\r
994         Counter614Control ShadCTC;\r
995 \r
996         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
997         pCtrCache = &((MF614_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
998 \r
999         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
1000 \r
1001         if (pCtrCache->Mode != Counting)\r
1002                 MF614CtrReset(DevRecord,channel);     // Enforce switching into a counter mode\r
1003 \r
1004         switch(param)\r
1005         {\r
1006                 case HudaqCtrRESETONREAD:\r
1007                         pCtrCache->ResetOnRead = (int)value;\r
1008                         return HUDAQSUCCESS;\r
1009 \r
1010                 case HudaqPwmCLOCKSOURCE:\r
1011                 case HudaqCtrCLOCKSOURCE:\r
1012                         ShadCTC = pCtrCache->CTC;\r
1013                         switch((int)value)\r
1014                         {\r
1015                                 case HudaqCtrCLOCKINRISING:\r
1016                                         ShadCTC.EdgeMode = 0;\r
1017                                         ShadCTC.CountSource = channel+1;\r
1018                                         break;\r
1019                                 case HudaqCtrCLOCKINFALLING:\r
1020                                         ShadCTC.EdgeMode = 1;\r
1021                                         ShadCTC.CountSource = channel+1;\r
1022                                         break;\r
1023                                 case HudaqCtrCLOCKPREVRISING:\r
1024                                         ShadCTC.EdgeMode = 0;\r
1025                                         ShadCTC.CountSource = 0;\r
1026                                         break;\r
1027                                 case HudaqCtrCLOCKPREVFALLING:\r
1028                                         ShadCTC.EdgeMode = 1;\r
1029                                         ShadCTC.CountSource = 0;\r
1030                                         break;\r
1031                                 case HudaqCtrCLOCK20MHz:    //1011-F1\r
1032                                         ShadCTC.CountSource=11;\r
1033                                         break;\r
1034                                 case HudaqCtrCLOCK2MHz:     //1100-F2\r
1035                                         ShadCTC.CountSource=12;\r
1036                                         break;\r
1037                                 case HudaqCtrCLOCK200kHz:   //1101-F3\r
1038                                         ShadCTC.CountSource=13;\r
1039                                         break;\r
1040                                 case HudaqCtrCLOCK20kHz:    //1110-F4\r
1041                                         ShadCTC.CountSource=14;\r
1042                                         break;\r
1043                                 case HudaqCtrCLOCK2kHz:     //1111-F5\r
1044                                         ShadCTC.CountSource=15;\r
1045                                         break;\r
1046 \r
1047                                 default:\r
1048                                         return HUDAQFAILURE;\r
1049                         }\r
1050 \r
1051                         if(*(int *)&pCtrCache->CTC != *(int *)&ShadCTC)\r
1052                         {\r
1053                                 StoreByte(Ptr0, TCP, 0xC0| (1<<channel));      // disarm\r
1054 \r
1055                                 *(int *)&pCtrCache->CTC = *(int *)&ShadCTC;\r
1056                                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
1057                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
1058                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
1059 \r
1060                                 StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
1061                         }\r
1062                         return HUDAQSUCCESS;\r
1063 \r
1064                 case HudaqCtrOUTPUTCONTROL:\r
1065                         if( (int)value == HudaqCtrOUTPUTNORMAL) return HUDAQSUCCESS;\r
1066                         return HUDAQBADARG;\r
1067 \r
1068                 case HudaqPwmGATEPOLARITY:\r
1069                 case HudaqCtrGATEPOLARITY:\r
1070                         switch ((int)value)\r
1071                         {\r
1072                                 case 0: if (pCtrCache->CTC.GateMode==0 || pCtrCache->CTC.GateMode==5) return HUDAQSUCCESS;\r
1073                                                 pCtrCache->CTC.GateMode=5;\r
1074                                         break;\r
1075                                 case 1: if (pCtrCache->CTC.GateMode==4) return HUDAQSUCCESS;\r
1076                                                 pCtrCache->CTC.GateMode=4;\r
1077                                         break;\r
1078                                 default: return HUDAQBADARG;\r
1079                         }\r
1080                         StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
1081                         StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
1082                         StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
1083 \r
1084                         StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
1085                         return HUDAQSUCCESS;\r
1086 \r
1087                 case HudaqPwmGATESOURCE:\r
1088                 case HudaqCtrGATESOURCE:\r
1089                         switch ((int)value)\r
1090                         {\r
1091                                 case HudaqCtrGATEHIGH:\r
1092                                         if (pCtrCache->CTC.GateMode==0) return HUDAQSUCCESS;\r
1093                                         pCtrCache->CTC.GateMode=0;\r
1094                                         break;\r
1095                                 case HudaqCtrGATEINPUT:\r
1096                                         if (pCtrCache->CTC.GateControl==0)\r
1097                                                 pCtrCache->CTC.GateControl=5;       //activate low level gate N\r
1098                                         else\r
1099                                         {\r
1100                                                 if (pCtrCache->CTC.GateMode==1) return HUDAQSUCCESS;  //gate is now active\r
1101                                         }\r
1102                                         pCtrCache->CTC.GateMode=1;\r
1103                                         break;\r
1104                                 default:\r
1105                                         return HUDAQBADARG;\r
1106                         }\r
1107 \r
1108                         StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
1109                         StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
1110                         StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
1111 \r
1112                         StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
1113                         return HUDAQSUCCESS;\r
1114 \r
1115                 case HudaqCtrDIRECTION:\r
1116                         if(value>=2) return HUDAQBADARG;\r
1117                         if (pCtrCache->CTC.Direction!=(int)value);\r
1118                         {\r
1119                                 pCtrCache->CTC.Direction = (int)value;\r
1120                                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
1121                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
1122                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
1123 \r
1124                                 StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
1125                         }\r
1126                         return HUDAQSUCCESS;\r
1127 \r
1128                 case HudaqCtrLOADTOGGLE:\r
1129                         if(value>=2) return HUDAQBADARG;\r
1130                         if (pCtrCache->CTC.ReloadMode!=(int)value);\r
1131                         {\r
1132                                 pCtrCache->CTC.ReloadMode=(int)value;\r
1133                                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
1134                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
1135                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
1136 \r
1137                                 StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
1138                         }\r
1139                         return HUDAQSUCCESS;\r
1140 \r
1141                 case HudaqCtrREPETITION:\r
1142                         if(value>=2) return HUDAQBADARG;\r
1143                         if (pCtrCache->CTC.RepeatMode!=(int)value);\r
1144                         {\r
1145                                 pCtrCache->CTC.RepeatMode = (int)value;\r
1146                                 StoreByte(Ptr0, TCP, channel+1);               // select counter mode register\r
1147                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC);\r
1148                                 StoreByte(Ptr0, TDP, *(int *)&pCtrCache->CTC>>8);\r
1149 \r
1150                                 StoreByte(Ptr0, TCP, 0x20 |(1<<channel));      // arm\r
1151                         }\r
1152                         return HUDAQSUCCESS;\r
1153 \r
1154                 case HudaqCtrTRIGSOURCE:\r
1155                         if ((int)value == HudaqCtrTRIGDISABLE) return HUDAQSUCCESS;\r
1156                         return HUDAQBADARG;\r
1157 \r
1158                 case HudaqPwmFILTER:\r
1159                 case HudaqCtrFILTER:\r
1160                         if ((int)value == 0) return HUDAQSUCCESS;\r
1161                         return HUDAQBADARG;\r
1162         }\r
1163 \r
1164         return HUDAQNOTSUPPORTED;\r
1165 }\r
1166 \r
1167 \r
1168 static int MF614CtrRead(const DeviceRecord *DevRecord, unsigned channel)\r
1169 {\r
1170         size_t Ptr0;\r
1171         Counter614 *pCtrCache;\r
1172 \r
1173         if (channel>=CTR_CHANNELS) return 0;\r
1174         pCtrCache = &((MF614_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
1175 \r
1176         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
1177 \r
1178         if (pCtrCache->Mode != Counting)\r
1179         {\r
1180                 MF614CtrReset(DevRecord,channel);\r
1181                 return 0;\r
1182         }\r
1183 \r
1184         StoreByte(Ptr0, TCP, 0xA0|(1<<channel));         // save counter\r
1185         StoreByte(Ptr0, TCP, 0x11+channel);              // read value hold\r
1186         pCtrCache->BCache = GetPackedWord(Ptr0,TDP);     // Hold register is influenced\r
1187 \r
1188         if(pCtrCache->ResetOnRead)\r
1189         {\r
1190                 StoreByte(Ptr0, TCP, 0x40 | (1<<channel));     // load the selected counter, A register contains zero\r
1191         }\r
1192 \r
1193         return(pCtrCache->BCache);\r
1194 }\r
1195 \r
1196 \r
1197 /** Handle stepping motor through MF614. */\r
1198 static HUDAQSTATUS MF614StepWrite(const DeviceRecord *DevRecord, unsigned channel, int position)\r
1199 {\r
1200         size_t Ptr0;\r
1201         int maskC, maskF, status, count;\r
1202         __int32 N;\r
1203         int NewSteps;\r
1204         int digin;\r
1205         int State=0;\r
1206         MF614_Private *Cache;\r
1207         Stepper614 *CacheStep;\r
1208         DWORD NewTime;\r
1209         double NewFrequency;\r
1210 \r
1211         NewTime=timeGetTime();\r
1212 \r
1213         if (channel>=STEP_CHANNELS) return HUDAQBADARG;\r
1214         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
1215 \r
1216         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
1217         CacheStep = &(Cache->step[channel]);\r
1218 \r
1219         maskF=1<<(2*channel);\r
1220         maskC=maskF<<1;\r
1221 \r
1222         if (Cache->counter[2*channel].Mode != StepperPWM)\r
1223         {           //check for the first time initialization of slave counter\r
1224                 Cache->counter[2*channel].Mode = StepperPWM;\r
1225 \r
1226                 StoreByte(Ptr0, TCP, 2*channel+1);                   // select counter 1 mode register\r
1227                 StoreByte(Ptr0, TDP, 0x22);\r
1228                 StoreByte(Ptr0, TDP, 0x8C);     // mode E, F2 source (2MHz), toggle output\r
1229 \r
1230                 N = (int) (MASTERFREQUENCY/(2*10*CacheStep->Fmin));\r
1231                 if(Cache->counter[2*channel].ACache!=N)\r
1232                 {\r
1233                         StoreByte(Ptr0, TCP, (0x9+2*channel));    //Load register\r
1234                         StoreByte(Ptr0, TDP, N);\r
1235                         StoreByte(Ptr0, TDP, N>>8);\r
1236                         Cache->counter[2*channel].ACache = N;\r
1237                 }\r
1238 \r
1239                 StoreByte(Ptr0, TCP, 0xE1+2*channel);                // reset output\r
1240 \r
1241                 State|=1;\r
1242         }\r
1243 \r
1244         if (Cache->counter[2*channel+1].Mode != StepperMaster)\r
1245         {      //check for the first time initialization of master counter\r
1246                 Cache->counter[2*channel+1].Mode = StepperMaster;\r
1247 \r
1248                 StoreByte(Ptr0, TCP, 2*channel+2);                   // select counter 2 mode register\r
1249                 StoreByte(Ptr0, TDP, 0x02);\r
1250                 StoreByte(Ptr0, TDP, 0x12+2*channel);                // mode A, output TC toggle\r
1251                 // Internal TC(n-1) do not work, I suspect internal hazards.\r
1252 \r
1253                 if(Cache->counter[2*channel+1].ACache!=1)\r
1254                 {\r
1255                         StoreByte(Ptr0, TCP, (0x9+2*channel+1));           //Load register\r
1256                         StoreByte(Ptr0, TDP, 1);\r
1257                         StoreByte(Ptr0, TDP, 0);\r
1258                         Cache->counter[2*channel+1].ACache = 1;\r
1259                 }\r
1260 \r
1261                 StoreByte(Ptr0, TCP, 0xE1 + 2*channel+1);            // reset output\r
1262 \r
1263                 State|=2;\r
1264         }\r
1265 \r
1266         if (State!=0)         //check for the first time initialization\r
1267         {\r
1268                 CacheStep->Direction=0;\r
1269                 CacheStep->LastPosition=0;\r
1270                 CacheStep->ChainedPosition=0;\r
1271                 CacheStep->Frequency=0;\r
1272 \r
1273                 StoreByte(Ptr0, TCP, 0x4F);                      // load counters\r
1274                 StoreByte(Ptr0, TCP, 0x25);                      // arm counters\r
1275         }\r
1276 \r
1277         //********* check for end switches **********\r
1278         digin = GetByte(DevRecord->DrvRes.Resources.MemResources[1].Base,DIN);\r
1279         if( ((digin & (1<<(2*channel)))==0 && (CacheStep->Direction>0)) ||\r
1280                         ( ((digin & (1<<(2*channel+1)))==0) && (CacheStep->Direction<0)) )  // end switch\r
1281         {\r
1282                 StoreByte(Ptr0, TCP, 0xA0|maskF|maskC);         // save both counters\r
1283                 StoreByte(Ptr0, TCP, 0x11+2*channel+1);         // read count hold\r
1284                 NewSteps = GetPackedWord(Ptr0,TDP);\r
1285                 Cache->counter[2*channel+1].BCache = NewSteps++;\r
1286 \r
1287                 StoreByte(Ptr0, TCP, 0x1F);                     // read status register\r
1288                 status=GetPackedWord(Ptr0,TDP);\r
1289                 if((status & (0x04<<2*channel)) == 0)           // stopped?\r
1290                         NewSteps = 0;\r
1291 \r
1292                 StoreByte(Ptr0, TCP, 0xE2+2*channel);           // reset output\r
1293                 StoreByte(Ptr0, TCP, 0xC0|maskC);               // disarm\r
1294 \r
1295                 CacheStep->LastPosition -= NewSteps * Cache->step[channel].Direction;   //actualize real position\r
1296 \r
1297                 CacheStep->Frequency = 0;\r
1298                 CacheStep->Direction = 0;\r
1299                 CacheStep->TimeStamp = NewTime;\r
1300                 return HUDAQSUCCESS;\r
1301         }\r
1302 \r
1303         //***** actualize step count ******\r
1304         if (CacheStep->LastPosition!=position)\r
1305         {                                     //amount of steps to do\r
1306                 if( (State&3)!=0 ) //the first time initialization\r
1307                 {\r
1308                         NewSteps = 0;\r
1309                 }\r
1310                 else               // counter already runs\r
1311                 {\r
1312                         StoreByte(Ptr0, TCP, 0x1F);\r
1313                         status=GetPackedWord(Ptr0,TDP);\r
1314                         if((status & (0x04<<2*channel)) ==0)               // stopped\r
1315                                 NewSteps = 0;\r
1316                         else\r
1317                         {\r
1318                                 StoreByte(Ptr0, TCP, 0xA0|maskF|maskC);          // save both counters\r
1319                                 StoreByte(Ptr0, TCP, 0x11+2*channel);            // read frequency hold\r
1320                                 Cache->counter[2*channel].BCache =\r
1321                                         status = count = GetPackedWord(Ptr0,TDP);\r
1322                                 while(count < 40)           // wait until safe to change pulse count\r
1323                                 {\r
1324                                         StoreByte(Ptr0, TCP, 0xA0|maskF|maskC);        // save both counters\r
1325                                         StoreByte(Ptr0, TCP, 0x11+2*channel);          // read frequency hold\r
1326                                         Cache->counter[2*channel].BCache =\r
1327                                                 count = GetPackedWord(Ptr0,TDP);\r
1328                                         if(status==count)\r
1329                                         {\r
1330                                                 Cache->counter[2*channel+1].BCache = 0;\r
1331                                                 NewSteps = 0;\r
1332                                                 goto UnexpectedStop;                         //unexpected stop of frequency counter\r
1333                                         }\r
1334                                         status=count;\r
1335                                 }\r
1336                                 StoreByte(Ptr0, TCP, 0x11+2*channel+1);          // read count hold\r
1337                                 NewSteps = GetPackedWord(Ptr0,TDP);\r
1338                                 Cache->counter[2*channel+1].BCache = NewSteps++;\r
1339 UnexpectedStop: ;\r
1340                         }\r
1341                 }\r
1342 \r
1343                 if(NewSteps==0)\r
1344                 {\r
1345                         CacheStep->Direction=0;   //motor is already stopped.\r
1346                         CacheStep->Frequency=0;\r
1347                 }\r
1348 \r
1349                 // CurrentPosition = LastPosition - |x|*Direction\r
1350                 // CurrentPosition = NewPosition - NewSteps\r
1351                 //   ===> NewSteps = NewPosition - LastPosition + |x|*Direction\r
1352                 NewSteps = position - CacheStep->LastPosition + NewSteps*CacheStep->Direction;\r
1353 \r
1354 \r
1355                 if (NewSteps>2)     //It is impossible to realise less than 3 steps???\r
1356                 {\r
1357                         if (CacheStep->Direction==-1)\r
1358                         {       //direction has been reversed, calculate a false target\r
1359                                 //NewSteps = Fmin(channel)*Fmin(channel)+Frequency[channel]*Frequency[channel])/(2*Acc(channel)) - 1;\r
1360                                 // \todo   Study behaviour of this feature. It lets stepper to finish in oposite direction.\r
1361                                 goto TargetIsFixed;\r
1362                         }\r
1363                         CacheStep->LastPosition = position;   //actualize position cache\r
1364                         CacheStep->Direction=1;\r
1365                         if( NewSteps > 0xFFFF )\r
1366                         {\r
1367                                 CacheStep->LastPosition -= NewSteps-0xFFFF;\r
1368                                 NewSteps = 0xFFFF;\r
1369                         }\r
1370                         StoreByte(Ptr0, TCP, 0xC0|maskC);                     // disarm count\r
1371                         StoreByte(Ptr0, TCP, 0x0A+2*channel);                 // count load register\r
1372                         StoreByte(Ptr0, TDP, NewSteps-1);\r
1373                         StoreByte(Ptr0, TDP, (NewSteps-1)>>8);\r
1374                         State |= 4;                           //reload & run counter\r
1375                 }\r
1376 \r
1377                 if (NewSteps<-2)   //It is impossible to realise less than 3 steps???\r
1378                 {\r
1379                         if (CacheStep->Direction==1)         //if (Frequency[channel]>Fmin[channel])\r
1380                         {       //direction has been reversed, calculate a false target\r
1381                                 //NewSteps = Fmin(channel)*Fmin(channel)+Frequency[channel]*Frequency[channel])/(2*Acc(channel)) - 1;\r
1382                                 // \todo   Study behaviour of this feature. It lets stepper to finish in oposite direction.\r
1383                                 goto TargetIsFixed;\r
1384                         }\r
1385                         CacheStep->LastPosition = position;        //actualize position cache\r
1386                         CacheStep->Direction = -1;\r
1387                         NewSteps = -NewSteps;\r
1388                         if( NewSteps > 0xFFFF )\r
1389                         {\r
1390                                 CacheStep->LastPosition += NewSteps-0xFFFF;\r
1391                                 NewSteps = 0xFFFF;\r
1392                         }\r
1393                         StoreByte(Ptr0, TCP, 0xC0|maskC);                       // disarm count\r
1394                         StoreByte(Ptr0, TCP, 0x0A+2*channel);                   // count load register\r
1395                         StoreByte(Ptr0, TDP, NewSteps-1);\r
1396                         StoreByte(Ptr0, TDP, (NewSteps-1)>>8);\r
1397                         State |= 4;                                //reload & run counter\r
1398                 }\r
1399 \r
1400                 if (NewSteps==0)\r
1401                 {\r
1402                         //CacheStep->LastPosition;   does not need to be actualised\r
1403                         CacheStep->Direction=0;\r
1404                 }\r
1405         }\r
1406 \r
1407 TargetIsFixed:\r
1408         CacheStep->ChainedPosition = position;   // chained position makes sense only if direction is reversed\r
1409 \r
1410 \r
1411         //************ set frequency - (lastreq contains amount of remainning steps) ***********\r
1412         if (CacheStep->Acc<=0)\r
1413                 CacheStep->Frequency=CacheStep->Fmax;        // full acceleration when acceleration is negative\r
1414         else\r
1415         {\r
1416                 if (CacheStep->Frequency==0)\r
1417                 {\r
1418                         CacheStep->Frequency = CacheStep->Fmin;\r
1419                 }\r
1420                 else\r
1421                 {\r
1422                         if ((CacheStep->Acc > 0) && (NewSteps > (CacheStep->Fmin*CacheStep->Fmin+CacheStep->Frequency*CacheStep->Frequency)/(2*CacheStep->Acc)))\r
1423                                 NewFrequency = CacheStep->Frequency + CacheStep->Acc*abs((long)NewTime-(long)CacheStep->TimeStamp)/1000.0;\r
1424                         else\r
1425                                 NewFrequency = CacheStep->Frequency + CacheStep->Acc*abs((long)NewTime-(long)CacheStep->TimeStamp)/1000.0;\r
1426 \r
1427                         if (NewFrequency > CacheStep->Fmax) NewFrequency = CacheStep->Fmax;\r
1428                         if (NewFrequency < CacheStep->Fmin) NewFrequency = CacheStep->Fmin;\r
1429 \r
1430                         if (NewFrequency>(CacheStep->Frequency+MAXIMAL_STEP_INCREMENT)) //maximal increment threshold\r
1431                         {\r
1432                                 CacheStep->Frequency += MAXIMAL_STEP_INCREMENT;\r
1433                         }\r
1434                         else\r
1435                                 CacheStep->Frequency = NewFrequency;\r
1436                 }\r
1437         }\r
1438 \r
1439         N = (__int32)(MASTERFREQUENCY / (10*CacheStep->Frequency));   //set a real frequency\r
1440         N = max(1,N/2);\r
1441         if(N>65535) N=65535;\r
1442 \r
1443         if(Cache->counter[2*channel].ACache != N)\r
1444         {\r
1445                 StoreByte(Ptr0, TCP, 0x09+2*channel);\r
1446                 StoreByte(Ptr0, TDP, N);\r
1447                 StoreByte(Ptr0, TDP, N>>8);\r
1448                 Cache->counter[2*channel].ACache = N;\r
1449         }\r
1450 \r
1451         //write stepper direction to digital output\r
1452         if(CacheStep->Direction!=0)  // only when stepper is expected to be running\r
1453                 MF614DOWriteBit(DevRecord,0,channel,CacheStep->Direction>0);\r
1454 \r
1455         //start counters\r
1456         if ( (State&4) != 0 )\r
1457         {\r
1458                 StoreByte(Ptr0, TCP, 0x60|maskC);                // load and arm count\r
1459                 StoreByte(Ptr0, TCP, 0xEA+2*channel);            // set toggle out -> start\r
1460         }\r
1461 \r
1462         CacheStep->TimeStamp = NewTime;                    //refresh timestamp\r
1463         return HUDAQSUCCESS;\r
1464 }\r
1465 \r
1466 \r
1467 \r
1468 static HUDAQSTATUS MF614StepSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1469 {\r
1470         MF614_Private *Cache;\r
1471         size_t Ptr0;\r
1472         int status, RemainingSteps;\r
1473 \r
1474         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
1475         if (channel>=STEP_CHANNELS) return HUDAQBADARG;\r
1476 \r
1477         if (Cache->counter[2*channel].Mode != StepperPWM || Cache->counter[2*channel+1].Mode != StepperMaster)\r
1478                 MF614StepWrite(DevRecord,channel,0);     //call stepper initialization if it is not called before\r
1479 \r
1480         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
1481 \r
1482         switch(param)\r
1483         {\r
1484                 case HudaqStepFMIN:\r
1485                         Cache->step[channel].Fmin = value;\r
1486                         return HUDAQSUCCESS;\r
1487                 case HudaqStepFMAX:\r
1488                         Cache->step[channel].Fmax = value;\r
1489                         return HUDAQSUCCESS;\r
1490                 case HudaqStepACCELERATION:\r
1491                         Cache->step[channel].Acc = value;\r
1492                         return HUDAQSUCCESS;\r
1493 \r
1494                 case HudaqStepCURRENTPOSITION:\r
1495                         StoreByte(Ptr0, TCP, 0xA0|(2<<(2*channel)));    // save counter\r
1496                         StoreByte(Ptr0, TCP, 0x11+2*channel+1);         // read count hold\r
1497                         RemainingSteps = GetPackedWord(Ptr0,TDP);\r
1498                         Cache->counter[2*channel+1].BCache = RemainingSteps++;\r
1499 \r
1500                         StoreByte(Ptr0, TCP, 0x1F);                     // read status register\r
1501                         status = GetPackedWord(Ptr0,TDP);\r
1502                         if((status & (0x04<<2*channel)) == 0)           // stopped?\r
1503                                 RemainingSteps = 0;\r
1504 \r
1505                         // CurrentPosition = LastPosition - Direction * AmountOfSteps\r
1506                         //     NewPosition = NewLastPosition - Direction * AmountOfSteps\r
1507                         // ==>  NewLastPosition = NewPosition + Direction * AmountOfSteps\r
1508                         Cache->step[channel].LastPosition =\r
1509                                 (int)value + Cache->step[channel].Direction * RemainingSteps;\r
1510                         return HUDAQSUCCESS;\r
1511 \r
1512         }\r
1513 \r
1514         return HUDAQNOTSUPPORTED;\r
1515 }\r
1516 \r
1517 \r
1518 static double MF614StepGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1519 {\r
1520         MF614_Private *Cache;\r
1521         size_t Ptr0;\r
1522         int status, RetVal;\r
1523 \r
1524         Cache = (MF614_Private *)DevRecord->DrvRes.DriverData;\r
1525         if (channel>=2) return WRONG_VALUE;\r
1526 \r
1527         if (Cache->counter[2*channel].Mode != StepperPWM || Cache->counter[2*channel+1].Mode != StepperMaster)\r
1528                 MF614StepWrite(DevRecord,channel,0);     //call stepper initialization if it is not called before\r
1529 \r
1530         Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
1531 \r
1532         switch(param)\r
1533         {\r
1534                 case HudaqStepFMIN:\r
1535                         return(Cache->step[channel].Fmin);\r
1536                 case HudaqStepFMAX:\r
1537                         return(Cache->step[channel].Fmax);\r
1538                 case HudaqStepACCELERATION:\r
1539                         return(Cache->step[channel].Acc);\r
1540                 case HudaqStepCURRENTPOSITION:      // CurrentPosition = LastPosition - Direction * AmountOfSteps\r
1541                         StoreByte(Ptr0, TCP, 0xA0|(2<<(2*channel)));    // save counter\r
1542                         StoreByte(Ptr0, TCP, 0x11+2*channel+1);         // read count hold\r
1543                         RetVal = GetPackedWord(Ptr0,TDP);\r
1544                         Cache->counter[2*channel+1].BCache = RetVal++;\r
1545 \r
1546                         StoreByte(Ptr0, TCP, 0x1F);                     // read status register\r
1547                         status=GetPackedWord(Ptr0,TDP);\r
1548                         if((status & (0x04<<2*channel)) == 0)           // stopped?\r
1549                                 return(0);\r
1550 \r
1551                         return(Cache->step[channel].LastPosition -\r
1552                                         Cache->step[channel].Direction * RetVal);\r
1553 \r
1554                 case HudaqStepREMAININGSTEPS:\r
1555                         StoreByte(Ptr0, TCP, 0xA0|(2<<(2*channel)));    // save counter\r
1556                         StoreByte(Ptr0, TCP, 0x11+2*channel+1);         // read count hold\r
1557                         RetVal = GetPackedWord(Ptr0,TDP);\r
1558                         Cache->counter[2*channel+1].BCache = RetVal++;\r
1559 \r
1560                         StoreByte(Ptr0, TCP, 0x1F);                     // read status register\r
1561                         status=GetPackedWord(Ptr0,TDP);\r
1562                         if((status & (0x04<<2*channel)) == 0)           // stopped?\r
1563                                 return(0);\r
1564                         return(RetVal);\r
1565 \r
1566                 case HudaqStepTARGETPOSITION:\r
1567                         return(Cache->step[channel].ChainedPosition);\r
1568                 case HudaqStepNUMCHANNELS:\r
1569                         return STEP_CHANNELS;\r
1570         }\r
1571 \r
1572         return WRONG_VALUE;\r
1573 }\r
1574 \r
1575 \r
1576 /****************************************************************\r
1577  *                                                              *\r
1578  *                    GET/SET PARAMETERS                        *\r
1579  *                                                              *\r
1580  ****************************************************************/\r
1581 \r
1582 static HUDAQSTATUS MF614SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1583 {\r
1584         switch(param & HudaqSubsystemMASK)\r
1585         {\r
1586                 //case HudaqDI:\r
1587                 //case HudaqDO:\r
1588                 case HudaqAI:  return MF614AISetParameter(DevRecord,channel,param,value);\r
1589                 case HudaqAO:  return MF614AOSetParameter(DevRecord,channel,param,value);\r
1590                 case HudaqEnc: return MF614EncSetParameter(DevRecord,channel,param,value);\r
1591                 case HudaqCtr: return MF614CtrSetParameter(DevRecord,channel,param,value);\r
1592                 case HudaqStep:return MF614StepSetParameter(DevRecord,channel,param,value);\r
1593         }\r
1594         return HUDAQNOTSUPPORTED;\r
1595 }\r
1596 \r
1597 \r
1598 static HUDAQSTATUS AD612SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1599 {\r
1600         switch(param & HudaqSubsystemMASK)\r
1601         {\r
1602                 //case HudaqDI:\r
1603                 //case HudaqDO:\r
1604                 case HudaqAI:  return MF614AISetParameter(DevRecord,channel,param,value);\r
1605                 case HudaqAO:  return MF614AOSetParameter(DevRecord,channel,param,value);\r
1606                                //case HudaqEnc:\r
1607                                //case HudaqCtr:\r
1608                                //case HudaqStep:\r
1609         }\r
1610         return HUDAQNOTSUPPORTED;\r
1611 }\r
1612 \r
1613 \r
1614 static double MF614GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1615 {\r
1616         switch(param & HudaqSubsystemMASK)\r
1617         {\r
1618                 case HudaqDI:\r
1619                 case HudaqDO:  return MF614DIOGetParameter(channel,param);\r
1620                 case HudaqAI:  return MF614AIGetParameter(DevRecord,channel,param);\r
1621                 case HudaqAO:  return MF614AOGetParameter(DevRecord,channel,param);\r
1622                 case HudaqEnc: return MF614EncGetParameter(DevRecord,channel,param);\r
1623                 case HudaqCtr: return MF614CtrGetParameter(DevRecord,channel,param);\r
1624                 case HudaqStep:return MF614StepGetParameter(DevRecord,channel,param);\r
1625         }\r
1626         return WRONG_VALUE;\r
1627 }\r
1628 \r
1629 \r
1630 static double AD612GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1631 {\r
1632         switch(param & HudaqSubsystemMASK)\r
1633         {\r
1634                 case HudaqDI:\r
1635                 case HudaqDO:  return MF614DIOGetParameter(channel,param);\r
1636                 case HudaqAI:  return MF614AIGetParameter(DevRecord,channel,param);\r
1637                 case HudaqAO:  return MF614AOGetParameter(DevRecord,channel,param);\r
1638                                //case HudaqEnc:\r
1639                                //case HudaqCtr:\r
1640                                //case HudaqStep:\r
1641         }\r
1642         return WRONG_VALUE;\r
1643 }\r
1644 \r
1645 \r
1646 static const HudaqRange *MF614QueryRange(const DeviceRecord *DevRecord, HudaqSubsystem S, unsigned item)\r
1647 {\r
1648         switch(S & HudaqSubsystemMASK)\r
1649         {\r
1650                 case HudaqAI:  if(item>=4) return NULL;\r
1651                                        return &MF614RangeAIO[item];\r
1652                 case HudaqAO:  if(item>=1) return NULL;\r
1653                                        return &MF614RangeAIO[0];\r
1654         }\r
1655         return NULL;\r
1656 }\r
1657 \r
1658 \r
1659 \r
1660 \r
1661 /****************************************************************\r
1662  *                                                              *\r
1663  *                       INITIALIZATION                         *\r
1664  *                                                              *\r
1665  ****************************************************************/\r
1666 \r
1667 /** Initialize subset of hardware that is supported by both MF614 & AD612. */\r
1668 static void InitAiAoDiDo(size_t Ptr0, size_t Ptr1, MF614_Private *Cache)\r
1669 {\r
1670         int i;\r
1671 \r
1672         /* Initialization of AD's */\r
1673         for(i=0;i<AD_CHANNELS;i++)\r
1674         {\r
1675                 Cache->AIOptions[i].Range=1;    // 10V\r
1676                 Cache->AIOptions[i].Bipolar=1;  // + -\r
1677                 Cache->AIOptions[i].Raw=0;\r
1678         }\r
1679 \r
1680         /* Initialization of DA's */\r
1681         for(i=0;i<DA_CHANNELS;i++)\r
1682         {\r
1683                 Cache->DAOptions[i].Raw = 0;\r
1684                 Cache->DAOptions[i].LoCache = 0;\r
1685                 Cache->DAOptions[i].HiCache = 0x8;\r
1686 \r
1687                 StoreByte(Ptr0, DA0LO + 2*i, 0);\r
1688                 StoreByte(Ptr0, DA0HI + 2*i, 0x8);\r
1689         }\r
1690         i = GetByte(Ptr0, DALATCHEN);            // update the output\r
1691 \r
1692         /* Initialization of digital outputs */\r
1693         Cache->DoutCache = 0;\r
1694         StoreByte(Ptr0, DOUT, 0);\r
1695 }\r
1696 \r
1697 \r
1698 /** Internal initialization function that should be called from HudaqOpenDevice\r
1699  * \todo Exclusive access is only partially implemented. */\r
1700 static int Init614(DeviceRecord *DevRecord, int IniOptions)\r
1701 {\r
1702         size_t Ptr0, Ptr1, Ptr2;\r
1703         MF614_Private *Cache;\r
1704         int i;\r
1705 \r
1706         if (DevRecord==NULL) return -1;\r
1707 \r
1708         if (DevRecord->DrvRes.Resources.NumMemResources<2) return -2;  //insufficient amount of resources\r
1709         Cache = DevRecord->DrvRes.DriverData;\r
1710         if (Cache==NULL) return -3;\r
1711         if (DevRecord->DrvRes.DriverDataSize<sizeof(MF614_Private)) return -4;\r
1712 \r
1713         DevRecord->pCT = &CT614;\r
1714 \r
1715         if ((IniOptions & HudaqOpenNOINIT)==0)\r
1716         {                           //initialize hardware only if no other application is running\r
1717                 Ptr0 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
1718                 Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base + 0x400;\r
1719                 Ptr2 = DevRecord->DrvRes.Resources.MemResources[0].Base;\r
1720 \r
1721                 /* Initialization of AD's, DA's, DI and DO. */\r
1722                 InitAiAoDiDo(Ptr0, Ptr1, Cache);\r
1723 \r
1724                 /* initialization IRC */\r
1725                 for (i=0; i<ENC_CHANNELS; i++)\r
1726                 {\r
1727                         StoreByte(Ptr0,IRC0CMDWR+2*i,0x03);    // reset BP & CNTR  0 00 00 01 1\r
1728                         StoreByte(Ptr0, IRC0DATAWR+2*i, 9);    // PR0=9; program filter to 250 kHz (20MHz/9)\r
1729                         StoreByte(Ptr0, IRC0CMDWR+2*i, 0x18);  // PR0 -> PSC\r
1730                         Cache->EncOptions[i].Filter = 9;\r
1731 \r
1732                         StoreByte(Ptr0,IRC0CMDWR+2*i,0x38);    // CMR 0 01 11 0 0 0\r
1733                         Cache->EncOptions[i].Quadrature = 3;   // ----------^\r
1734 \r
1735                         StoreByte(Ptr0,IRC0CMDWR+2*i,0x45);    // IOR 0 10 00 1 0 1 (use gate)\r
1736                         Cache->EncOptions[i].Index = 0;\r
1737 \r
1738                         StoreByte(Ptr0,IRC0CMDWR+2*i,0x60);    // IDR 0 11 00 0 0 0 (index pulse polarity)\r
1739                         Cache->EncOptions[i].Polarity = 0;     // --------------^ ^\r
1740                         Cache->EncOptions[i].Level = 1;        // ----------------|\r
1741 \r
1742                         Cache->EncOptions[i].ResetOnRead = 0;\r
1743                 }\r
1744 \r
1745                 /* Initialization of counters */\r
1746                 /* set counters mode */\r
1747                 StoreByte(Ptr0,TCP,0xFF);                 // master reset\r
1748                 StoreByte(Ptr0,TCP,0x17);                 // master mode register\r
1749                 StoreByte(Ptr0,TDP,0x00);\r
1750                 StoreByte(Ptr0,TDP,0x80);                 // BCD prescaler\r
1751                 for(i=0;i<CTR_CHANNELS;i++)\r
1752                 {\r
1753                         Cache->counter[i].Mode = Unspecified;\r
1754                         Cache->counter[i].ACache = 0;\r
1755                         Cache->counter[i].BCache = 0;\r
1756                 }\r
1757 \r
1758                 /* Initialization of stepper motors courtesy */\r
1759                 for(i=0;i<STEP_CHANNELS;i++)\r
1760                 {\r
1761                         Cache->step[i].LastPosition = 0;\r
1762                         Cache->step[i].ChainedPosition = 0;\r
1763                         Cache->step[i].Direction = 0;\r
1764 \r
1765                         Cache->step[i].Fmin = 1000;\r
1766                         Cache->step[i].Fmax = 5000;\r
1767                         Cache->step[i].Acc = 1000;\r
1768                 }\r
1769 \r
1770         }\r
1771 \r
1772         return 1; //success\r
1773 }\r
1774 \r
1775 \r
1776 /** Internal cleanup procedure for MF614 & AD612 */\r
1777 static void Done614(DeviceRecord *DevRecord)\r
1778 {\r
1779         if (DevRecord==NULL) return;\r
1780 \r
1781         DevRecord->pCT = &CtDummy;\r
1782 }\r
1783 \r
1784 \r
1785 const CallTable CT614 =\r
1786 {\r
1787         "MF614", 0x186C, 0x0614,\r
1788         Init614,\r
1789         Done614,\r
1790 \r
1791         MF614SetParameter,\r
1792         MF614GetParameter,\r
1793         MF614QueryRange,\r
1794 \r
1795         // INITIALIZE DI callers\r
1796         MF614DIRead,\r
1797         GenericDIReadBit,           //Generic implementation\r
1798         GenericOneDIReadMultiple,\r
1799         // INITIALIZE DO callers\r
1800         MF614DOWrite,\r
1801         MF614DOWriteBit,\r
1802         MF614DOWriteMultipleBits,\r
1803         GenericDOWriteMultiple,\r
1804         // INITIALIZE AI callers\r
1805         MF614AIRead,\r
1806         GenericAIReadMultiple,\r
1807         // INITIALIZE AO callers\r
1808         MF614AOWrite,\r
1809         MF614AOWriteMultiple,\r
1810         // INITIALIZE Enc callers\r
1811         MF614EncRead,\r
1812         MF614EncReset,\r
1813         // INITIALIZE Ctr callers\r
1814         MF614CtrRead,\r
1815         MF614CtrReset,\r
1816         // INITIALIZE PWM callers\r
1817         MF614PWMWrite,\r
1818         NULL,\r
1819         NULL,\r
1820         // INITIALIZE Step callers\r
1821         MF614StepWrite,\r
1822 };\r
1823 \r
1824 \r
1825 /* Initialization procedure for AD612 card. */\r
1826 static int Init612(DeviceRecord *DevRecord, int IniOptions)\r
1827 {\r
1828         MF614_Private *Cache;\r
1829 \r
1830         if (DevRecord==NULL) return -1;\r
1831 \r
1832         if (DevRecord->DrvRes.Resources.NumMemResources<2) return -2;  //insufficient amount of resources\r
1833         Cache = DevRecord->DrvRes.DriverData;\r
1834         if (Cache==NULL) return -3;\r
1835         if (DevRecord->DrvRes.DriverDataSize<sizeof(MF614_Private)) return -4;\r
1836 \r
1837         DevRecord->pCT = &CT612;\r
1838 \r
1839         if ((IniOptions & HudaqOpenNOINIT)==0)\r
1840         {                           //initialize hardware only if no other application is running\r
1841                 /* Initialization of AD's, DA's, DI and DO. */\r
1842                 InitAiAoDiDo(DevRecord->DrvRes.Resources.MemResources[1].Base,         //Ptr0\r
1843                                 DevRecord->DrvRes.Resources.MemResources[1].Base + 0x400, //Ptr1\r
1844                                 Cache);\r
1845         }\r
1846 \r
1847         return 1; //success\r
1848 }\r
1849 \r
1850 \r
1851 const CallTable CT612 =\r
1852 {\r
1853         "AD612", 0x186C, 0x0612,\r
1854         Init612,\r
1855         Done614,\r
1856 \r
1857         AD612SetParameter,\r
1858         AD612GetParameter,\r
1859         MF614QueryRange,\r
1860 \r
1861         // INITIALIZE DI callers\r
1862         MF614DIRead,\r
1863         GenericDIReadBit,           //Generic implementation\r
1864         GenericOneDIReadMultiple,\r
1865         // INITIALIZE DO callers\r
1866         MF614DOWrite,\r
1867         MF614DOWriteBit,\r
1868         MF614DOWriteMultipleBits,\r
1869         GenericDOWriteMultiple,\r
1870         // INITIALIZE AI callers\r
1871         MF614AIRead,\r
1872         GenericAIReadMultiple,\r
1873         // INITIALIZE AO callers\r
1874         MF614AOWrite,\r
1875         MF614AOWriteMultiple,\r
1876         // INITIALIZE Enc callers\r
1877         NULL,                       // Not available\r
1878         NULL,\r
1879         // INITIALIZE Ctr callers\r
1880         NULL,                       // Not available\r
1881         NULL,\r
1882         // INITIALIZE PWM callers\r
1883         NULL,                       // Not available\r
1884         NULL,\r
1885         NULL,\r
1886         // INITIALIZE Step callers\r
1887         NULL,                       // Not available\r
1888 };\r