]> rtime.felk.cvut.cz Git - mf6xx.git/blob - src/hudaqlib/MF624.c
QEMU mf624.c formatted to make QEMU checkpatch.pl mostly happy.
[mf6xx.git] / src / hudaqlib / MF624.c
1 /****************************************************************/\r
2 /**@file MF624.c:\r
3  * Description: API layer for MF624 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 #else\r
11 #include <sys/io.h>\r
12 #endif\r
13 #include <malloc.h>\r
14 #include <math.h>\r
15 \r
16 #include "hudaqlib.h"\r
17 #include "hudaq_internal.h"\r
18 \r
19 \r
20 \r
21 /** IRC control register */\r
22 typedef struct\r
23 {\r
24         unsigned int IRC_MODE:2;      ///< 0-IRC; 01-Rising Edge; 10-Falling Edge; 11-either edges\r
25         unsigned int BLOCK_MODE:2;    ///< 00-enable; 01-disable; 10-count if I=0; 11-10-count if I=1\r
26         unsigned int RESET_MODE:3;    ///< 000-enable; 001b-reset; 010-reset I=0; 011-reset I=1\r
27         ///< 100-reset on rising I; 101-reset on falling I; 110-reset on either edges I; 111-none\r
28         unsigned int FILTER:1;        ///< 0-off; 1-on\r
29 } IRC_Control;\r
30 \r
31 \r
32 /** Counter control register */\r
33 typedef struct\r
34 {\r
35         unsigned int Direction:1;     ///< 0-Down; 1-Up\r
36         unsigned int Repetition:1;    ///< 0-One shot; 1-repeat\r
37         unsigned int LoadToggle:1;    ///< 0-Load from A; 1-alternate A and B\r
38         unsigned int OutputToggle:1;  ///< 0-terminal count; 1-toggle\r
39         unsigned int OutputControl:2; ///< 00-output; 01-inverted output; 10-0; 11-1\r
40         unsigned int TriggerSource:2; ///< 00-1; 01-IN; 10-Out_n-1; 11-Out_n+1\r
41         unsigned int TriggerType:2;   ///< 00-disabled; 01-rising edge; 10-falling edge; 11-either egde\r
42         unsigned int ReTrigger:1;     ///< 0-disabled; 1-enabled\r
43         unsigned int GateSource:2;    ///< 00-1; 01-IN; 10-Out_n-1; 11-Out_n+1\r
44         unsigned int GatePolarity:1;  ///< 0-'0' disables counting; 1-'1' disables counting\r
45         unsigned int ClockSource:4;   ///< 0-internal 50MHz; 1-10MHz; 2-1MHz; 3-100kHz; 4-NOTHING\r
46         ///< 5-Rise IN; 6-Fall IN; 7-Either IN; 8-NOTHING\r
47         ///< 9-Rise N-1; 10-Fall N-1; 11-Either N-1; 12-NOTHING\r
48         ///< 13-Rise N+1; 14-Fall N+1; 15-Either N+1\r
49         unsigned int Filter:1;        ///< 0-off; 1-on\r
50 } CounterControl;\r
51 \r
52 \r
53 /** This enum contains internal state info. */\r
54 typedef enum\r
55 {\r
56         Unspecified = 0,\r
57         PWM,                          ///< PWM is output from counter\r
58         OUT_0,                        ///< counter is generating 0 permanently\r
59         OUT_1,                        ///< counter is generating 1 permanently\r
60         Counting,                     ///< counting on external signal\r
61         StepperPWM,                   ///< Part of stepper motor that generates output\r
62         StepperMaster,                ///< Part of stepper motors that counts pulses\r
63         custom                        ///< counter contains externally defined value\r
64 } InternalCounterMode;\r
65 \r
66 \r
67 /** Symbolic constants for handling Counter Control Register. These cnstants are aimed to\r
68   use by this way: GLOB_CTR_BASE0*(GLOB_CTR_STOP|GLOB_CTR_LOAD) | GLOB_CTR_BASE1*(GLOB_CTR_START) */\r
69 #define GLOB_CTR_START      1    ///< Counter start command\r
70 #define GLOB_CTR_STOP       2    ///< Counter stop command\r
71 #define GLOB_CTR_LOAD       4    ///< Counter load command\r
72 #define GLOB_CTR_RESET      8    ///< Counter reset command\r
73 #define GLOB_CTR_OUT_SET   16    ///< Set counter output toggle bit\r
74 #define GLOB_CTR_OUT_RESET 32    ///< Reset counter output toggle bit\r
75 \r
76 #define GLOB_CTR_BASE0         1 ///< bit offset of a 1st counter in GLOB_CTR register\r
77 #define GLOB_CTR_BASE1      0x40 ///< bit offset of a 2nd counter in GLOB_CTR register\r
78 #define GLOB_CTR_BASE2    0x1000 ///< bit offset of a 3rd counter in GLOB_CTR register\r
79 #define GLOB_CTR_BASE3   0x40000 ///< bit offset of a 4th counter in GLOB_CTR register\r
80 #define GLOB_CTR_BASE4 0x1000000 ///< bit offset of a 5th counter in GLOB_CTR register\r
81 \r
82 \r
83 #define DA_CHANNELS   8         ///< Amount of Analog Outputs\r
84 #define AD_CHANNELS   8         ///< Amount of Analog Inputs\r
85 #define ENC_CHANNELS  4         ///< Amount of Encoders\r
86 #define CTR_CHANNELS  5         ///< Amount of counters\r
87 #define STEP_CHANNELS 2         ///< Amount of steppers\r
88 #define PWM_625_SUBCHANNELS  6  ///< Amount of PWM channel comparators\r
89 #define PWM_625_CHANNELS 1      ///< Amount of PWM channels\r
90 \r
91 \r
92 \r
93 /** One record related to stepper motor. */\r
94 typedef struct\r
95 {\r
96         double Fmin;                  ///< Minimal frequency of stepper motor\r
97         double Fmax;                  ///< Maximal frequency of stepper motor\r
98         signed char Direction;        ///< Direction of stepping motor\r
99         __int32 LastPosition;         ///< Stepper motor target position\r
100         __int32 ChainedPosition;      ///< Stepper motor position\r
101         DWORD TimeStamp;              ///< Last refresh time mark call of HudaqStepout\r
102         double Frequency;             ///< Current speed (frequency) of stepper motor\r
103         double Acc;                   ///< Acceleration of stepping motor in steps/s^2\r
104 } Stepper;\r
105 \r
106 \r
107 \r
108 /** Commands are mixed into ::MF625Register. */\r
109 typedef enum\r
110 {\r
111         MF625_START =                1,  ///< Start PWM counter\r
112         MF625_STOP =                 2,  ///< Stop PWM counter\r
113         MF625_RESET =                8,  ///< Reset PWM counter\r
114         MF625_SHEDULE_UPDATE =  0x2000,  ///< Shedule dual buffer update\r
115         MF625_CANCEL_SHEDULE = 0x10000,  ///< Discard any pending shedule update\r
116         MF625_FORCE_UPDATE =   0x20000,  ///< Update registers from dual buffer now, clear shedule flag like ::MF625_CANCEL_SHEDULE\r
117         MF625_LOAD =          0x100000,  ///< Reload counter with a divider value\r
118         MF625_FORCE_DOWN =    0x200000,  ///< Set counter direction down (counter switches automatically to down, when reaching max value).\r
119         MF625_FORCE_UP =      0x400000,  ///< Set counter direction up (counter switches automatically to up, when reaching zero value).\r
120         MF625_COMMANDS = MF625_START | MF625_STOP | MF625_RESET | MF625_LOAD |\r
121                 MF625_SHEDULE_UPDATE | MF625_CANCEL_SHEDULE | MF625_FORCE_UPDATE |\r
122                 MF625_FORCE_DOWN | MF625_FORCE_UP\r
123 } MF625Commands;\r
124 \r
125 \r
126 /** Counter control register for MF625. */\r
127 typedef struct\r
128 {\r
129         unsigned int Running:1; ///< R  0- counter is stopped; 1-counter is running\r
130         unsigned int Empty0:1;  ///< -\r
131         unsigned int GatePolarity:1;    ///< RW 0-'0' disables counting; 1-'1' disables counting\r
132         unsigned int InputWire:1;       ///< R  a state of input wire\r
133         unsigned int OutputControl:2; ///< RW 00-output; 01-inverted output; 10-0; 11-1\r
134         unsigned int ClockSource:4;   ///< RW 0-internal 50MHz; 1-10MHz; 2-1MHz; 3-100kHz; 4-NOTHING\r
135         ///<    5-Rise IN; 6-Fall IN; 7-Either IN; 8-NOTHING\r
136         ///<    9-Rise N-1; 10-Fall N-1; 11-Either N-1; 12-NOTHING\r
137         ///<    13-Rise N+1; 14-Fall N+1; 15-Either N+1\r
138         unsigned int Filter:1;        ///< RW 0-off; 1-on\r
139         unsigned int GateSource:2;    ///< RW 00-1; 01-IN; 10-Out_n-1; 11-Out_n+1\r
140         unsigned int UpdatePending:1;   ///< R  The update command waits to be realised. Update occurs when counter changes direction frou UP to down.\r
141         unsigned int OutputUDCtrl:2;    ///< RW register PWM output polarity control; 00-output; 01-inverted output; 10-0; 11-1\r
142         unsigned int PhaseOut:6;        ///< R  Readback of comparators.\r
143         unsigned int UpDown:1;  ///< R  A direction of main counter.\r
144         unsigned int Transparent:1;   ///< RW This handles dual buffering: 0-off; 1-on.\r
145         unsigned int Inversions:6;    ///< RW Turn on/off inversion of n'th pwm output subchannel.\r
146         unsigned int Emergency:2;     ///< RW Selection of emergency shutdown, see ::HudaqPwmEmergency.\r
147 } MF625Register;\r
148 \r
149 \r
150 /** Modes of 3 phase PWM counter. */\r
151 typedef enum\r
152 {\r
153         Unspecified_3f = 0,\r
154         OUT_0_ALL,                    ///< counter is generating 0 permanently on all wires\r
155         OUT_1_ALL,                    ///< counter is generating 1 permanently on all wires\r
156         Phase3PWM,                      ///< 3 phases + their inversions\r
157         Phase3PWM_prep,         ///<   intermediate internal state between something and Phase3PWM\r
158         Phase6PWM,                      ///< 6 phases\r
159         Phase6PWM_prep,         ///<   intermediate internal state between something and Phase6PWM\r
160 } MF625Mode;\r
161 \r
162 \r
163 /** Record related to multiphase PWM. */\r
164 typedef struct\r
165 {\r
166         __int32 Comparators[PWM_625_SUBCHANNELS]; ///< Cached values for comparators\r
167         __int32 Divider;                ///< Cached value of a divider.\r
168         MF625Register CTR;              ///< Counter control register cache - see ::MF625Register.\r
169         double DeadBand;                ///< Delay between inverted and not inverted phases. Used when InvertedPhase=1.\r
170         double CtrFixPWM;             ///< Fix PWM when switching from low to high frequency, 0 turns off\r
171         MF625Mode Mode;         ///< 0 - after initialization; 1 phases + inversion; 2 - 6 phases\r
172 } PWMMultiple;\r
173 \r
174 \r
175 /** One record related to counter. */\r
176 typedef struct\r
177 {\r
178         CounterControl CCache;        ///< Counter state register cache\r
179         __int32 ACache;               ///< Counter A register cache\r
180         __int32 BCache;               ///< Counter B register cache\r
181         InternalCounterMode Mode;       ///< Flag of subsystem that uses counter\r
182         double CtrFixPWM;             ///< Fix PWM when switching from low to high frequency, 0 turns off\r
183         int CtrResetCounter:1;        ///< Reset counter after every read\r
184 } Counter;\r
185 \r
186 \r
187 /** One record related to Digital to Analog output channel. */\r
188 typedef struct\r
189 {\r
190         __int16 Cache;                ///< Cached raw value for digital output\r
191         int Options;                  ///< DOut bit options; bit 0=0 volts, 1-raw\r
192 } DaSetup;\r
193 \r
194 \r
195 #define IrcResetCounter 2       ///< Binary flag for IrcOptions\r
196 \r
197 /** Ranges for both Analog Inputs and Analog Outputs - only one item for MF624. */\r
198 static const HudaqRange MF624RangeAIO = {-10,+10};\r
199 \r
200 \r
201 /** Cache of MF624 state, this cache is shared across all applications inside shared memory. */\r
202 typedef struct\r
203 {\r
204         UserDataHeader Hdr;                   ///< General device setup\r
205 \r
206         /* Digital inputs/outputs */\r
207         __int16 DoutCache;                    ///< digital outputs to be cached\r
208 \r
209         /* Analog inputs/outputs */\r
210 \r
211         unsigned AIOptions[AD_CHANNELS];        ///< Options for analog inputs\r
212         __int16 ADCtrlCache;                  ///< Selected channels for conversion\r
213 \r
214         DaSetup DA[DA_CHANNELS];                ///< DA values to be cached\r
215 \r
216         /* Counters and encoders */\r
217 \r
218         __int32 EncCache;                     ///< Encoder (IRC counter) state register\r
219         int EncOptions[ENC_CHANNELS];           ///< Bit options for encoders\r
220 \r
221         Counter counter[CTR_CHANNELS];  ///< Array that holds state of all counters\r
222 \r
223         union {\r
224                 Stepper step[STEP_CHANNELS];    ///< Stepper motor table\r
225                 PWMMultiple pwm;                ///< MF625 specialized PWM counter\r
226         };\r
227 } MF624_Private;\r
228 \r
229 #define MAXIMAL_STEP_INCREMENT 500      ///< Maximal speed increase between two calls of StepWrite.\r
230 \r
231 \r
232 /** Convert values from A/D convertor to a voltage. */\r
233 static __inline double MF624_AD2VOLT(signed __int16 x)\r
234 {\r
235         return 10.0*((signed __int16)(x<<2))/(double)0x8000;\r
236 }\r
237 \r
238 \r
239 /** Convert voltage to a raw values for D/A convertor. */\r
240 static __inline __int16 MF624_VOLT2DA(double y)\r
241 {\r
242         y = 0x4000*(y+10)/20;\r
243         if (y>=0x3FFF) return(0x3FFF);\r
244         if (y<=0) return(0x0000);\r
245         return( (__int16)(y) );\r
246 }\r
247 \r
248 \r
249 #define MASTERFREQUENCY 50000000        ///< Internal oscillator frequency is 50MHz\r
250 \r
251 \r
252 \r
253 /** Symbolic aliases for available registers inside BADR0. */\r
254 typedef enum\r
255 {\r
256         INTCSR  = 0x4C,\r
257         GPIOC   = 0x54\r
258 } BADR0;\r
259 \r
260 #define GPIOC_BASE  0x024006C0\r
261 #define GPIOC_EOLC  0x00020000\r
262 #define GPIOC_LDAC  0x00800000\r
263 #define GPIOC_DACEN 0x04000000\r
264 \r
265 /** Symbolic aliases for available registers inside BADR1 read. */\r
266 typedef enum\r
267 {\r
268         ADDATA  = 0x00,\r
269         DIN     = 0x10,\r
270         ADSTART = 0x20,\r
271 } BADR1IN;\r
272 \r
273 /** Symbolic aliases for available registers inside BADR1 write. */\r
274 typedef enum\r
275 {\r
276         ADCTRL = 0x0,\r
277         DOUT   = 0x10,\r
278         DADATA = 0x20,\r
279 } BADR1OUT;\r
280 \r
281 /** Symbolic aliases for available registers inside BADR2. */\r
282 typedef enum\r
283 {\r
284         CTRXCTRL = 0x60,\r
285         IRCCTRL  = 0x6C\r
286 } BADR2OUT;\r
287 \r
288 typedef enum\r
289 {\r
290         IRCSTATUS = 0x6C,             ///< Status of encoders, it contains\r
291         IRC0      = 0x70,             ///< Read a value from encoder 0\r
292         IRC1      = 0x74,             ///< Read a value from encoder 1\r
293         IRC2      = 0x78,             ///< Read a value from encoder 2\r
294         IRC3      = 0x7C              ///< Read a value from encoder 3\r
295 } BADR2IN;\r
296 \r
297 \r
298 #define LOOP_AD_TIMEOUT 0xFFFF  ///< timeout per loop for endless AD conversion\r
299 \r
300 \r
301 \r
302 /** write to memory mapped device word wise. */\r
303 static __inline void StoreWord(size_t Ptr, int Offset, __int16 value)\r
304 {\r
305         ((volatile unsigned __int16 *)Ptr)[Offset/2] = value;\r
306 }\r
307 \r
308 \r
309 /** write to memory mapped device word wise through cache. */\r
310 static __inline void StoreCachedWord(size_t Ptr, int Offset, __int16 value, __int16 *CachedValue)\r
311 {\r
312         if (*CachedValue != value)\r
313         {\r
314                 *CachedValue = value;\r
315                 StoreWord(Ptr,Offset,value);\r
316         }\r
317 }\r
318 \r
319 \r
320 /** read from memory mapped device word wise. */\r
321 static __inline __int16 GetWord(size_t Ptr, int Offset)\r
322 {\r
323         return ((volatile unsigned __int16 *)Ptr)[Offset/2];\r
324 }\r
325 \r
326 \r
327 /** write to memory mapped device doubleword wise. */\r
328 static __inline void StoreDword(size_t Ptr, int Offset, __int32 value)\r
329 {\r
330         ((volatile unsigned __int32 *)Ptr)[Offset/4] = value;\r
331 }\r
332 \r
333 \r
334 /** write to memory mapped device word wise through cache. */\r
335 static __inline void StoreCachedDword(size_t Ptr, int Offset, __int32 value, __int32 *CachedValue)\r
336 {\r
337         if (*CachedValue != value)\r
338         {\r
339                 *CachedValue = value;\r
340                 StoreDword(Ptr,Offset,value);\r
341         }\r
342 }\r
343 \r
344 \r
345 /** read from memory mapped device double word wise. */\r
346 static __inline __int32 GetDword(size_t Ptr, int Offset)\r
347 {\r
348         return ((volatile unsigned __int32 *)Ptr)[Offset/4];\r
349 }\r
350 \r
351 \r
352 /****************************************************************\r
353  *                                                              *\r
354  *                 DIGITAL INPUTS & OUTPUTS                     *\r
355  *                                                              *\r
356  ****************************************************************/\r
357 \r
358 \r
359 /** Get data from digital input. */\r
360 static int MF624DIRead(const DeviceRecord *DevRecord, unsigned channel)\r
361 {\r
362         if (channel!=0) return 0;\r
363         return GetWord(DevRecord->DrvRes.Resources.MemResources[1].Base,DIN);\r
364 }\r
365 \r
366 \r
367 /** Write data to digital output. */\r
368 static HUDAQSTATUS MF624DOWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned value)\r
369 {\r
370         if (channel!=0) return HUDAQBADARG;\r
371         StoreCachedWord(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, value,\r
372                         & ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );\r
373         return HUDAQSUCCESS;\r
374 }\r
375 \r
376 \r
377 /** Write one bit to digital output. */\r
378 static void MF624DOWriteBit(const DeviceRecord *DevRecord, unsigned channel, unsigned bit, int value)\r
379 {\r
380         __int16 DoutWord;\r
381         if (channel!=0) return;\r
382         if (bit>=8) return;   //there are only 8 bits available in MF624\r
383 \r
384         DoutWord = ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache;\r
385         if (value) DoutWord |= (1<<bit);\r
386         else DoutWord &= ~(1<<bit);\r
387 \r
388         StoreCachedWord(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, DoutWord,\r
389                         & ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );\r
390 }\r
391 \r
392 \r
393 static void MF624DOWriteMultipleBits(const DeviceRecord *DevRecord, unsigned channel, unsigned mask, unsigned value)\r
394 {\r
395         __int16 DoutWord;\r
396         if (channel!=0) return;\r
397 \r
398         DoutWord = ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache;\r
399         DoutWord = (DoutWord & ~mask) | (value & mask);\r
400 \r
401         StoreCachedWord(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, DoutWord,\r
402                         & ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );\r
403 }\r
404 \r
405 \r
406 static double MF624DIOGetParameter(unsigned channel, HudaqParameter param)\r
407 {\r
408         switch(param)\r
409         {\r
410                 case HudaqDINUMBITS:\r
411                         return (channel==0) ? 8 : WRONG_VALUE;\r
412                 case HudaqDONUMBITS:\r
413                         return (channel==0) ? 8 : WRONG_VALUE;\r
414                 case HudaqDINUMCHANNELS:\r
415                         return 1;\r
416                 case HudaqDONUMCHANNELS:\r
417                         return 1;\r
418         }\r
419 \r
420         return WRONG_VALUE;\r
421 }\r
422 \r
423 \r
424 \r
425 /****************************************************************\r
426  *                                                              *\r
427  *                  ANALOG INPUTS & OUTPUTS                     *\r
428  *                                                              *\r
429  ****************************************************************/\r
430 \r
431 \r
432 static HUDAQSTATUS MF624AISetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
433 {\r
434         MF624_Private *Cache;\r
435 \r
436         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
437         if (channel>=AD_CHANNELS) return HUDAQBADARG;\r
438 \r
439         switch(param)\r
440         {\r
441                 case HudaqAIUNITS:\r
442                         if (value!=0)\r
443                                 Cache->AIOptions[channel] |= 1;\r
444                         else\r
445                                 Cache->AIOptions[channel] &= ~1;\r
446                         return HUDAQSUCCESS;\r
447 \r
448                 case HudaqAIRange:\r
449                         if ((int)value == 0) return HUDAQSUCCESS;   // only one range "0" -10:10V is supported\r
450                         return HUDAQBADARG;\r
451         }\r
452 \r
453         return HUDAQNOTSUPPORTED;\r
454 }\r
455 \r
456 \r
457 static double MF624AIGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
458 {\r
459         MF624_Private *Cache;\r
460 \r
461         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
462         if (channel>=AD_CHANNELS) return WRONG_VALUE;\r
463 \r
464         switch(param)\r
465         {\r
466                 case HudaqAIUNITS:\r
467                         return(Cache->AIOptions[channel] & 1);\r
468                 case HudaqAIRange:\r
469                         return 0;\r
470                 case HudaqAINUMCHANNELS:\r
471                         return AD_CHANNELS;\r
472         }\r
473 \r
474         return WRONG_VALUE;\r
475 }\r
476 \r
477 \r
478 /** Get data from analog input. */\r
479 static double MF624AIRead(const DeviceRecord *DevRecord, unsigned channel)\r
480 {\r
481         size_t Ptr0;\r
482         size_t Ptr1;\r
483         unsigned TimeoutCounter;\r
484         MF624_Private *Cache;\r
485 \r
486         if (channel>=AD_CHANNELS) return UNDEFINED_VALUE;\r
487 \r
488         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
489         Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
490         StoreCachedWord(Ptr1,ADCTRL,1<<channel,&Cache->ADCtrlCache);\r
491         GetWord(Ptr1,ADSTART);        /* Start A/D conversion - dummy read */\r
492         Ptr0 = DevRecord->DrvRes.Resources.MemResources[0].Base;\r
493 \r
494         TimeoutCounter=0;\r
495         while(GetDword(Ptr0,GPIOC) & GPIOC_EOLC)\r
496         {\r
497                 if (TimeoutCounter++>LOOP_AD_TIMEOUT)\r
498                         return UNDEFINED_VALUE;           /*timeout*/\r
499         }\r
500 \r
501         if ((Cache->AIOptions[channel]&1)==1)\r
502                 return GetWord(Ptr1,ADDATA);\r
503         else\r
504                 return MF624_AD2VOLT(GetWord(Ptr1,ADDATA));\r
505 }\r
506 \r
507 \r
508 /** Read multiple data from analog input. */\r
509 static HUDAQSTATUS MF624AIReadMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned *channels, double *values)\r
510 {\r
511         size_t Ptr0;\r
512         size_t Ptr1;\r
513         unsigned i;\r
514         unsigned __int16 Mask;\r
515         unsigned __int16 RawValues[8];\r
516         unsigned __int32 ReadCache;\r
517         int RetVal = HUDAQSUCCESS;\r
518 \r
519         if (number<=0 || channels==NULL || values==NULL) return HUDAQBADARG;\r
520 \r
521         Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
522         Ptr0 = DevRecord->DrvRes.Resources.MemResources[0].Base;\r
523 \r
524         Mask=0;       /* Prepare list of channels to convert. */\r
525         for(i=0;i<number;i++)\r
526         {\r
527                 if (channels[i]<=7)\r
528                         Mask |= 1<<channels[i];\r
529         }\r
530 \r
531         StoreCachedWord(Ptr1,ADCTRL,Mask,&(((MF624_Private *)(DevRecord->DrvRes.DriverData))->ADCtrlCache));\r
532         i = GetWord(Ptr1,ADSTART);            /* Start A/D conversion */\r
533         i=0;\r
534         while(GetDword(Ptr0,GPIOC) & GPIOC_EOLC)\r
535         {\r
536                 if (i++>LOOP_AD_TIMEOUT)\r
537                         return HUDAQFAILURE; /*timeout*/\r
538         }\r
539 \r
540         i=0;\r
541         while(i<=7)     /* Sort A/D values converted. */\r
542         {\r
543                 if ( (Mask & (1<<i)) != 0)\r
544                 {\r
545                         Mask &= ~(1<<i);                     /* Cleanup mask flag. */\r
546                         if (Mask==0)                          /* Do we have only one last read? */\r
547                                 RawValues[i] = GetWord(Ptr1,ADDATA);\r
548                         else\r
549                         {\r
550                                 ReadCache = GetDword(Ptr1,ADDATA); /* Extract two values together from A/D FIFO */\r
551                                 RawValues[i] = ReadCache;          /* feed the first value */\r
552 \r
553                                 while(i<=6)                        /* Look for second counterpart - compare to 6 because i is incremented below. */\r
554                                 {\r
555                                         i++;\r
556                                         if ((Mask & (1<<i)) != 0)        /* Second counterpart has been found. */\r
557                                         {\r
558                                                 Mask &= ~(1<<i);\r
559                                                 RawValues[i] = ReadCache>>16;  /* feed second value */\r
560                                                 break;\r
561                                         }\r
562                                 }\r
563                         }\r
564                 }\r
565                 i++;\r
566         }\r
567 \r
568         /* Store conversion results into output structure. */\r
569         for(i=0;i<number;i++)\r
570         {\r
571                 if (channels[i]<=7)\r
572                 {                       /* When you switch different channel mode, be consistent with AIRead. */\r
573                         if ((((MF624_Private *)(DevRecord->DrvRes.DriverData))->AIOptions[channels[i]]&1)==1)\r
574                                 values[i] = RawValues[channels[i]];\r
575                         else\r
576                                 values[i] = MF624_AD2VOLT(RawValues[channels[i]]);\r
577                 }\r
578                 else\r
579                 {\r
580                         values[i] = UNDEFINED_VALUE;\r
581                         RetVal = HUDAQPARTIAL;\r
582                 }\r
583 \r
584         }\r
585         return RetVal;\r
586 }\r
587 \r
588 \r
589 static double MF624AOGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
590 {\r
591         MF624_Private *Cache;\r
592 \r
593         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
594         if (channel>=DA_CHANNELS) return WRONG_VALUE;\r
595 \r
596         switch(param)\r
597         {\r
598                 case HudaqAOUNITS:\r
599                         return(Cache->DA[channel].Options & 1);\r
600                         return HUDAQSUCCESS;\r
601                 case HudaqAORange:\r
602                         return 0;\r
603                 case HudaqAONUMCHANNELS:\r
604                         return DA_CHANNELS;\r
605         }\r
606 \r
607         return WRONG_VALUE;\r
608 }\r
609 \r
610 \r
611 static HUDAQSTATUS MF624AOSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
612 {\r
613         MF624_Private *Cache;\r
614 \r
615         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
616         if (channel>=DA_CHANNELS) return HUDAQBADARG;\r
617 \r
618         switch(param)\r
619         {\r
620                 case HudaqAOUNITS:\r
621                         if (value!=0)\r
622                                 Cache->DA[channel].Options |= 1;\r
623                         else\r
624                                 Cache->DA[channel].Options &= ~1;\r
625                         return HUDAQSUCCESS;\r
626 \r
627                 case HudaqAORange:\r
628                         if ((int)value == 0) return HUDAQSUCCESS;   // only one range "0" -10:10V is supported\r
629                         return HUDAQBADARG;\r
630         }\r
631         return HUDAQNOTSUPPORTED;\r
632 }\r
633 \r
634 \r
635 /** Write data to analog output. */\r
636 static void MF624AOWrite(const DeviceRecord *DevRecord, unsigned channel, double value)\r
637 {\r
638         DaSetup *DAx;\r
639         if (channel>=DA_CHANNELS) return;\r
640 \r
641         DAx = &((MF624_Private *)(DevRecord->DrvRes.DriverData))->DA[channel];\r
642         StoreCachedWord(DevRecord->DrvRes.Resources.MemResources[1].Base,\r
643                         DADATA + 2*channel,\r
644                         ((DAx->Options & 1) == 1) ? (__int16)value : MF624_VOLT2DA(value),\r
645                         &DAx->Cache );\r
646 }\r
647 \r
648 \r
649 /** Write multiple data to analog output synchronized. */\r
650 static HUDAQSTATUS MF624AOWriteMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned* channels, const double* values)\r
651 {\r
652         size_t Ptr0;\r
653         size_t Ptr1;\r
654         MF624_Private *Cache;\r
655         int RetVal = HUDAQSUCCESS;\r
656         unsigned __int16 Val2DAC;\r
657         unsigned char DirtyMask=0;\r
658 \r
659         if (number<=0 || channels==NULL || values==NULL) return HUDAQBADARG;\r
660 \r
661         Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
662         Ptr0 = DevRecord->DrvRes.Resources.MemResources[0].Base;\r
663         Cache = (MF624_Private *)(DevRecord->DrvRes.DriverData);\r
664 \r
665         /* Decide which channel(s) to write. */\r
666         while(number-->0)\r
667         {\r
668                 if (*channels>=DA_CHANNELS)                     /* Channel is out of range. */\r
669                         RetVal = HUDAQPARTIAL;\r
670                 else\r
671                 {\r
672                         if ((Cache->DA[*channels].Options & 1) == 1) /* Use raw value? */\r
673                                 Val2DAC = (unsigned __int16)(*values);\r
674                         else\r
675                                 Val2DAC = (unsigned __int16)MF624_VOLT2DA(*values);\r
676 \r
677                         if (Val2DAC != Cache->DA[*channels].Cache)\r
678                         {\r
679                                 DirtyMask |= 1<<*channels;\r
680                                 Cache->DA[*channels].Cache = Val2DAC;\r
681                         }\r
682                 }\r
683                 values++;\r
684                 channels++;\r
685         }\r
686 \r
687         /* Write all dirty channels to DAC. */\r
688         if (DirtyMask!=0)\r
689         {\r
690                 StoreDword(Ptr0,GPIOC,GPIOC_BASE|GPIOC_DACEN|GPIOC_LDAC);/*enable DACen + Hold LDAC*/\r
691 \r
692                 for(number=0;number<DA_CHANNELS;number+=2)\r
693                 {\r
694                         if ((DirtyMask & (3<<number)) == (3<<number))\r
695                         {\r
696                                 DirtyMask &= ~(3<<number);\r
697                                 StoreDword(Ptr1, DADATA + number*2, Cache->DA[number].Cache + 0x10000*Cache->DA[number+1].Cache);\r
698                         }\r
699                 }\r
700 \r
701                 for(number=0;number<DA_CHANNELS;number++)\r
702                 {\r
703                         if (DirtyMask & (1<<number))\r
704                         {\r
705                                 DirtyMask &= ~(1<<number);\r
706                                 StoreWord(Ptr1, DADATA + number*2, Cache->DA[number].Cache);\r
707                         }\r
708                 }\r
709 \r
710                 StoreDword(Ptr0,GPIOC,GPIOC_BASE|GPIOC_DACEN);        /*enable DACen*/\r
711         }\r
712         return RetVal;\r
713 }\r
714 \r
715 \r
716 /****************************************************************\r
717  *                                                              *\r
718  *                          ENCODERS                            *\r
719  *                                                              *\r
720  ****************************************************************/\r
721 \r
722 \r
723 static double MF624EncGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
724 {\r
725         MF624_Private *Cache;\r
726         IRC_Control *pCTRi;\r
727         __int8 IrcValue[ENC_CHANNELS];\r
728 \r
729         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
730         if (channel>=ENC_CHANNELS) return WRONG_VALUE;\r
731 \r
732         switch(param)\r
733         {\r
734                 case HudaqEncRESETONREAD:\r
735                         return(Cache->EncOptions[channel] & IrcResetCounter);\r
736                 case HudaqEncFILTER:\r
737                         return(Cache->EncCache & (0x80<<(8*channel)));\r
738                 case HudaqEncMODE:\r
739                         pCTRi = (IRC_Control *)&(((char *)&(Cache->EncCache))[channel]);\r
740                         return(pCTRi->IRC_MODE);\r
741                 case HudaqEncCOUNTCONTROL:\r
742                         pCTRi = (IRC_Control *)&(((char *)&(Cache->EncCache))[channel]);\r
743                         return(pCTRi->BLOCK_MODE);\r
744                 case HudaqEncRESETMODE:\r
745                         pCTRi = (IRC_Control *)&(((char *)&(Cache->EncCache))[channel]);\r
746                         return(pCTRi->RESET_MODE);\r
747                 case HudaqEncI:\r
748                         *(__int32 *)IrcValue = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, IRCSTATUS);\r
749                         return(IrcValue[channel]);\r
750                 case HudaqEncNUMCHANNELS:\r
751                         return ENC_CHANNELS;\r
752         }\r
753 \r
754         return WRONG_VALUE;\r
755 }\r
756 \r
757 \r
758 static HUDAQSTATUS MF624EncSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
759 {\r
760         size_t Ptr2;\r
761         MF624_Private *Cache;\r
762         __int8 IrcValue[ENC_CHANNELS];\r
763         IRC_Control *pCTRi;\r
764 \r
765         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
766         if (channel>=ENC_CHANNELS) return HUDAQBADARG;\r
767         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
768 \r
769         switch(param)\r
770         {\r
771                 case HudaqEncRESETONREAD:\r
772                         if (value!=0)\r
773                                 Cache->EncOptions[channel] |= IrcResetCounter;\r
774                         else\r
775                                 Cache->EncOptions[channel] &= ~IrcResetCounter;\r
776                         return HUDAQSUCCESS;\r
777 \r
778                 case HudaqEncFILTER:    // 0 is false, any nonzero value expands to true.\r
779                         if (value!=0)\r
780                                 StoreCachedDword(Ptr2, IRCCTRL, Cache->EncCache | (0x80<<(8*channel)), &Cache->EncCache);\r
781                         else\r
782                                 StoreCachedDword(Ptr2, IRCCTRL, Cache->EncCache & ~(0x80<<(8*channel)), &Cache->EncCache);\r
783                         return HUDAQSUCCESS;\r
784 \r
785                 case HudaqEncMODE:\r
786                         if (value<0 || value>=4) return HUDAQBADARG;\r
787                         *(unsigned __int32 *)&IrcValue = Cache->EncCache;\r
788                         pCTRi = (IRC_Control *)&IrcValue[channel];\r
789 \r
790                         pCTRi->IRC_MODE = (int)value;\r
791                         StoreCachedDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue, &Cache->EncCache);\r
792                         return HUDAQSUCCESS;\r
793 \r
794                 case HudaqEncRESETMODE:\r
795                         if (value<0 || value>=7) return HUDAQBADARG;\r
796                         *(unsigned __int32 *)&IrcValue = Cache->EncCache;\r
797                         pCTRi = (IRC_Control *)&IrcValue[channel];\r
798 \r
799                         pCTRi->RESET_MODE = (int)value;\r
800                         StoreCachedDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue, &Cache->EncCache);\r
801                         return HUDAQSUCCESS;\r
802 \r
803                 case HudaqEncCOUNTCONTROL:\r
804                         if (value<0 || value>=4) return HUDAQBADARG;\r
805                         *(unsigned __int32 *)&IrcValue = Cache->EncCache;\r
806                         pCTRi = (IRC_Control *)&IrcValue[channel];\r
807 \r
808                         pCTRi->BLOCK_MODE = (int)value;\r
809                         StoreCachedDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue, &Cache->EncCache);\r
810                         return HUDAQSUCCESS;\r
811         }\r
812 \r
813         return HUDAQNOTSUPPORTED;\r
814 }\r
815 \r
816 \r
817 static HUDAQSTATUS MF624EncReset(const DeviceRecord *DevRecord, unsigned channel)\r
818 {\r
819         size_t Ptr2;\r
820         MF624_Private *Cache;\r
821         __int8 IrcValue[ENC_CHANNELS];\r
822         IRC_Control *pCTRi;\r
823         char OldResetMode;\r
824 \r
825         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
826         if (channel>=ENC_CHANNELS) return HUDAQBADARG;\r
827         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
828 \r
829         *(unsigned __int32*)&IrcValue = Cache->EncCache;\r
830         pCTRi = (IRC_Control *)&IrcValue[channel];\r
831 \r
832         OldResetMode = pCTRi->RESET_MODE; //store original reset mode\r
833         pCTRi->RESET_MODE = 1;                               //reset encoder\r
834         StoreDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue);\r
835         pCTRi->RESET_MODE = OldResetMode;                    //enable counting\r
836         StoreDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue);\r
837         return HUDAQSUCCESS;\r
838 }\r
839 \r
840 \r
841 static int MF624EncRead(const DeviceRecord *DevRecord, unsigned channel)\r
842 {\r
843         IRC_Control *pCTRi;\r
844         size_t Ptr2;\r
845         __int8 IrcValue[ENC_CHANNELS];\r
846         __int32 RetVal;\r
847         MF624_Private *Cache;\r
848         char OldResetMode;\r
849 \r
850         if (channel>=ENC_CHANNELS) return 0;\r
851         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
852         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
853 \r
854         RetVal=GetDword(Ptr2, IRC0 + 4*channel);      // read a value from encoder\r
855 \r
856         if (Cache->EncOptions[channel] & IrcResetCounter) //RESET counter on every read\r
857         {\r
858                 *(unsigned __int32*)&IrcValue = Cache->EncCache;\r
859                 pCTRi = (IRC_Control *)&IrcValue[channel];\r
860 \r
861                 OldResetMode = pCTRi->RESET_MODE; //store original reset mode\r
862                 pCTRi->RESET_MODE = 1;                              //reset encoder\r
863                 StoreDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue);\r
864                 pCTRi->RESET_MODE = OldResetMode;                   //enable counting\r
865                 StoreDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue);\r
866         }\r
867 \r
868         return RetVal;\r
869 }\r
870 \r
871 \r
872 /****************************************************************\r
873  *                                                              *\r
874  *                COUNTERS PWM + Count + Step                   *\r
875  *                                                              *\r
876  ****************************************************************/\r
877 \r
878 static HUDAQSTATUS MF624PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)\r
879 {\r
880         double T1,T2;\r
881         double T;\r
882         Counter *CacheCtrItem;\r
883         size_t Ptr2;\r
884         CounterControl CTC;\r
885 \r
886         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
887 \r
888         CacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
889         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
890 \r
891         if (dutycycle>=1)\r
892         {\r
893                 if (CacheCtrItem->Mode != OUT_1)\r
894                 {\r
895                         CacheCtrItem->Mode = OUT_1;\r
896                         *(unsigned *)&CTC=0;\r
897                         CTC.OutputControl = 3;\r
898                         StoreCachedDword(Ptr2, 0x10*channel,*(unsigned __int32 *)&CTC, (__int32 *)&CacheCtrItem->CCache);\r
899                 }\r
900                 return HUDAQSUCCESS;\r
901         }\r
902         if (dutycycle<=0)\r
903         {\r
904                 if (CacheCtrItem->Mode != OUT_0)\r
905                 {\r
906                         CacheCtrItem->Mode = OUT_0;\r
907                         *(unsigned *)&CTC=0;\r
908                         CTC.OutputControl = 2;\r
909                         StoreCachedDword(Ptr2, 0x10*channel,*(unsigned __int32 *)&CTC, (__int32 *)&CacheCtrItem->CCache);\r
910                 }\r
911                 return HUDAQSUCCESS;\r
912         }\r
913 \r
914         if (frequency<=0) return HUDAQBADARG; //Allow duty cycle 0 and 1 for f=0.\r
915 \r
916         /* Prescaller is not set automatically, but when user sets it, calculate with a value given. */\r
917         if (CacheCtrItem->Mode != PWM) CTC.ClockSource = HudaqCtrCLOCK50MHz;\r
918         switch (CTC.ClockSource)\r
919         {\r
920                 case HudaqCtrCLOCK100kHz:T = MASTERFREQUENCY/frequency/500.0;break;\r
921                 case HudaqCtrCLOCK1MHz:  T = MASTERFREQUENCY/frequency/50.0; break;\r
922                 case HudaqCtrCLOCK10MHz: T = MASTERFREQUENCY/frequency/5.0;  break;\r
923                 case HudaqCtrCLOCK50MHz:\r
924                 default:                     T = MASTERFREQUENCY/frequency;      break;\r
925         }\r
926 \r
927         if (dutycycle<0.5)\r
928         {\r
929                 T1 = max(1.0, min(T*dutycycle, (double)0xFFFFFFFF+1.0));\r
930                 T2 = max(1.0, min(T-T1, (double)0xFFFFFFFF+1.0));\r
931         }\r
932         else\r
933         {\r
934                 T2 = max(1.0, min(T*(1-dutycycle), (double)0xFFFFFFFF+1.0));\r
935                 T1 = max(1.0, min(T-T2, (double)0xFFFFFFFF+1.0));\r
936         }\r
937         //sorry, but ((int)(T1+.5))-1 do not work for all values\r
938         StoreCachedDword(Ptr2, 0x10*channel+4, (unsigned __int32)(T1-0.5), &CacheCtrItem->ACache);\r
939         StoreCachedDword(Ptr2, 0x10*channel+8, (unsigned __int32)(T2-0.5), &CacheCtrItem->BCache);\r
940 \r
941         if (CacheCtrItem->Mode != PWM)\r
942         {\r
943                 CacheCtrItem->Mode = PWM;\r
944 \r
945                 // STOP counter\r
946                 StoreDword(Ptr2, CTRXCTRL,\r
947                                 (GLOB_CTR_STOP | GLOB_CTR_LOAD | GLOB_CTR_RESET | GLOB_CTR_OUT_RESET)<<(6*channel) );\r
948 \r
949                 *(unsigned *)&CTC=0;\r
950                 //CTC.ClockSource=0;    //50MHz is default.\r
951                 CTC.Repetition=1;\r
952                 CTC.LoadToggle=1;\r
953                 CTC.OutputToggle=1;\r
954 \r
955                 // SET counter CWR\r
956                 StoreCachedDword(Ptr2, 0x10*channel,*(unsigned __int32 *)&CTC, (__int32 *)&CacheCtrItem->CCache);\r
957 \r
958                 // START couner\r
959                 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_START)<<(6*channel));\r
960         }\r
961 \r
962         if (CacheCtrItem->CtrFixPWM!=0)  //Fix relatively big value change\r
963         {\r
964                 T1 = (unsigned __int32)GetDword(Ptr2, 0x10*channel+4);\r
965                 if (T1 > T+CacheCtrItem->CtrFixPWM)\r
966                 {\r
967                         StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_LOAD)<<(6*channel));\r
968                 }\r
969         }\r
970 \r
971         return HUDAQSUCCESS;\r
972 }\r
973 \r
974 \r
975 static HUDAQSTATUS MF624CtrReset(const DeviceRecord *DevRecord, unsigned channel)\r
976 {\r
977         size_t Ptr2;\r
978         Counter *CacheCtrItem;\r
979         CounterControl CTC;\r
980 \r
981         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
982 \r
983         CacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
984         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
985 \r
986         //if ((options & IrcNoSwitchMode)==0)\r
987         {\r
988                 if (CacheCtrItem->Mode != Counting)\r
989                 {\r
990                         CacheCtrItem->Mode = Counting;\r
991 \r
992                         // When counting down, 0 causes CE, thus load 0xFFFFFFFF\r
993                         //StoreCachedDword(Ptr2, 0x10*channel+4, 0xFFFFFFFF, &Cache->counter[channel].ACache);\r
994                         // When counting up, 0xFFFFFFFF causes CE, thus load 0\r
995                         StoreCachedDword(Ptr2, 0x10*channel+4, 0, &CacheCtrItem->ACache);\r
996                         // Initial CTR mode setup.\r
997                         *(unsigned *)&CTC=0;\r
998                         CTC.ClockSource=5;        //Rise IN\r
999                         CTC.Repetition=1;\r
1000                         CTC.LoadToggle=0;\r
1001                         CTC.Direction=1;          //Up\r
1002                         // SET counter CWR\r
1003                         StoreCachedDword(Ptr2, 0x10*channel,*(__int32 *)&CTC, (__int32 *)&CacheCtrItem->CCache);\r
1004                 }\r
1005         }\r
1006 \r
1007         // RESET couner and START it\r
1008         StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_START|GLOB_CTR_RESET)<<(6*channel));\r
1009         return HUDAQSUCCESS;\r
1010 }\r
1011 \r
1012 \r
1013 static double MF624CtrGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1014 {\r
1015         Counter *pCacheCtrItem;\r
1016 \r
1017         if (channel>=CTR_CHANNELS) return WRONG_VALUE;\r
1018         pCacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
1019 \r
1020         switch(param)\r
1021         {\r
1022                 case HudaqCtrDIRECTION:\r
1023                         return(pCacheCtrItem->CCache.Direction);\r
1024                 case HudaqCtrRESETONREAD:\r
1025                         return(pCacheCtrItem->CtrResetCounter);\r
1026                 case HudaqCtrREPETITION:\r
1027                         return(pCacheCtrItem->CCache.Repetition);\r
1028                 case HudaqCtrLOADTOGGLE:\r
1029                         return(pCacheCtrItem->CCache.LoadToggle);\r
1030                 case HudaqCtrOUTTOGGLE:\r
1031                         return(pCacheCtrItem->CCache.OutputToggle);\r
1032                 case HudaqPwmCLOCKSOURCE:\r
1033                 case HudaqCtrCLOCKSOURCE:\r
1034                         return(pCacheCtrItem->CCache.ClockSource);\r
1035                 case HudaqPwmOUTPUTCONTROL:\r
1036                 case HudaqCtrOUTPUTCONTROL:\r
1037                         return(pCacheCtrItem->CCache.OutputControl);\r
1038                 case HudaqCtrTRIGSOURCE:\r
1039                         return(pCacheCtrItem->CCache.TriggerSource);\r
1040                 case HudaqCtrTRIGTYPE:\r
1041                         return(pCacheCtrItem->CCache.TriggerType);\r
1042                 case HudaqCtrRETRIGGER:\r
1043                         return(pCacheCtrItem->CCache.ReTrigger);\r
1044                 case HudaqPwmGATESOURCE:\r
1045                 case HudaqCtrGATESOURCE:\r
1046                         return(pCacheCtrItem->CCache.GateSource);\r
1047                 case HudaqPwmGATEPOLARITY:\r
1048                 case HudaqCtrGATEPOLARITY:\r
1049                         return(pCacheCtrItem->CCache.GatePolarity);\r
1050                 case HudaqPwmFILTER:\r
1051                 case HudaqCtrFILTER:\r
1052                         return(pCacheCtrItem->CCache.Filter);\r
1053 \r
1054                 case HudaqPwmNUMCHANNELS:\r
1055                 case HudaqCtrNUMCHANNELS:\r
1056                         return CTR_CHANNELS;\r
1057 \r
1058                 case HudaqPwmTHRESHOLD:\r
1059                         return(pCacheCtrItem->CtrFixPWM/MASTERFREQUENCY);\r
1060 \r
1061         }\r
1062 \r
1063         return WRONG_VALUE;\r
1064 }\r
1065 \r
1066 \r
1067 static HUDAQSTATUS MF624CtrSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1068 {\r
1069         Counter *pCacheCtrItem;\r
1070 \r
1071         pCacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
1072         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
1073 \r
1074         if (pCacheCtrItem->Mode != Counting)\r
1075                 MF624CtrReset(DevRecord,channel);     // Enforce switching into a counter mode\r
1076 \r
1077         switch(param)\r
1078         {\r
1079                 case HudaqCtrRESETONREAD:               //Feature is provided by SW layer only.\r
1080                         pCacheCtrItem->CtrResetCounter = ((int)value==0) ? 0 : 1;\r
1081                         return HUDAQSUCCESS;\r
1082                 case HudaqCtrREPETITION:\r
1083                         if (value>=2 || value<0) return HUDAQBADARG;\r
1084                         if (pCacheCtrItem->CCache.Repetition != (int)value)\r
1085                         {\r
1086                                 pCacheCtrItem->CCache.Repetition = (int)value;\r
1087                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1088                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1089                         }\r
1090                         return HUDAQSUCCESS;\r
1091                 case HudaqCtrLOADTOGGLE:\r
1092                         if (value>=2 || value<0) return HUDAQBADARG;\r
1093                         if (pCacheCtrItem->CCache.LoadToggle != (int)value)\r
1094                         {\r
1095                                 pCacheCtrItem->CCache.LoadToggle = (int)value;\r
1096                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1097                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1098                         }\r
1099                         return HUDAQSUCCESS;\r
1100                 case HudaqCtrOUTTOGGLE:\r
1101                         if (value>=2 || value<0) return HUDAQBADARG;\r
1102                         if (pCacheCtrItem->CCache.OutputToggle != (int)value)\r
1103                         {\r
1104                                 pCacheCtrItem->CCache.OutputToggle = (int)value;\r
1105                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1106                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1107                         }\r
1108                         return HUDAQSUCCESS;\r
1109                 case HudaqPwmCLOCKSOURCE:\r
1110                 case HudaqCtrCLOCKSOURCE:\r
1111                         if (value>=16 || value<0) return HUDAQBADARG;\r
1112                         if (pCacheCtrItem->CCache.ClockSource != (int)value)\r
1113                         {\r
1114                                 pCacheCtrItem->CCache.ClockSource = (int)value;\r
1115                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1116                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1117                         }\r
1118                         return HUDAQSUCCESS;\r
1119                 case HudaqPwmOUTPUTCONTROL:\r
1120                 case HudaqCtrOUTPUTCONTROL:\r
1121                         if (value>=4 || value<0) return HUDAQBADARG;\r
1122                         if (pCacheCtrItem->CCache.OutputControl != (int)value)\r
1123                         {\r
1124                                 pCacheCtrItem->CCache.OutputControl = (int)value;\r
1125                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1126                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1127                         }\r
1128                         return HUDAQSUCCESS;\r
1129                 case HudaqCtrDIRECTION:\r
1130                         if (value>=2 || value<0) return HUDAQBADARG;\r
1131                         if (pCacheCtrItem->CCache.Direction != (int)value)\r
1132                         {\r
1133                                 pCacheCtrItem->CCache.Direction = (int)value;\r
1134                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1135                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1136 \r
1137                                 StoreCachedDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x10*channel+4,\r
1138                                                 (pCacheCtrItem->CCache.Direction==1) ? 0 : 0xFFFFFFFF,\r
1139                                                 &pCacheCtrItem->ACache);\r
1140                         }\r
1141                         return HUDAQSUCCESS;\r
1142                 case HudaqCtrTRIGSOURCE:\r
1143                         if (value>=4 || value<0) return HUDAQBADARG;\r
1144                         if (pCacheCtrItem->CCache.TriggerSource != (int)value)\r
1145                         {\r
1146                                 pCacheCtrItem->CCache.TriggerSource = (int)value;\r
1147                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1148                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1149                         }\r
1150                         return HUDAQSUCCESS;\r
1151                 case HudaqCtrTRIGTYPE:\r
1152                         if (value>=4 || value<0) return HUDAQBADARG;\r
1153                         if (pCacheCtrItem->CCache.TriggerType != (int)value)\r
1154                         {\r
1155                                 pCacheCtrItem->CCache.TriggerType = (int)value;\r
1156                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1157                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1158                         }\r
1159                         return HUDAQSUCCESS;\r
1160                 case HudaqCtrRETRIGGER:\r
1161                         if (value>=2 || value<0) return HUDAQBADARG;\r
1162                         if (pCacheCtrItem->CCache.ReTrigger != (int)value)\r
1163                         {\r
1164                                 pCacheCtrItem->CCache.ReTrigger = (int)value;\r
1165                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1166                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1167                         }\r
1168                         return HUDAQSUCCESS;\r
1169                 case HudaqPwmGATESOURCE:\r
1170                 case HudaqCtrGATESOURCE:\r
1171                         if (value>=4 || value<0) return HUDAQBADARG;\r
1172                         if (pCacheCtrItem->CCache.GateSource != (int)value)\r
1173                         {\r
1174                                 pCacheCtrItem->CCache.GateSource = (int)value;\r
1175                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1176                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1177                         }\r
1178                         return HUDAQSUCCESS;\r
1179                 case HudaqPwmGATEPOLARITY:\r
1180                 case HudaqCtrGATEPOLARITY:\r
1181                         if (value>=2 || value<0) return HUDAQBADARG;\r
1182                         if (pCacheCtrItem->CCache.GatePolarity != (int)value)\r
1183                         {\r
1184                                 pCacheCtrItem->CCache.GatePolarity = (int)value;\r
1185                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1186                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1187                         }\r
1188                         return HUDAQSUCCESS;\r
1189                 case HudaqPwmFILTER:\r
1190                 case HudaqCtrFILTER:\r
1191                         if (value>=2 || value<0) return HUDAQBADARG;\r
1192                         if (pCacheCtrItem->CCache.Filter != (int)value)\r
1193                         {\r
1194                                 pCacheCtrItem->CCache.Filter = (int)value;\r
1195                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1196                                                 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);\r
1197                         }\r
1198                         return HUDAQSUCCESS;\r
1199 \r
1200                 case HudaqPwmTHRESHOLD:\r
1201                         pCacheCtrItem->CtrFixPWM = (value==0)? 0 : MASTERFREQUENCY/value;\r
1202                         return HUDAQSUCCESS;\r
1203         }\r
1204 \r
1205         return HUDAQNOTSUPPORTED;\r
1206 }\r
1207 \r
1208 \r
1209 static int MF624CtrRead(const DeviceRecord *DevRecord, unsigned channel)\r
1210 {\r
1211         size_t Ptr2;\r
1212         Counter *CacheCtrItem;\r
1213         __int32 RetVal;\r
1214 \r
1215         if (channel>=CTR_CHANNELS) return 0;\r
1216 \r
1217         CacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];\r
1218         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
1219 \r
1220         if (CacheCtrItem->Mode != Counting)\r
1221         {\r
1222                 MF624CtrReset(DevRecord,channel);\r
1223                 return 0;\r
1224         }\r
1225 \r
1226         RetVal = GetDword(Ptr2, 0x10*channel+4);\r
1227 \r
1228         if (CacheCtrItem->CtrResetCounter)        // Reset a counter after each read\r
1229         {\r
1230                 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_RESET)<<(6*channel));\r
1231         }\r
1232 \r
1233         return RetVal;\r
1234 }\r
1235 \r
1236 \r
1237 /****************************************************************************/\r
1238 /** Stepout procedure makes a defined amount of steps\r
1239  *****************************************************************************/\r
1240 static HUDAQSTATUS MF624StepWrite(const DeviceRecord *DevRecord, unsigned channel, int position)\r
1241 {\r
1242         int digin;\r
1243         int State=0;\r
1244         size_t Ptr2;\r
1245         long NewSteps;\r
1246         MF624_Private *Cache;\r
1247         CounterControl CTC;\r
1248         unsigned __int32 N;\r
1249         DWORD NewTime;\r
1250         double NewFrequency;\r
1251         Stepper *CacheStep;\r
1252 \r
1253         NewTime=timeGetTime();\r
1254 \r
1255         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
1256         if (channel>=STEP_CHANNELS) return HUDAQBADARG;\r
1257         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
1258         CacheStep = &(Cache->step[channel]);\r
1259 \r
1260         if (Cache->counter[2*channel].Mode != StepperPWM)\r
1261         {           //check for the first time initialization of slave counter\r
1262                 Cache->counter[2*channel].Mode = StepperPWM;\r
1263 \r
1264                 *(unsigned *)&CTC=0;\r
1265                 CTC.Repetition=1;\r
1266                 CTC.OutputToggle=1;\r
1267                 CTC.GateSource=3;           //OUT(n+1)\r
1268                 CTC.GatePolarity=0;\r
1269 \r
1270                 // SET counter CWR\r
1271                 StoreCachedDword(Ptr2, 0x10*(2*channel),*(__int32 *)&CTC, (__int32 *)&Cache->counter[2*channel].CCache);\r
1272 \r
1273                 //set a real frequency\r
1274                 N = (unsigned __int32)(MASTERFREQUENCY / (2*CacheStep->Fmin));\r
1275                 N = max(0,N-1);\r
1276 \r
1277                 StoreCachedDword(Ptr2, 0x10*(2*channel)+4, N, &Cache->counter[2*channel].ACache);\r
1278 \r
1279                 State|=1;\r
1280         }\r
1281         if (Cache->counter[2*channel+1].Mode != StepperMaster)\r
1282         {      //check for the first time initialization of master counter\r
1283                 Cache->counter[2*channel+1].Mode = StepperMaster;\r
1284 \r
1285                 *(unsigned *)&CTC=0;\r
1286                 CTC.Repetition=0;\r
1287                 CTC.OutputToggle=0;\r
1288                 CTC.ClockSource=10;         //Fall N-1\r
1289 \r
1290                 // SET counter CWR\r
1291                 StoreCachedDword(Ptr2, 0x10*(2*channel+1),*(__int32 *)&CTC, (__int32 *)&Cache->counter[2*channel+1].CCache);\r
1292 \r
1293                 State|=2;\r
1294         }\r
1295 \r
1296         if (State!=0)         //check for the first time initialization\r
1297         {\r
1298                 CacheStep->Direction=0;\r
1299                 CacheStep->LastPosition=0;\r
1300                 CacheStep->ChainedPosition=0;\r
1301                 CacheStep->Frequency=0;\r
1302 \r
1303                 StoreDword(Ptr2, CTRXCTRL,\r
1304                                 (GLOB_CTR_STOP|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET)<<(6*(2*channel)) |    //reset PWM counter\r
1305                                 (GLOB_CTR_STOP|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET)<<(6*(2*channel+1)) ); //reload step NO counter\r
1306         }\r
1307 \r
1308 \r
1309         //********* check for end switches **********\r
1310         digin = MF624DIRead(DevRecord,0);\r
1311         if ( ((digin & (1<<(2*channel)))==0 && (CacheStep->Direction>0)) ||\r
1312                         ( ((digin & (1<<(2*channel+1)))==0) && (CacheStep->Direction<0)) )  // end switch\r
1313         {\r
1314                 StoreCachedDword(Ptr2, 0x10*(2*channel+1)+4, 0, &Cache->counter[2*channel+1].ACache); //fill A register with 0\r
1315                 NewSteps = GetDword(Ptr2, 0x10*(2*channel+1)+4);            //read counter value\r
1316                 if ((GetDword(Ptr2,0x10*(2*channel+1))&1) == 0)              //Is counter still running?\r
1317                         NewSteps = 0;\r
1318 \r
1319                 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_STOP)<<(6*(2*channel)) |       //stop PWM counter\r
1320                                 (GLOB_CTR_LOAD)<<(6*(2*channel+1)) );    //reload step NO counter\r
1321 \r
1322                 CacheStep->LastPosition -= NewSteps * CacheStep->Direction;         //actualize real position\r
1323 \r
1324                 CacheStep->Frequency = 0;\r
1325                 CacheStep->Direction = 0;\r
1326                 CacheStep->TimeStamp = NewTime;\r
1327                 return HUDAQSUCCESS;\r
1328         }\r
1329 \r
1330         //***** actualize step count ******\r
1331 \r
1332         if (CacheStep->LastPosition!=position)\r
1333         {                                     //amount of steps to do\r
1334                 if ( (State&3)!=0 ) //the first time initialization\r
1335                 {\r
1336                         NewSteps = 0;\r
1337                 }\r
1338                 else               // counter already runs\r
1339                 {\r
1340                         NewSteps = GetDword(Ptr2, 0x10*(2*channel+1)+4);\r
1341                         if ((GetDword(Ptr2,0x10*(2*channel+1))&1) == 0)              //Is counter still running?\r
1342                                 NewSteps = 0;\r
1343                 }\r
1344 \r
1345                 if (NewSteps==0)\r
1346                 {\r
1347                         CacheStep->Direction=0;   //motor is already stopped.\r
1348                         CacheStep->Frequency=0;\r
1349                 }\r
1350 \r
1351                 // CurrentPosition = LastPosition - |x|*Direction\r
1352                 // CurrentPosition = NewPosition - NewSteps\r
1353                 //   ===> NewSteps = NewPosition - LastPosition + |x|*Direction\r
1354                 NewSteps = position - CacheStep->LastPosition + NewSteps*CacheStep->Direction;\r
1355 \r
1356                 if (NewSteps>0)\r
1357                 {\r
1358                         if (CacheStep->Direction==-1)\r
1359                         {       //direction has been reversed, calculate a false target\r
1360                                 //NewSteps = Fmin(channel)*Fmin(channel)+Frequency[channel]*Frequency[channel])/(2*Acc(channel)) - 1;\r
1361                                 // \todo   Study behaviour of this feature. It lets stepper to finish in oposite direction.\r
1362                                 goto TargetIsFixed;\r
1363                         }\r
1364                         CacheStep->LastPosition = position;   //actualize position cache\r
1365                         CacheStep->Direction=1;\r
1366                         StoreCachedDword(Ptr2, 0x10*(2*channel+1)+4, NewSteps, &Cache->counter[2*channel+1].ACache);\r
1367                         State |= 4;       //reload & run counter\r
1368                 }\r
1369                 if (NewSteps<0)\r
1370                 {\r
1371                         if (CacheStep->Direction==1)         //if (Frequency[channel]>Fmin[channel])\r
1372                         {       //direction has been reversed, calculate a false target\r
1373                                 //NewSteps = Fmin(channel)*Fmin(channel)+Frequency[channel]*Frequency[channel])/(2*Acc(channel)) - 1;\r
1374                                 // \todo   Study behaviour of this feature. It lets stepper to finish in oposite direction.\r
1375                                 goto TargetIsFixed;\r
1376                         }\r
1377                         CacheStep->LastPosition = position;   //actualize position cache\r
1378                         CacheStep->Direction=-1;\r
1379                         StoreCachedDword(Ptr2, 0x10*(2*channel+1)+4, -NewSteps, &Cache->counter[2*channel+1].ACache);\r
1380                         State |= 4;       //reload & run counter\r
1381                 }\r
1382                 if (NewSteps==0)\r
1383                 {\r
1384                         //CacheStep->LastPosition;      does not need to be actualised\r
1385                         CacheStep->Direction=0;\r
1386                 }\r
1387         }\r
1388 \r
1389 TargetIsFixed:\r
1390         CacheStep->ChainedPosition = position;   // chained position makes sense only if direction is reversed\r
1391 \r
1392         //************ set frequency - (lastreq contains amount of remainning steps) ***********\r
1393         if (CacheStep->Acc<=0)\r
1394                 CacheStep->Frequency=CacheStep->Fmax;        // full acceleration when accteleration is negative\r
1395         else\r
1396         {\r
1397                 if (CacheStep->Frequency==0)\r
1398                 {\r
1399                         CacheStep->Frequency = CacheStep->Fmin;\r
1400                 }\r
1401                 else\r
1402                 {\r
1403                         if ((CacheStep->Acc > 0) && (NewSteps > (CacheStep->Fmin*CacheStep->Fmin+CacheStep->Frequency*CacheStep->Frequency)/(2*CacheStep->Acc)))\r
1404                                 NewFrequency = CacheStep->Frequency + CacheStep->Acc*abs((long)NewTime-(long)CacheStep->TimeStamp)/1000.0;\r
1405                         else\r
1406                                 NewFrequency = CacheStep->Frequency + CacheStep->Acc*abs((long)NewTime-(long)CacheStep->TimeStamp)/1000.0;\r
1407 \r
1408                         if (NewFrequency > CacheStep->Fmax) NewFrequency = CacheStep->Fmax;\r
1409                         if (NewFrequency < CacheStep->Fmin) NewFrequency = CacheStep->Fmin;\r
1410 \r
1411                         if (NewFrequency>(CacheStep->Frequency+MAXIMAL_STEP_INCREMENT)) //maximal increment threshold\r
1412                         {\r
1413                                 CacheStep->Frequency += MAXIMAL_STEP_INCREMENT;\r
1414                         }\r
1415                         else\r
1416                                 CacheStep->Frequency = NewFrequency;\r
1417                 }\r
1418         }\r
1419 \r
1420         //set a real frequency\r
1421         NewFrequency = MASTERFREQUENCY / (2*CacheStep->Frequency);\r
1422         if (NewFrequency>0xFFFFFFFF) NewFrequency=0xFFFFFFFF;\r
1423         N = max(0,(unsigned __int32)NewFrequency-1);\r
1424 \r
1425         StoreCachedDword(Ptr2, 0x10*(2*channel)+4, N, &Cache->counter[2*channel].ACache);\r
1426 \r
1427         //write stepper direction to digital output\r
1428         if (CacheStep->Direction!=0)  // only when stepper is expected to be running\r
1429                 MF624DOWriteBit(DevRecord,0,channel,CacheStep->Direction>0);\r
1430 \r
1431         //start counters\r
1432         if ( (State&4) != 0 )\r
1433         {\r
1434                 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_START)<<(6*(2*channel)) |      //stop PWM counter\r
1435                                 (GLOB_CTR_LOAD|GLOB_CTR_START)<<(6*(2*channel+1)) );    //reload step NO counter\r
1436         }\r
1437 \r
1438         CacheStep->TimeStamp = NewTime;               //refresh timestamp\r
1439         return HUDAQSUCCESS;\r
1440 }\r
1441 \r
1442 \r
1443 static HUDAQSTATUS MF624StepSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1444 {\r
1445         MF624_Private *Cache;\r
1446 \r
1447         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
1448         if (channel>=STEP_CHANNELS) return HUDAQBADARG;\r
1449 \r
1450         if (Cache->counter[2*channel].Mode != StepperPWM || Cache->counter[2*channel+1].Mode != StepperMaster)\r
1451                 MF624StepWrite(DevRecord,channel,0);     //call stepper initialization if it is not called before\r
1452 \r
1453         switch(param)\r
1454         {\r
1455                 case HudaqStepFMIN:\r
1456                         Cache->step[channel].Fmin = value;\r
1457                         return HUDAQSUCCESS;\r
1458                 case HudaqStepFMAX:\r
1459                         Cache->step[channel].Fmax = value;\r
1460                         return HUDAQSUCCESS;\r
1461                 case HudaqStepACCELERATION:\r
1462                         Cache->step[channel].Acc = value;\r
1463                         return HUDAQSUCCESS;\r
1464                 case HudaqStepCURRENTPOSITION:      // !!!!! Test value>MAXINT !!!!!\r
1465                         Cache->step[channel].LastPosition =\r
1466                                 (int)value + Cache->step[channel].Direction * GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x10*(2*channel+1)+4);\r
1467                         // CurrentPosition = LastPosition - Direction * AmountOfSteps\r
1468                         //     NewPosition = NewLastPosition - Direction * AmountOfSteps\r
1469                         // ==>  NewLastPosition = NewPosition + Direction * AmountOfSteps\r
1470 \r
1471                         if ((GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base,0x10*(2*channel+1))&1) == 0) //Is counter still running or stopped?\r
1472                                 Cache->step[channel].LastPosition = (int)value;\r
1473                         return HUDAQSUCCESS;\r
1474         }\r
1475 \r
1476         return HUDAQNOTSUPPORTED;\r
1477 }\r
1478 \r
1479 \r
1480 static double MF624StepGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1481 {\r
1482         MF624_Private *Cache;\r
1483         size_t Ptr2;\r
1484         int RetVal;\r
1485 \r
1486         Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;\r
1487         if (channel>=STEP_CHANNELS) return WRONG_VALUE;\r
1488 \r
1489         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
1490 \r
1491         if (Cache->counter[2*channel].Mode != StepperPWM || Cache->counter[2*channel+1].Mode != StepperMaster)\r
1492                 MF624StepWrite(DevRecord,channel,0);     //call stepper initialization if it is not called before\r
1493 \r
1494         switch(param)\r
1495         {\r
1496                 case HudaqStepFMIN:\r
1497                         return(Cache->step[channel].Fmin);\r
1498                 case HudaqStepFMAX:\r
1499                         return(Cache->step[channel].Fmax);\r
1500                 case HudaqStepACCELERATION:\r
1501                         return(Cache->step[channel].Acc);\r
1502                 case HudaqStepCURRENTPOSITION:      // CurrentPosition = LastPosition - Direction * AmountOfSteps\r
1503                         return(\r
1504                                         Cache->step[channel].LastPosition -\r
1505                                         Cache->step[channel].Direction * GetDword(Ptr2, 0x10*(2*channel+1)+4));\r
1506                 case HudaqStepREMAININGSTEPS:\r
1507                         RetVal = GetDword(Ptr2, 0x10*(2*channel+1)+4);\r
1508                         if ((GetDword(Ptr2,0x10*(2*channel+1))&1) == 0)              //Is counter still running or stopped?\r
1509                                 return(0);\r
1510                         return(RetVal);\r
1511                 case HudaqStepTARGETPOSITION:\r
1512                         return(Cache->step[channel].ChainedPosition);\r
1513                 case HudaqStepNUMCHANNELS:\r
1514                         return STEP_CHANNELS;\r
1515         }\r
1516 \r
1517         return WRONG_VALUE;\r
1518 }\r
1519 \r
1520 \r
1521 /****************************************************************\r
1522  *                                                              *\r
1523  *                    MF625 specific code                       *\r
1524  *                                                              *\r
1525  ****************************************************************/\r
1526 \r
1527 /** MF625 has only one internal counter. Give a chance to handle it. */\r
1528 static int MF625CtrRead(const DeviceRecord *DevRecord, unsigned channel)\r
1529 {\r
1530         return MF624CtrRead(DevRecord,channel+4);\r
1531 }\r
1532 \r
1533 \r
1534 static HUDAQSTATUS MF625CtrReset(const DeviceRecord *DevRecord, unsigned channel)\r
1535 {\r
1536         return MF624CtrReset(DevRecord,channel+4);\r
1537 }\r
1538 \r
1539 \r
1540 static double MF625CtrGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1541 {\r
1542         if (channel>=1) return WRONG_VALUE;\r
1543 \r
1544         switch(param)\r
1545         {\r
1546                 case HudaqCtrNUMCHANNELS:\r
1547                         return 1;\r
1548                 default: return MF624CtrGetParameter(DevRecord,channel+4,param);\r
1549         }\r
1550         return WRONG_VALUE;\r
1551 }\r
1552 \r
1553 \r
1554 static HUDAQSTATUS MF625CtrSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1555 {\r
1556         if (channel>=1) return HUDAQBADARG;\r
1557         return MF624CtrSetParameter(DevRecord,channel+4,param,value);\r
1558 }\r
1559 \r
1560 \r
1561 ////////////////////////////////////////////////////////////////////////////\r
1562 \r
1563 #define MAX24BITUINT 0xFFFFFF   ///< Maximal range of MF625 counters.\r
1564 \r
1565 /** Internal PWM procedure is used from both 3 phase and 6 phase PWM. */\r
1566 static HUDAQSTATUS iMF625PWMMultiphaseWrite(const DeviceRecord *DevRecord, unsigned NewDivider, const unsigned *CompareVals)\r
1567 {\r
1568         typedef enum\r
1569         {\r
1570                 DirtyDIVIDER = 0x100,\r
1571                 PwmSTOPPED =   0x200,\r
1572                 AnotherMODE =  0x400,\r
1573                 NeedRELOAD =   0x800\r
1574         } DirtyFlags;\r
1575         double T, CounterValue=-1;\r
1576         PWMMultiple *pCachePWM;\r
1577         size_t Ptr2;\r
1578         unsigned dirty = 0;\r
1579         unsigned i;\r
1580         int Timeout = 120;      //Timeout for waiting to edge\r
1581 \r
1582         pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;\r
1583 \r
1584         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
1585 \r
1586         /* Counter prepare phase */\r
1587         if (pCachePWM->Mode==Phase3PWM || pCachePWM->Mode==Phase6PWM)\r
1588         {       /* Counter runs in a right mode, check whether update is pending. */\r
1589                 *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);\r
1590 \r
1591                 while (pCachePWM->CTR.UpdatePending && pCachePWM->CTR.Running && !pCachePWM->CTR.Transparent)\r
1592                 { /* Yes, exit loop when time is sufficient or update pending flag is cleared (or Timeout occurs :-(() */\r
1593                         CounterValue = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x4);\r
1594                         if (pCachePWM->CTR.UpDown)\r
1595                                 T = 2.0*pCachePWM->Divider - CounterValue; //when going up, enlarge time.\r
1596                         else\r
1597                                 T = CounterValue;\r
1598 \r
1599                         switch (pCachePWM->CTR.ClockSource)     // convert T to time units\r
1600                         {\r
1601                                 case HudaqCtrCLOCK100kHz:T *= 500.0/MASTERFREQUENCY;\r
1602                                                          break;\r
1603                                 case HudaqCtrCLOCK1MHz:  T *= 50.0/MASTERFREQUENCY;\r
1604                                                          break;\r
1605                                 case HudaqCtrCLOCK10MHz: T *= 5.0/MASTERFREQUENCY;\r
1606                                                          break;\r
1607                                 case HudaqCtrCLOCK50MHz: T *= 1.0/MASTERFREQUENCY;\r
1608                                                          break;\r
1609                                 default:                 T *= 1.0/MASTERFREQUENCY;\r
1610                                                          Timeout = -1;  //Don't wait for external unknown clock.\r
1611                                                          break;\r
1612                         }\r
1613                         if (T>=8e-6) break;      // more than 8us is sufficient enough for feeding 7 registers.\r
1614 \r
1615                         if (Timeout-- < 0) break;\r
1616                         *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);\r
1617                 }\r
1618 \r
1619                 if (pCachePWM->CTR.Running==0) dirty |= PwmSTOPPED;  //when dirty>0, counter will be started t the end of this procedure\r
1620                 *(__int32 *)&(pCachePWM->CTR) &= ~MF625_COMMANDS; //clear all command positions\r
1621         }\r
1622         else  // A counter is switched in another mode, stop it.\r
1623         {\r
1624                 *(int *)&(pCachePWM->CTR) = 0;          //reset internal register\r
1625                 StoreDword(Ptr2, 0x0, *(__int32*)(&pCachePWM->CTR) |MF625_STOP|MF625_RESET);\r
1626                 dirty |= AnotherMODE;\r
1627         }\r
1628 \r
1629         /* Write frequency divider */\r
1630         if (NewDivider != pCachePWM->Divider)\r
1631         {\r
1632                 pCachePWM->Divider = NewDivider;\r
1633                 StoreDword(Ptr2, 4, pCachePWM->Divider);\r
1634                 dirty |= DirtyDIVIDER;\r
1635         }\r
1636 \r
1637         /* Write comparator values for particular channels. */\r
1638         for(i=0; i<PWM_625_SUBCHANNELS; i++)\r
1639         {\r
1640                 if (CompareVals[i]!=pCachePWM->Comparators[i])\r
1641                 {\r
1642                         StoreDword(Ptr2, 0x10+4*i, CompareVals[i]);\r
1643                         pCachePWM->Comparators[i] = CompareVals[i];\r
1644                         dirty |= 1<<i;\r
1645                 }\r
1646         }\r
1647 \r
1648         /* Are we doing initialization? */\r
1649         if (pCachePWM->Mode!=Phase3PWM && pCachePWM->Mode!=Phase6PWM)\r
1650         {\r
1651                 if (pCachePWM->Mode==Phase3PWM_prep)\r
1652                 {\r
1653                         // pCachePWM->CTR.Inversions = 0x38;  //111000b  - switch on 0\r
1654                         pCachePWM->CTR.Inversions = 0x07;           //000111b  - inverted approach (Jirka's hack)\r
1655                         pCachePWM->Mode=Phase3PWM;\r
1656                 }\r
1657                 else\r
1658                 {\r
1659                         pCachePWM->CTR.Inversions = 0;  // don't invert anything\r
1660                         pCachePWM->Mode=Phase6PWM;\r
1661                 }\r
1662                 pCachePWM->CTR.Filter = 1;              // Turn on a filter, for safety. User could turn a filter off by ::SetParameter.\r
1663 \r
1664                 pCachePWM->CTR.OutputControl = HudaqCtrOUTPUTNORMAL; // 00b - enable direct output\r
1665                 StoreDword(Ptr2, 0x0, *(__int32*)(&pCachePWM->CTR) |MF625_START|MF625_RESET|MF625_FORCE_UPDATE);\r
1666                 dirty = 0;                              // clean up dirty flag\r
1667         }\r
1668 \r
1669         /* Masking dirty flag eliminates unnecesary write when transparent mode is on. */\r
1670         if (pCachePWM->CTR.Transparent) dirty &= ~0x1FF;  // mask divider and all phase channels\r
1671 \r
1672         if (pCachePWM->CtrFixPWM!=0 && (dirty & 0x100))  //Fix relatively big value change (only when divider changed)\r
1673         {\r
1674                 if (CounterValue==-1) CounterValue = (unsigned __int32)GetDword(Ptr2, 0x4);\r
1675                 if (CounterValue > pCachePWM->Divider+pCachePWM->CtrFixPWM)\r
1676                         dirty |= NeedRELOAD;\r
1677         }\r
1678 \r
1679         /* When dirty, shedule update on down->up counter switch. */\r
1680         if (dirty>0)\r
1681         {\r
1682                 StoreDword(Ptr2, 0x0, *(__int32*)(&pCachePWM->CTR) |MF625_SHEDULE_UPDATE|MF625_START\r
1683                                 | (((dirty&NeedRELOAD) ==0) ? 0 : MF625_LOAD) );\r
1684         }\r
1685 \r
1686         return HUDAQSUCCESS;\r
1687 }\r
1688 \r
1689 \r
1690 static HUDAQSTATUS MF625PWMMultiphaseWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned phasenum, const unsigned *phases, double frequency, const double *duties)\r
1691 {\r
1692         unsigned CompareVals[6];\r
1693         unsigned i;\r
1694         PWMMultiple *pCachePWM;\r
1695         double T;\r
1696         unsigned DefaultChannels[PWM_625_SUBCHANNELS] = {0,1,2,3,4,5};\r
1697 \r
1698         if (channel>=1) return HUDAQBADARG;\r
1699         pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;\r
1700 \r
1701         if (phases==NULL)\r
1702         {\r
1703                 if (phasenum>PWM_625_SUBCHANNELS) return HUDAQBADARG;\r
1704                 phases=DefaultChannels;\r
1705         }\r
1706 \r
1707         if (frequency<0) return HUDAQBADARG;\r
1708 \r
1709         /* Check for valid channel numbers. */\r
1710         for (i=0; i<phasenum; i++)\r
1711         {\r
1712                 if (phases[i]>=PWM_625_SUBCHANNELS) return HUDAQBADARG;\r
1713                 //Allow duty cycle 0 or 1 for f=0.\r
1714                 if (frequency==0 && (phases[i]>0 && phases[i]<1)) return HUDAQBADARG;\r
1715         }\r
1716 \r
1717         /* Feed old values. */\r
1718         for(i=0;i<PWM_625_SUBCHANNELS;i++)\r
1719         {\r
1720                 CompareVals[i] = pCachePWM->Comparators[i];\r
1721         }\r
1722 \r
1723         if (pCachePWM->Mode!=Phase6PWM)\r
1724         {\r
1725                 pCachePWM->Mode=Phase6PWM_prep;\r
1726                 pCachePWM->CTR.ClockSource=HudaqCtrCLOCK50MHz;\r
1727         }\r
1728 \r
1729         /* Calculate with prescaller. It is not set automatically, but user is allowed to set it when needed. */\r
1730         switch (pCachePWM->CTR.ClockSource)\r
1731         {\r
1732                 case HudaqCtrCLOCK100kHz:T = MASTERFREQUENCY/500.0;break;\r
1733                 case HudaqCtrCLOCK1MHz:  T = MASTERFREQUENCY/50.0; break;\r
1734                 case HudaqCtrCLOCK10MHz: T = MASTERFREQUENCY/5.0;  break;\r
1735                 case HudaqCtrCLOCK50MHz: T = MASTERFREQUENCY;      break;\r
1736                                          // When external clock is used, frequency means division ratio.\r
1737                 default:                     T = 1.0;\r
1738         }\r
1739 \r
1740         if (frequency == 0) T = MAX24BITUINT-2;\r
1741         else {\r
1742                 T = max((T/frequency/2.0) - 1.5, 0.0);\r
1743                 T = min(T,(double)MAX24BITUINT);\r
1744         }\r
1745 \r
1746         while (phasenum>0)\r
1747         {  /* There is intentionally a value >(Divider+1), because it generates true 1 on output. */\r
1748                 CompareVals[*phases] = (unsigned) min( max(*duties * ((unsigned)T+2), 0.0), (double)MAX24BITUINT );\r
1749 \r
1750                 phases++;       /* Toss away these arrays. */\r
1751                 duties++;\r
1752                 phasenum--;\r
1753         }\r
1754 \r
1755         return iMF625PWMMultiphaseWrite(DevRecord, (unsigned)T, CompareVals);\r
1756 }\r
1757 \r
1758 \r
1759 HUDAQSTATUS MF625PWM3Write(const DeviceRecord *DevRecord, unsigned channel, double frequency, double duty1, double duty2, double duty3)\r
1760 {\r
1761         PWMMultiple *pCachePWM;\r
1762         int DeadBandTicks;\r
1763         double T;\r
1764         unsigned CompareVals[6];\r
1765 \r
1766         if (channel>=1) return HUDAQBADARG;\r
1767         pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;\r
1768 \r
1769         if (frequency<=0)\r
1770         {\r
1771                 if (frequency<0) return HUDAQBADARG;\r
1772                 //Allow duty cycle 0 or 1 for f=0.\r
1773                 if ((duty1>0 && duty1<1) || (duty2>0 && duty2<1) || (duty3>0 && duty3<1))\r
1774                         return HUDAQBADARG;\r
1775         }\r
1776 \r
1777         /* Here is a small hack introduced by Jirka Sehnal. The key idea is to invert duty,\r
1778            deadband and output polarity together. It behaves same and it also guarantee symetrical\r
1779            pulses around counter value 'n'. MF625 flips on zero passing. After this hack it would\r
1780            look like it flips in n passing. */\r
1781         duty1 = 1 - duty1;\r
1782         duty2 = 1 - duty2;\r
1783         duty3 = 1 - duty3;\r
1784 \r
1785 \r
1786         if (pCachePWM->Mode!=Phase3PWM)\r
1787         {\r
1788                 pCachePWM->Mode=Phase3PWM_prep;\r
1789                 pCachePWM->CTR.ClockSource=HudaqCtrCLOCK50MHz;\r
1790         }\r
1791 \r
1792         /* Calculate with prescaller. It is not set automatically, but user is allowed to set it when needed. */\r
1793         switch (pCachePWM->CTR.ClockSource)\r
1794         {\r
1795                 case HudaqCtrCLOCK100kHz:T = MASTERFREQUENCY/500.0;break;\r
1796                 case HudaqCtrCLOCK1MHz:  T = MASTERFREQUENCY/50.0; break;\r
1797                 case HudaqCtrCLOCK10MHz: T = MASTERFREQUENCY/5.0;  break;\r
1798                 case HudaqCtrCLOCK50MHz: T = MASTERFREQUENCY;      break;\r
1799                                          // When external clock is used, frequency means division ratio.\r
1800                 default:                     T = 1.0;\r
1801         }\r
1802 \r
1803         DeadBandTicks = (int)(T*pCachePWM->DeadBand + 0.5);\r
1804 \r
1805         if (frequency == 0) T = MAX24BITUINT-2;\r
1806         else {\r
1807                 T = max((T/frequency/2.0) - 1.5, 0.0);\r
1808                 T = min(T,(double)MAX24BITUINT);\r
1809         }\r
1810 \r
1811         //A differency between Phases[0]-Phases[3] is guaranteed\r
1812         CompareVals[0] = (unsigned)min( max(((int)T+2)*duty1 + DeadBandTicks/2.0, 0.0), (double)MAX24BITUINT);\r
1813         CompareVals[3] = (unsigned)min( max((int)CompareVals[0]-DeadBandTicks, 0), MAX24BITUINT);\r
1814 \r
1815         CompareVals[1] = (unsigned)min( max(((int)T+2)*duty2 + DeadBandTicks/2.0, 0.0), (double)MAX24BITUINT);\r
1816         CompareVals[4] = (unsigned)min( max((int)CompareVals[1]-DeadBandTicks, 0), MAX24BITUINT);\r
1817 \r
1818         CompareVals[2] = (unsigned)min( max(((int)T+2)*duty3 + DeadBandTicks/2.0, 0.0), (double)MAX24BITUINT);\r
1819         CompareVals[5] = (unsigned)min( max((int)CompareVals[2]-DeadBandTicks, 0), MAX24BITUINT);\r
1820 \r
1821         return iMF625PWMMultiphaseWrite(DevRecord, (unsigned)T, CompareVals);\r
1822 }\r
1823 \r
1824 \r
1825 static HUDAQSTATUS MF625PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)\r
1826 {\r
1827         if (channel>=1) return HUDAQBADARG;\r
1828         return MF625PWMMultiphaseWrite(DevRecord, 0, 1, &channel, frequency, &dutycycle);\r
1829 }\r
1830 \r
1831 \r
1832 static double MF625PwmGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
1833 {\r
1834         PWMMultiple *pCachePWM;\r
1835         int retval;\r
1836 \r
1837         if (channel>=PWM_625_CHANNELS) return WRONG_VALUE;\r
1838         pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;\r
1839 \r
1840         switch(param)\r
1841         {\r
1842                 case HudaqPwmNUMCHANNELS:\r
1843                         return PWM_625_CHANNELS;\r
1844                 case HudaqPwmDEADBAND:\r
1845                         return pCachePWM->DeadBand;\r
1846                 case HudaqPwmOUTPUTUDCONTROL:\r
1847                         return pCachePWM->CTR.OutputUDCtrl;\r
1848                 case HudaqPwmOUTPUTCONTROL:\r
1849                         return pCachePWM->CTR.OutputControl;\r
1850                 case HudaqPwmCLOCKSOURCE:\r
1851                         return pCachePWM->CTR.ClockSource;\r
1852                 case HudaqPwmFILTER:\r
1853                         return pCachePWM->CTR.Filter;\r
1854                 case HudaqPwmGATESOURCE:\r
1855                         return pCachePWM->CTR.GateSource;\r
1856                 case HudaqPwmTRANSPARENT:\r
1857                         return pCachePWM->CTR.Transparent;\r
1858                 case HudaqPwmEMERGENCY:\r
1859                         return pCachePWM->CTR.Emergency;\r
1860                 case HudaqPwmGATEPOLARITY:\r
1861                         return pCachePWM->CTR.GatePolarity;\r
1862                 case HudaqPwmINPUT:\r
1863                         *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);\r
1864                         retval = pCachePWM->CTR.InputWire;\r
1865                         *(__int32 *)&(pCachePWM->CTR) &= ~MF625_COMMANDS; //clear all command positions\r
1866                         return retval;\r
1867                 case HudaqPwmPHASES:\r
1868                         *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);\r
1869                         retval = pCachePWM->CTR.PhaseOut;\r
1870                         *(__int32 *)&(pCachePWM->CTR) &= ~MF625_COMMANDS; //clear all command positions\r
1871                         return retval;\r
1872                 case HudaqPwmUPDOWN:\r
1873                         *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);\r
1874                         retval = pCachePWM->CTR.UpDown;\r
1875                         *(__int32 *)&(pCachePWM->CTR) &= ~MF625_COMMANDS; //clear all command positions\r
1876                         return retval;\r
1877                 case HudaqPwmINVERSIONS:\r
1878                         *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);\r
1879                         retval = pCachePWM->CTR.Inversions;\r
1880                         *(__int32 *)&(pCachePWM->CTR) &= ~MF625_COMMANDS; //clear all command positions\r
1881                         return retval;\r
1882                 case HudaqPwmTHRESHOLD:\r
1883                         return(pCachePWM->CtrFixPWM/MASTERFREQUENCY);\r
1884         }\r
1885 \r
1886         return WRONG_VALUE;\r
1887 }\r
1888 \r
1889 \r
1890 static HUDAQSTATUS MF625PwmSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
1891 {\r
1892         PWMMultiple *pCachePWM;\r
1893         size_t Ptr2;\r
1894 \r
1895         if (channel>=PWM_625_CHANNELS) return HUDAQBADARG;\r
1896         pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;\r
1897         Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
1898 \r
1899         switch(param)\r
1900         {\r
1901                 case HudaqPwmDEADBAND:\r
1902                         if (value<0 || value>1e-3) return HUDAQBADARG; //allow range <0;1ms>\r
1903                         pCachePWM->DeadBand = value;\r
1904                         return HUDAQSUCCESS;\r
1905 \r
1906                 case HudaqPwmFILTER:\r
1907                         if (value>=2 || value<0) return HUDAQBADARG;\r
1908                         if (pCachePWM->CTR.Filter != (int)value)\r
1909                         {\r
1910                                 pCachePWM->CTR.Filter = (int)value;\r
1911                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1912                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1913                         }\r
1914                         return HUDAQSUCCESS;\r
1915 \r
1916                 case HudaqPwmOUTPUTCONTROL:\r
1917                         if (value>=4 || value<0) return HUDAQBADARG;\r
1918                         if (pCachePWM->CTR.OutputControl != (int)value)\r
1919                         {\r
1920                                 pCachePWM->CTR.OutputControl = (int)value;\r
1921                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1922                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1923                         }\r
1924                         return HUDAQSUCCESS;\r
1925 \r
1926                 case HudaqPwmOUTPUTUDCONTROL:\r
1927                         if (value>=4 || value<0) return HUDAQBADARG;\r
1928                         if (pCachePWM->CTR.OutputUDCtrl != (int)value)\r
1929                         {\r
1930                                 pCachePWM->CTR.OutputUDCtrl = (int)value;\r
1931                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1932                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1933                         }\r
1934                         return HUDAQSUCCESS;\r
1935 \r
1936                 case HudaqPwmCLOCKSOURCE:\r
1937                         if (value>=16 || value<0) return HUDAQBADARG;\r
1938                         if (pCachePWM->CTR.ClockSource != (int)value)\r
1939                         {\r
1940                                 pCachePWM->CTR.ClockSource = (int)value;\r
1941                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1942                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1943                         }\r
1944                         return HUDAQSUCCESS;\r
1945 \r
1946                 case HudaqPwmGATEPOLARITY:\r
1947                         if (value>=2 || value<0) return HUDAQBADARG;\r
1948                         if (pCachePWM->CTR.GatePolarity != (int)value)\r
1949                         {\r
1950                                 pCachePWM->CTR.GatePolarity = (int)value;\r
1951                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1952                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1953                         }\r
1954                         return HUDAQSUCCESS;\r
1955 \r
1956                 case HudaqPwmGATESOURCE:\r
1957                         if (value>=4 || value<0) return HUDAQBADARG;\r
1958                         if (pCachePWM->CTR.GateSource != (int)value)\r
1959                         {\r
1960                                 pCachePWM->CTR.GateSource = (int)value;\r
1961                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1962                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1963                         }\r
1964                         return HUDAQSUCCESS;\r
1965 \r
1966                 case HudaqPwmTRANSPARENT:\r
1967                         if (value>=2 || value<0) return HUDAQBADARG;\r
1968                         if (pCachePWM->CTR.Transparent != (int)value)\r
1969                         {\r
1970                                 pCachePWM->CTR.Transparent = (int)value;\r
1971                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1972                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1973                         }\r
1974                         return HUDAQSUCCESS;\r
1975 \r
1976                 case HudaqPwmEMERGENCY:\r
1977                         if (value>=4 || value<0) return HUDAQBADARG;\r
1978                         if (pCachePWM->CTR.Emergency != (int)value)\r
1979                         {\r
1980                                 pCachePWM->CTR.Emergency = (int)value;\r
1981                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1982                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1983                         }\r
1984                         return HUDAQSUCCESS;\r
1985 \r
1986                 case HudaqPwmINVERSIONS:\r
1987                         if (value>0x3F || value<0) return HUDAQBADARG;  //6 bits only\r
1988                         if (pCachePWM->CTR.Inversions != (int)value)\r
1989                         {\r
1990                                 pCachePWM->CTR.Inversions = (int)value;\r
1991                                 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,\r
1992                                                 0x0,*(__int32 *)&pCachePWM->CTR);\r
1993                         }\r
1994                         return HUDAQSUCCESS;\r
1995 \r
1996                 case HudaqPwmTHRESHOLD:\r
1997                         pCachePWM->CtrFixPWM = (value==0)? 0 : MASTERFREQUENCY/value;\r
1998                         return HUDAQSUCCESS;\r
1999 \r
2000         }\r
2001 \r
2002         return HUDAQNOTSUPPORTED;\r
2003 }\r
2004 \r
2005 \r
2006 /****************************************************************\r
2007  *                                                              *\r
2008  *                    GET/SET PARAMETERS                        *\r
2009  *                                                              *\r
2010  ****************************************************************/\r
2011 \r
2012 static HUDAQSTATUS MF624SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
2013 {\r
2014         switch(param & HudaqSubsystemMASK)\r
2015         {\r
2016                 //case HudaqDI:\r
2017                 //case HudaqDO:\r
2018                 case HudaqAI:  return MF624AISetParameter(DevRecord,channel,param,value);\r
2019                 case HudaqAO:  return MF624AOSetParameter(DevRecord,channel,param,value);\r
2020                 case HudaqEnc: return MF624EncSetParameter(DevRecord,channel,param,value);\r
2021                 case HudaqPWM:\r
2022                 case HudaqCtr: return MF624CtrSetParameter(DevRecord,channel,param,value);\r
2023                 case HudaqStep:return MF624StepSetParameter(DevRecord,channel,param,value);\r
2024         }\r
2025         return HUDAQNOTSUPPORTED;\r
2026 }\r
2027 \r
2028 \r
2029 static HUDAQSTATUS MF625SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
2030 {\r
2031         switch(param & HudaqSubsystemMASK)\r
2032         {\r
2033                 //case HudaqDI:\r
2034                 //case HudaqDO:\r
2035                 case HudaqAI:  return MF624AISetParameter(DevRecord,channel,param,value);\r
2036                 case HudaqAO:  return MF624AOSetParameter(DevRecord,channel,param,value);\r
2037                 case HudaqEnc: return MF624EncSetParameter(DevRecord,channel,param,value);\r
2038                 case HudaqPWM: return MF625PwmSetParameter(DevRecord,channel,param,value);\r
2039                 case HudaqCtr: return MF625CtrSetParameter(DevRecord,channel,param,value);\r
2040                                //case HudaqStep:\r
2041         }\r
2042         return HUDAQNOTSUPPORTED;\r
2043 }\r
2044 \r
2045 \r
2046 static HUDAQSTATUS AD622SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
2047 {\r
2048         switch(param & HudaqSubsystemMASK)\r
2049         {\r
2050                 //case HudaqDI:\r
2051                 //case HudaqDO:\r
2052                 case HudaqAI:  return MF624AISetParameter(DevRecord,channel,param,value);\r
2053                 case HudaqAO:  return MF624AOSetParameter(DevRecord,channel,param,value);\r
2054                                //case HudaqEnc:\r
2055                                //case HudaqCtr:\r
2056                                //case HudaqStep:\r
2057         }\r
2058         return HUDAQNOTSUPPORTED;\r
2059 }\r
2060 \r
2061 \r
2062 \r
2063 static double MF624GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
2064 {\r
2065         switch(param & HudaqSubsystemMASK)\r
2066         {\r
2067                 case HudaqDI:\r
2068                 case HudaqDO:  return MF624DIOGetParameter(channel,param);\r
2069                 case HudaqAI:  return MF624AIGetParameter(DevRecord,channel,param);\r
2070                 case HudaqAO:  return MF624AOGetParameter(DevRecord,channel,param);\r
2071                 case HudaqEnc: return MF624EncGetParameter(DevRecord,channel,param);\r
2072                 case HudaqPWM:\r
2073                 case HudaqCtr: return MF624CtrGetParameter(DevRecord,channel,param);\r
2074                 case HudaqStep:return MF624StepGetParameter(DevRecord,channel,param);\r
2075                 case HudaqIRQ: return ((MF624_Private *)DevRecord->DrvRes.DriverData)->Hdr.IRQcounter;\r
2076         }\r
2077         return WRONG_VALUE;\r
2078 }\r
2079 \r
2080 \r
2081 static double MF625GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
2082 {\r
2083         switch(param & HudaqSubsystemMASK)\r
2084         {\r
2085                 case HudaqDI:\r
2086                 case HudaqDO:  return MF624DIOGetParameter(channel,param);\r
2087                 case HudaqAI:  return MF624AIGetParameter(DevRecord,channel,param);\r
2088                 case HudaqAO:  return MF624AOGetParameter(DevRecord,channel,param);\r
2089                 case HudaqEnc: return MF624EncGetParameter(DevRecord,channel,param);\r
2090                 case HudaqPWM: return MF625PwmGetParameter(DevRecord,channel,param);\r
2091                 case HudaqCtr: return MF625CtrGetParameter(DevRecord,channel,param);\r
2092                                //case HudaqStep:\r
2093         }\r
2094         return WRONG_VALUE;\r
2095 }\r
2096 \r
2097 \r
2098 static double AD622GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
2099 {\r
2100         switch(param & HudaqSubsystemMASK)\r
2101         {\r
2102                 case HudaqDI:\r
2103                 case HudaqDO:  return MF624DIOGetParameter(channel,param);\r
2104                 case HudaqAI:  return MF624AIGetParameter(DevRecord,channel,param);\r
2105                 case HudaqAO:  return MF624AOGetParameter(DevRecord,channel,param);\r
2106                                //case HudaqEnc:\r
2107                                //case HudaqPWM:\r
2108                                //case HudaqCtr:\r
2109                                //case HudaqStep:\r
2110         }\r
2111         return WRONG_VALUE;\r
2112 }\r
2113 \r
2114 \r
2115 static const HudaqRange *MF624QueryRange(const DeviceRecord *DevRecord, HudaqSubsystem S, unsigned item)\r
2116 {\r
2117         if (item>=1) return NULL;\r
2118         switch(S & HudaqSubsystemMASK)\r
2119         {\r
2120                 case HudaqAI:  return &MF624RangeAIO;\r
2121                 case HudaqAO:  return &MF624RangeAIO;\r
2122         }\r
2123         return NULL;\r
2124 }\r
2125 \r
2126 \r
2127 /****************************************************************\r
2128  *                                                              *\r
2129  *                       INITIALIZATION                         *\r
2130  *                                                              *\r
2131  ****************************************************************/\r
2132 \r
2133 /** Initialize subset of hardware that is supported by both MF624 & AD622. */\r
2134 static void InitAiAoDiDo(size_t Ptr0, size_t Ptr1, MF624_Private *Cache)\r
2135 {\r
2136         int i;\r
2137 \r
2138         /* Initialization of AD's */\r
2139         Cache->ADCtrlCache = 0;   /* 0 in channel selector is inocent. First real cycle will use nonzero value. */\r
2140         for(i=0;i<AD_CHANNELS;i++)\r
2141         {\r
2142                 Cache->AIOptions[i] = 0;\r
2143         }\r
2144 \r
2145         /* Initialization of DA's */\r
2146         for(i=0;i<DA_CHANNELS;i++)    /*First clear DAC registers and then allow GPIOC_DACEN to avoid hazard pulse.*/\r
2147         {\r
2148                 StoreWord(Ptr1,DADATA+2*i,0x2000);\r
2149                 Cache->DA[i].Cache = 0x2000;\r
2150                 Cache->DA[i].Options = 0;\r
2151         }\r
2152         StoreDword(Ptr0, GPIOC, GPIOC_BASE|GPIOC_DACEN);  //enable DACen\r
2153 \r
2154         /* Initialization of digital outputs */\r
2155         Cache->DoutCache = 0;\r
2156         StoreWord(Ptr1, DOUT, 0);\r
2157 }\r
2158 \r
2159 \r
2160 \r
2161 /** Internal initialization function that should be called from HudaqOpenDevice\r
2162  * \todo Exclusive access is only partially implemented. */\r
2163 static int Init624(DeviceRecord *DevRecord, int IniOptions)\r
2164 {\r
2165         size_t Ptr1, Ptr2;\r
2166         int i;\r
2167         MF624_Private *Cache;\r
2168 \r
2169         if (DevRecord == NULL)\r
2170                 return -1;\r
2171 \r
2172         if (DevRecord->DrvRes.Resources.NumMemResources < 3)\r
2173                 return -2;  //insufficient amount of resources\r
2174 \r
2175         Cache = DevRecord->DrvRes.DriverData;\r
2176         if (Cache == NULL)\r
2177         {       /*NO CACHE FOUND, cache==NULL*/\r
2178                 return -3;\r
2179         }\r
2180         if (DevRecord->DrvRes.DriverDataSize < sizeof(MF624_Private))\r
2181                 return -4;\r
2182 \r
2183         DevRecord->pCT = &CT624;\r
2184 \r
2185         if ((IniOptions & HudaqOpenNOINIT) == 0)\r
2186         {                           //initialize hardware only if no other application is running\r
2187                 Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
2188                 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
2189 \r
2190                 /* Initialization of AD's, DA's, DI and DO. */\r
2191                 InitAiAoDiDo(DevRecord->DrvRes.Resources.MemResources[0].Base, Ptr1, Cache);\r
2192 \r
2193                 /* Initialization of encoders */\r
2194                 Cache->EncCache = 0;\r
2195                 StoreDword(Ptr2, IRCCTRL, 0x10101010); //All 4 Encoder CTRs are RESET to 0\r
2196                 StoreDword(Ptr2, IRCCTRL, 0);          //Set also their CWR to 0\r
2197                 for(i = 0; i < ENC_CHANNELS; i++)\r
2198                 {\r
2199                         Cache->EncOptions[i] = 0;\r
2200                 }\r
2201 \r
2202                 /* Initialization of counters */\r
2203                 for(i = 0; i < CTR_CHANNELS; i++)\r
2204                 {\r
2205                         Cache->counter[i].Mode = OUT_0;\r
2206                         *((__int32 *)&(Cache->counter[i].CCache)) = 0;\r
2207                         Cache->counter[i].CCache.OutputControl = 2;       //permanent 0\r
2208                         StoreDword(Ptr2, 0x10*i, *(__int32 *)&(Cache->counter[i].CCache)); // write CTR mode\r
2209                         StoreDword(Ptr2, 0x10*i+4, 0);    // clear A register\r
2210                         Cache->counter[i].ACache = 0;\r
2211                         StoreDword(Ptr2, 0x10*i+8, 0);    // clear B register\r
2212                         Cache->counter[i].BCache = 0;\r
2213                         Cache->counter[i].CtrResetCounter = 0;\r
2214                         Cache->counter[i].CtrFixPWM = MASTERFREQUENCY/1000.0;  //fix time difference bigger than 1ms\r
2215                 }\r
2216 \r
2217                 StoreDword(Ptr2, CTRXCTRL,           // stop and reset all counters\r
2218                                 GLOB_CTR_BASE0*(GLOB_CTR_STOP|GLOB_CTR_LOAD|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET) |\r
2219                                 GLOB_CTR_BASE1*(GLOB_CTR_STOP|GLOB_CTR_LOAD|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET) |\r
2220                                 GLOB_CTR_BASE2*(GLOB_CTR_STOP|GLOB_CTR_LOAD|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET) |\r
2221                                 GLOB_CTR_BASE3*(GLOB_CTR_STOP|GLOB_CTR_LOAD|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET) |\r
2222                                 GLOB_CTR_BASE4*(GLOB_CTR_STOP|GLOB_CTR_LOAD|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET) );\r
2223 \r
2224                 /* Initialization of stepper motors courtesy */\r
2225                 for(i = 0; i < 2; i++)\r
2226                 {\r
2227                         Cache->step[i].LastPosition = 0;\r
2228                         Cache->step[i].ChainedPosition = 0;\r
2229                         Cache->step[i].Direction = 0;\r
2230 \r
2231                         Cache->step[i].Fmin = 1000;\r
2232                         Cache->step[i].Fmax = 5000;\r
2233                         Cache->step[i].Acc = 1000;\r
2234                 }\r
2235 \r
2236         }\r
2237         //else\r
2238         //  {\r
2239         //  if (exclusive) return -5;  // exclusive access failed, resources.c calls HudaqCloseDevice\r
2240         //  }\r
2241 \r
2242         return 1; //success\r
2243 }\r
2244 \r
2245 \r
2246 /** Internal cleanup procedure for MF624 & MF625 & AD622 */\r
2247 static void Done624(DeviceRecord *DevRecord)\r
2248 {\r
2249         if (DevRecord == NULL)\r
2250                 return;\r
2251 \r
2252         DevRecord->pCT = &CtDummy;\r
2253 }\r
2254 \r
2255 \r
2256 const CallTable CT624 =\r
2257 {\r
2258         "MF624", 0x186C, 0x0624,\r
2259         Init624,\r
2260         Done624,\r
2261 \r
2262         MF624SetParameter,\r
2263         MF624GetParameter,\r
2264         MF624QueryRange,\r
2265 \r
2266         // INITIALIZE DI callers\r
2267         MF624DIRead,\r
2268         GenericDIReadBit,           //Generic implementation\r
2269         GenericOneDIReadMultiple,\r
2270         // INITIALIZE DO callers\r
2271         MF624DOWrite,\r
2272         MF624DOWriteBit,\r
2273         MF624DOWriteMultipleBits,\r
2274         GenericDOWriteMultiple,\r
2275         // INITIALIZE AI callers\r
2276         MF624AIRead,\r
2277         MF624AIReadMultiple,\r
2278         // INITIALIZE AO callers\r
2279         MF624AOWrite,\r
2280         MF624AOWriteMultiple,\r
2281         // INITIALIZE Enc callers\r
2282         MF624EncRead,\r
2283         MF624EncReset,\r
2284         // INITIALIZE Ctr callers\r
2285         MF624CtrRead,\r
2286         MF624CtrReset,\r
2287         // INITIALIZE PWM callers\r
2288         MF624PWMWrite,\r
2289         NULL,\r
2290         NULL,\r
2291         // INITIALIZE Step callers\r
2292         MF624StepWrite,\r
2293 };\r
2294 \r
2295 \r
2296 /** Internal initialization function that should be called from HudaqOpenDevice\r
2297  * \todo Exclusive access is only partially implemented. */\r
2298 static int Init625(DeviceRecord *DevRecord, int IniOptions)\r
2299 {\r
2300         size_t Ptr1, Ptr2;\r
2301         int i;\r
2302         MF624_Private *Cache;\r
2303 \r
2304         if (DevRecord==NULL) return -1;\r
2305 \r
2306         if (DevRecord->DrvRes.Resources.NumMemResources<3) return -2;  //insufficient amount of resources\r
2307         Cache = DevRecord->DrvRes.DriverData;\r
2308         if (Cache==NULL)\r
2309         {\r
2310                 //printf("\nNO CACHE FOUND, cache==NULL!");\r
2311                 return -3;\r
2312         }\r
2313         if (DevRecord->DrvRes.DriverDataSize<sizeof(MF624_Private)) return -4;\r
2314 \r
2315         DevRecord->pCT = &CT625;\r
2316 \r
2317         if ((IniOptions & HudaqOpenNOINIT)==0)\r
2318         {                           //initialize hardware only if no other application is running\r
2319                 Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
2320                 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;\r
2321 \r
2322                 /* Initialization of AD's, DA's, DI and DO. */\r
2323                 InitAiAoDiDo(DevRecord->DrvRes.Resources.MemResources[0].Base,Ptr1,Cache);\r
2324 \r
2325                 /* Initialization of encoders */\r
2326                 Cache->EncCache=0;\r
2327                 StoreDword(Ptr2,IRCCTRL,0x10101010); //All 4 Encoder CTRs are RESET to 0\r
2328                 StoreDword(Ptr2,IRCCTRL,0);          //Set also their CWR to 0\r
2329                 for(i=0;i<ENC_CHANNELS;i++)\r
2330                 {\r
2331                         Cache->EncOptions[i]=0;\r
2332                 }\r
2333 \r
2334                 /* Initialization of counters, MF625 supports only counter 4 */\r
2335                 for(i=4;i<CTR_CHANNELS;i++)\r
2336                 {\r
2337                         Cache->counter[i].Mode = OUT_0;\r
2338                         *((__int32 *)&(Cache->counter[i].CCache)) = 0;\r
2339                         Cache->counter[i].CCache.OutputControl = 2;       //permanent 0\r
2340                         StoreDword(Ptr2, 0x10*i, *(__int32 *)&(Cache->counter[i].CCache)); // write CTR mode\r
2341                         StoreDword(Ptr2, 0x10*i+4, 0);    // clear A register\r
2342                         Cache->counter[i].ACache = 0;\r
2343                         StoreDword(Ptr2, 0x10*i+8, 0);    // clear B register\r
2344                         Cache->counter[i].BCache = 0;\r
2345                         Cache->counter[i].CtrResetCounter = 0;\r
2346                         Cache->counter[i].CtrFixPWM = MASTERFREQUENCY/1000.0;  //fix time difference bigger than 1ms\r
2347                 }\r
2348 \r
2349                 StoreDword(Ptr2, CTRXCTRL,           // stop and reset internal counter\r
2350                                 GLOB_CTR_BASE4*(GLOB_CTR_STOP|GLOB_CTR_LOAD|GLOB_CTR_RESET|GLOB_CTR_OUT_RESET) );\r
2351 \r
2352                 /* Initialization of specialised PWM counter. */\r
2353                 Cache->pwm.DeadBand = 0;\r
2354                 Cache->pwm.CtrFixPWM = 0;\r
2355                 Cache->pwm.Mode = OUT_0_ALL;    // start in the mode all zeros\r
2356 \r
2357                 Cache->pwm.Divider = 0;\r
2358                 StoreDword(Ptr2, 0x4, 0);\r
2359 \r
2360                 for(i=0;i<PWM_625_SUBCHANNELS;i++)\r
2361                 {\r
2362                         Cache->pwm.Comparators[i] = 0;\r
2363                         StoreDword(Ptr2, 0x10+4*i, 0);\r
2364                 }\r
2365 \r
2366                 *(__int32*)(&Cache->pwm.CTR) = 0;\r
2367                 Cache->pwm.CTR.OutputControl = 2;       // 10b - fix output on 0\r
2368                 StoreDword(Ptr2, 0x0, *(__int32*)(&Cache->pwm.CTR) |\r
2369                                 MF625_STOP|MF625_RESET|MF625_CANCEL_SHEDULE|MF625_FORCE_UPDATE|MF625_FORCE_UP);\r
2370         }\r
2371 \r
2372         return 1; //success\r
2373 }\r
2374 \r
2375 const CallTable CT625 =\r
2376 {\r
2377         "MF625", 0x186C, 0x0625,\r
2378         Init625,\r
2379         Done624,\r
2380 \r
2381         MF625SetParameter,\r
2382         MF625GetParameter,\r
2383         MF624QueryRange,\r
2384 \r
2385         // INITIALIZE DI callers\r
2386         MF624DIRead,\r
2387         GenericDIReadBit,           //Generic implementation\r
2388         GenericOneDIReadMultiple,\r
2389         // INITIALIZE DO callers\r
2390         MF624DOWrite,\r
2391         MF624DOWriteBit,\r
2392         MF624DOWriteMultipleBits,\r
2393         GenericDOWriteMultiple,\r
2394         // INITIALIZE AI callers\r
2395         MF624AIRead,\r
2396         MF624AIReadMultiple,\r
2397         // INITIALIZE AO callers\r
2398         MF624AOWrite,\r
2399         MF624AOWriteMultiple,\r
2400         // INITIALIZE Enc callers\r
2401         MF624EncRead,\r
2402         MF624EncReset,\r
2403         // INITIALIZE Ctr callers\r
2404         MF625CtrRead,\r
2405         MF625CtrReset,\r
2406         // INITIALIZE PWM callers\r
2407         MF625PWMWrite,\r
2408         MF625PWMMultiphaseWrite,\r
2409         MF625PWM3Write,\r
2410         // INITIALIZE Step callers\r
2411         NULL,                   // Not available\r
2412 };\r
2413 \r
2414 \r
2415 \r
2416 /** Internal initialization function that should be called from HudaqOpenDevice\r
2417  * \todo Exclusive access is only partially implemented. */\r
2418 static int Init622(DeviceRecord *DevRecord, int IniOptions)\r
2419 {\r
2420         MF624_Private *Cache;\r
2421 \r
2422         if (DevRecord==NULL) return -1;\r
2423 \r
2424         if (DevRecord->DrvRes.Resources.NumMemResources<2) return -2;  //insufficient amount of resources\r
2425         Cache = DevRecord->DrvRes.DriverData;\r
2426         if (Cache==NULL) return -3;\r
2427         if (DevRecord->DrvRes.DriverDataSize<sizeof(MF624_Private)) return -4;\r
2428 \r
2429         DevRecord->pCT = &CT622;\r
2430 \r
2431         if ((IniOptions & HudaqOpenNOINIT)==0)\r
2432         {       //initialize hardware only if no other application is running\r
2433 \r
2434                 /* Initialization of AD's, DA's, DI and DO. */\r
2435                 InitAiAoDiDo(DevRecord->DrvRes.Resources.MemResources[0].Base,\r
2436                                 DevRecord->DrvRes.Resources.MemResources[1].Base,\r
2437                                 Cache);\r
2438         }\r
2439 \r
2440         return 1; //success\r
2441 }\r
2442 \r
2443 \r
2444 const CallTable CT622 =\r
2445 {\r
2446         "AD622", 0x186C, 0x0622,\r
2447         Init622,\r
2448         Done624,\r
2449 \r
2450         AD622SetParameter,\r
2451         AD622GetParameter,\r
2452         MF624QueryRange,\r
2453 \r
2454         // INITIALIZE DI callers\r
2455         MF624DIRead,\r
2456         GenericDIReadBit,           //Generic implementation\r
2457         GenericOneDIReadMultiple,\r
2458         // INITIALIZE DO callers\r
2459         MF624DOWrite,\r
2460         MF624DOWriteBit,\r
2461         MF624DOWriteMultipleBits,\r
2462         GenericDOWriteMultiple,\r
2463         // INITIALIZE AI callers\r
2464         MF624AIRead,\r
2465         MF624AIReadMultiple,\r
2466         // INITIALIZE AO callers\r
2467         MF624AOWrite,\r
2468         MF624AOWriteMultiple,\r
2469         // INITIALIZE Enc callers\r
2470         NULL,                       // Not available\r
2471         NULL,\r
2472         // INITIALIZE Ctr callers\r
2473         NULL,                       // Not available\r
2474         NULL,\r
2475         // INITIALIZE PWM callers\r
2476         NULL,                       // Not available\r
2477         NULL,\r
2478         NULL,\r
2479         // INITIALIZE Step callers\r
2480         NULL,                       // Not available\r
2481 };\r
2482 \r