1 /****************************************************************/
\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
8 #if defined(_WIN32) || defined(_WIN64)
\r
16 #include "hudaqlib.h"
\r
17 #include "hudaq_internal.h"
\r
21 /** IRC control register */
\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
32 /** Counter control register */
\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
53 /** This enum contains internal state info. */
\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
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
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
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
93 /** One record related to stepper motor. */
\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
108 /** Commands are mixed into ::MF625Register. */
\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
126 /** Counter control register for MF625. */
\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
150 /** Modes of 3 phase PWM counter. */
\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
163 /** Record related to multiphase PWM. */
\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
175 /** One record related to counter. */
\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
187 /** One record related to Digital to Analog output channel. */
\r
190 __int16 Cache; ///< Cached raw value for digital output
\r
191 int Options; ///< DOut bit options; bit 0=0 volts, 1-raw
\r
195 #define IrcResetCounter 2 ///< Binary flag for IrcOptions
\r
197 /** Ranges for both Analog Inputs and Analog Outputs - only one item for MF624. */
\r
198 static const HudaqRange MF624RangeAIO = {-10,+10};
\r
201 /** Cache of MF624 state, this cache is shared across all applications inside shared memory. */
\r
204 UserDataHeader Hdr; ///< General device setup
\r
206 /* Digital inputs/outputs */
\r
207 __int16 DoutCache; ///< digital outputs to be cached
\r
209 /* Analog inputs/outputs */
\r
211 unsigned AIOptions[AD_CHANNELS]; ///< Options for analog inputs
\r
212 __int16 ADCtrlCache; ///< Selected channels for conversion
\r
214 DaSetup DA[DA_CHANNELS]; ///< DA values to be cached
\r
216 /* Counters and encoders */
\r
218 __int32 EncCache; ///< Encoder (IRC counter) state register
\r
219 int EncOptions[ENC_CHANNELS]; ///< Bit options for encoders
\r
221 Counter counter[CTR_CHANNELS]; ///< Array that holds state of all counters
\r
224 Stepper step[STEP_CHANNELS]; ///< Stepper motor table
\r
225 PWMMultiple pwm; ///< MF625 specialized PWM counter
\r
229 #define MAXIMAL_STEP_INCREMENT 500 ///< Maximal speed increase between two calls of StepWrite.
\r
232 /** Convert values from A/D convertor to a voltage. */
\r
233 static __inline double MF624_AD2VOLT(signed __int16 x)
\r
235 return 10.0*((signed __int16)(x<<2))/(double)0x8000;
\r
239 /** Convert voltage to a raw values for D/A convertor. */
\r
240 static __inline __int16 MF624_VOLT2DA(double y)
\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
249 #define MASTERFREQUENCY 50000000 ///< Internal oscillator frequency is 50MHz
\r
253 /** Symbolic aliases for available registers inside BADR0. */
\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
265 /** Symbolic aliases for available registers inside BADR1 read. */
\r
273 /** Symbolic aliases for available registers inside BADR1 write. */
\r
281 /** Symbolic aliases for available registers inside BADR2. */
\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
298 #define LOOP_AD_TIMEOUT 0xFFFF ///< timeout per loop for endless AD conversion
\r
302 /** write to memory mapped device word wise. */
\r
303 static __inline void StoreWord(size_t Ptr, int Offset, __int16 value)
\r
305 ((volatile unsigned __int16 *)Ptr)[Offset/2] = value;
\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
312 if (*CachedValue != value)
\r
314 *CachedValue = value;
\r
315 StoreWord(Ptr,Offset,value);
\r
320 /** read from memory mapped device word wise. */
\r
321 static __inline __int16 GetWord(size_t Ptr, int Offset)
\r
323 return ((volatile unsigned __int16 *)Ptr)[Offset/2];
\r
327 /** write to memory mapped device doubleword wise. */
\r
328 static __inline void StoreDword(size_t Ptr, int Offset, __int32 value)
\r
330 ((volatile unsigned __int32 *)Ptr)[Offset/4] = value;
\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
337 if (*CachedValue != value)
\r
339 *CachedValue = value;
\r
340 StoreDword(Ptr,Offset,value);
\r
345 /** read from memory mapped device double word wise. */
\r
346 static __inline __int32 GetDword(size_t Ptr, int Offset)
\r
348 return ((volatile unsigned __int32 *)Ptr)[Offset/4];
\r
352 /****************************************************************
\r
354 * DIGITAL INPUTS & OUTPUTS *
\r
356 ****************************************************************/
\r
359 /** Get data from digital input. */
\r
360 static int MF624DIRead(const DeviceRecord *DevRecord, unsigned channel)
\r
362 if (channel!=0) return 0;
\r
363 return GetWord(DevRecord->DrvRes.Resources.MemResources[1].Base,DIN);
\r
367 /** Write data to digital output. */
\r
368 static HUDAQSTATUS MF624DOWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned value)
\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
377 /** Write one bit to digital output. */
\r
378 static void MF624DOWriteBit(const DeviceRecord *DevRecord, unsigned channel, unsigned bit, int value)
\r
381 if (channel!=0) return;
\r
382 if (bit>=8) return; //there are only 8 bits available in MF624
\r
384 DoutWord = ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache;
\r
385 if (value) DoutWord |= (1<<bit);
\r
386 else DoutWord &= ~(1<<bit);
\r
388 StoreCachedWord(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, DoutWord,
\r
389 & ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );
\r
393 static void MF624DOWriteMultipleBits(const DeviceRecord *DevRecord, unsigned channel, unsigned mask, unsigned value)
\r
396 if (channel!=0) return;
\r
398 DoutWord = ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache;
\r
399 DoutWord = (DoutWord & ~mask) | (value & mask);
\r
401 StoreCachedWord(DevRecord->DrvRes.Resources.MemResources[1].Base, DOUT, DoutWord,
\r
402 & ((MF624_Private *)(DevRecord->DrvRes.DriverData))->DoutCache );
\r
406 static double MF624DIOGetParameter(unsigned channel, HudaqParameter param)
\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
416 case HudaqDONUMCHANNELS:
\r
420 return WRONG_VALUE;
\r
425 /****************************************************************
\r
427 * ANALOG INPUTS & OUTPUTS *
\r
429 ****************************************************************/
\r
432 static HUDAQSTATUS MF624AISetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
434 MF624_Private *Cache;
\r
436 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
437 if (channel>=AD_CHANNELS) return HUDAQBADARG;
\r
443 Cache->AIOptions[channel] |= 1;
\r
445 Cache->AIOptions[channel] &= ~1;
\r
446 return HUDAQSUCCESS;
\r
449 if ((int)value == 0) return HUDAQSUCCESS; // only one range "0" -10:10V is supported
\r
450 return HUDAQBADARG;
\r
453 return HUDAQNOTSUPPORTED;
\r
457 static double MF624AIGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
459 MF624_Private *Cache;
\r
461 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
462 if (channel>=AD_CHANNELS) return WRONG_VALUE;
\r
467 return(Cache->AIOptions[channel] & 1);
\r
470 case HudaqAINUMCHANNELS:
\r
471 return AD_CHANNELS;
\r
474 return WRONG_VALUE;
\r
478 /** Get data from analog input. */
\r
479 static double MF624AIRead(const DeviceRecord *DevRecord, unsigned channel)
\r
483 unsigned TimeoutCounter;
\r
484 MF624_Private *Cache;
\r
486 if (channel>=AD_CHANNELS) return UNDEFINED_VALUE;
\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
495 while(GetDword(Ptr0,GPIOC) & GPIOC_EOLC)
\r
497 if (TimeoutCounter++>LOOP_AD_TIMEOUT)
\r
498 return UNDEFINED_VALUE; /*timeout*/
\r
501 if ((Cache->AIOptions[channel]&1)==1)
\r
502 return GetWord(Ptr1,ADDATA);
\r
504 return MF624_AD2VOLT(GetWord(Ptr1,ADDATA));
\r
508 /** Read multiple data from analog input. */
\r
509 static HUDAQSTATUS MF624AIReadMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned *channels, double *values)
\r
514 unsigned __int16 Mask;
\r
515 unsigned __int16 RawValues[8];
\r
516 unsigned __int32 ReadCache;
\r
517 int RetVal = HUDAQSUCCESS;
\r
519 if (number<=0 || channels==NULL || values==NULL) return HUDAQBADARG;
\r
521 Ptr1 = DevRecord->DrvRes.Resources.MemResources[1].Base;
\r
522 Ptr0 = DevRecord->DrvRes.Resources.MemResources[0].Base;
\r
524 Mask=0; /* Prepare list of channels to convert. */
\r
525 for(i=0;i<number;i++)
\r
527 if (channels[i]<=7)
\r
528 Mask |= 1<<channels[i];
\r
531 StoreCachedWord(Ptr1,ADCTRL,Mask,&(((MF624_Private *)(DevRecord->DrvRes.DriverData))->ADCtrlCache));
\r
532 i = GetWord(Ptr1,ADSTART); /* Start A/D conversion */
\r
534 while(GetDword(Ptr0,GPIOC) & GPIOC_EOLC)
\r
536 if (i++>LOOP_AD_TIMEOUT)
\r
537 return HUDAQFAILURE; /*timeout*/
\r
541 while(i<=7) /* Sort A/D values converted. */
\r
543 if ( (Mask & (1<<i)) != 0)
\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
550 ReadCache = GetDword(Ptr1,ADDATA); /* Extract two values together from A/D FIFO */
\r
551 RawValues[i] = ReadCache; /* feed the first value */
\r
553 while(i<=6) /* Look for second counterpart - compare to 6 because i is incremented below. */
\r
556 if ((Mask & (1<<i)) != 0) /* Second counterpart has been found. */
\r
559 RawValues[i] = ReadCache>>16; /* feed second value */
\r
568 /* Store conversion results into output structure. */
\r
569 for(i=0;i<number;i++)
\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
576 values[i] = MF624_AD2VOLT(RawValues[channels[i]]);
\r
580 values[i] = UNDEFINED_VALUE;
\r
581 RetVal = HUDAQPARTIAL;
\r
589 static double MF624AOGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
591 MF624_Private *Cache;
\r
593 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
594 if (channel>=DA_CHANNELS) return WRONG_VALUE;
\r
599 return(Cache->DA[channel].Options & 1);
\r
600 return HUDAQSUCCESS;
\r
603 case HudaqAONUMCHANNELS:
\r
604 return DA_CHANNELS;
\r
607 return WRONG_VALUE;
\r
611 static HUDAQSTATUS MF624AOSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
613 MF624_Private *Cache;
\r
615 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
616 if (channel>=DA_CHANNELS) return HUDAQBADARG;
\r
622 Cache->DA[channel].Options |= 1;
\r
624 Cache->DA[channel].Options &= ~1;
\r
625 return HUDAQSUCCESS;
\r
628 if ((int)value == 0) return HUDAQSUCCESS; // only one range "0" -10:10V is supported
\r
629 return HUDAQBADARG;
\r
631 return HUDAQNOTSUPPORTED;
\r
635 /** Write data to analog output. */
\r
636 static void MF624AOWrite(const DeviceRecord *DevRecord, unsigned channel, double value)
\r
639 if (channel>=DA_CHANNELS) return;
\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
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
654 MF624_Private *Cache;
\r
655 int RetVal = HUDAQSUCCESS;
\r
656 unsigned __int16 Val2DAC;
\r
657 unsigned char DirtyMask=0;
\r
659 if (number<=0 || channels==NULL || values==NULL) return HUDAQBADARG;
\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
665 /* Decide which channel(s) to write. */
\r
668 if (*channels>=DA_CHANNELS) /* Channel is out of range. */
\r
669 RetVal = HUDAQPARTIAL;
\r
672 if ((Cache->DA[*channels].Options & 1) == 1) /* Use raw value? */
\r
673 Val2DAC = (unsigned __int16)(*values);
\r
675 Val2DAC = (unsigned __int16)MF624_VOLT2DA(*values);
\r
677 if (Val2DAC != Cache->DA[*channels].Cache)
\r
679 DirtyMask |= 1<<*channels;
\r
680 Cache->DA[*channels].Cache = Val2DAC;
\r
687 /* Write all dirty channels to DAC. */
\r
690 StoreDword(Ptr0,GPIOC,GPIOC_BASE|GPIOC_DACEN|GPIOC_LDAC);/*enable DACen + Hold LDAC*/
\r
692 for(number=0;number<DA_CHANNELS;number+=2)
\r
694 if ((DirtyMask & (3<<number)) == (3<<number))
\r
696 DirtyMask &= ~(3<<number);
\r
697 StoreDword(Ptr1, DADATA + number*2, Cache->DA[number].Cache + 0x10000*Cache->DA[number+1].Cache);
\r
701 for(number=0;number<DA_CHANNELS;number++)
\r
703 if (DirtyMask & (1<<number))
\r
705 DirtyMask &= ~(1<<number);
\r
706 StoreWord(Ptr1, DADATA + number*2, Cache->DA[number].Cache);
\r
710 StoreDword(Ptr0,GPIOC,GPIOC_BASE|GPIOC_DACEN); /*enable DACen*/
\r
716 /****************************************************************
\r
720 ****************************************************************/
\r
723 static double MF624EncGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
725 MF624_Private *Cache;
\r
726 IRC_Control *pCTRi;
\r
727 __int8 IrcValue[ENC_CHANNELS];
\r
729 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
730 if (channel>=ENC_CHANNELS) return WRONG_VALUE;
\r
734 case HudaqEncRESETONREAD:
\r
735 return(Cache->EncOptions[channel] & IrcResetCounter);
\r
736 case HudaqEncFILTER:
\r
737 return(Cache->EncCache & (0x80<<(8*channel)));
\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
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
754 return WRONG_VALUE;
\r
758 static HUDAQSTATUS MF624EncSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
761 MF624_Private *Cache;
\r
762 __int8 IrcValue[ENC_CHANNELS];
\r
763 IRC_Control *pCTRi;
\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
771 case HudaqEncRESETONREAD:
\r
773 Cache->EncOptions[channel] |= IrcResetCounter;
\r
775 Cache->EncOptions[channel] &= ~IrcResetCounter;
\r
776 return HUDAQSUCCESS;
\r
778 case HudaqEncFILTER: // 0 is false, any nonzero value expands to true.
\r
780 StoreCachedDword(Ptr2, IRCCTRL, Cache->EncCache | (0x80<<(8*channel)), &Cache->EncCache);
\r
782 StoreCachedDword(Ptr2, IRCCTRL, Cache->EncCache & ~(0x80<<(8*channel)), &Cache->EncCache);
\r
783 return HUDAQSUCCESS;
\r
786 if (value<0 || value>=4) return HUDAQBADARG;
\r
787 *(unsigned __int32 *)&IrcValue = Cache->EncCache;
\r
788 pCTRi = (IRC_Control *)&IrcValue[channel];
\r
790 pCTRi->IRC_MODE = (int)value;
\r
791 StoreCachedDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue, &Cache->EncCache);
\r
792 return HUDAQSUCCESS;
\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
799 pCTRi->RESET_MODE = (int)value;
\r
800 StoreCachedDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue, &Cache->EncCache);
\r
801 return HUDAQSUCCESS;
\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
808 pCTRi->BLOCK_MODE = (int)value;
\r
809 StoreCachedDword(Ptr2, IRCCTRL, *(unsigned __int32 *)&IrcValue, &Cache->EncCache);
\r
810 return HUDAQSUCCESS;
\r
813 return HUDAQNOTSUPPORTED;
\r
817 static HUDAQSTATUS MF624EncReset(const DeviceRecord *DevRecord, unsigned channel)
\r
820 MF624_Private *Cache;
\r
821 __int8 IrcValue[ENC_CHANNELS];
\r
822 IRC_Control *pCTRi;
\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
829 *(unsigned __int32*)&IrcValue = Cache->EncCache;
\r
830 pCTRi = (IRC_Control *)&IrcValue[channel];
\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
841 static int MF624EncRead(const DeviceRecord *DevRecord, unsigned channel)
\r
843 IRC_Control *pCTRi;
\r
845 __int8 IrcValue[ENC_CHANNELS];
\r
847 MF624_Private *Cache;
\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
854 RetVal=GetDword(Ptr2, IRC0 + 4*channel); // read a value from encoder
\r
856 if (Cache->EncOptions[channel] & IrcResetCounter) //RESET counter on every read
\r
858 *(unsigned __int32*)&IrcValue = Cache->EncCache;
\r
859 pCTRi = (IRC_Control *)&IrcValue[channel];
\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
872 /****************************************************************
\r
874 * COUNTERS PWM + Count + Step *
\r
876 ****************************************************************/
\r
878 static HUDAQSTATUS MF624PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)
\r
882 Counter *CacheCtrItem;
\r
884 CounterControl CTC;
\r
886 if (channel>=CTR_CHANNELS) return HUDAQBADARG;
\r
888 CacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];
\r
889 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;
\r
893 if (CacheCtrItem->Mode != OUT_1)
\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
900 return HUDAQSUCCESS;
\r
904 if (CacheCtrItem->Mode != OUT_0)
\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
911 return HUDAQSUCCESS;
\r
914 if (frequency<=0) return HUDAQBADARG; //Allow duty cycle 0 and 1 for f=0.
\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
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
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
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
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
941 if (CacheCtrItem->Mode != PWM)
\r
943 CacheCtrItem->Mode = PWM;
\r
946 StoreDword(Ptr2, CTRXCTRL,
\r
947 (GLOB_CTR_STOP | GLOB_CTR_LOAD | GLOB_CTR_RESET | GLOB_CTR_OUT_RESET)<<(6*channel) );
\r
949 *(unsigned *)&CTC=0;
\r
950 //CTC.ClockSource=0; //50MHz is default.
\r
953 CTC.OutputToggle=1;
\r
956 StoreCachedDword(Ptr2, 0x10*channel,*(unsigned __int32 *)&CTC, (__int32 *)&CacheCtrItem->CCache);
\r
959 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_START)<<(6*channel));
\r
962 if (CacheCtrItem->CtrFixPWM!=0) //Fix relatively big value change
\r
964 T1 = (unsigned __int32)GetDword(Ptr2, 0x10*channel+4);
\r
965 if (T1 > T+CacheCtrItem->CtrFixPWM)
\r
967 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_LOAD)<<(6*channel));
\r
971 return HUDAQSUCCESS;
\r
975 static HUDAQSTATUS MF624CtrReset(const DeviceRecord *DevRecord, unsigned channel)
\r
978 Counter *CacheCtrItem;
\r
979 CounterControl CTC;
\r
981 if (channel>=CTR_CHANNELS) return HUDAQBADARG;
\r
983 CacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];
\r
984 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;
\r
986 //if ((options & IrcNoSwitchMode)==0)
\r
988 if (CacheCtrItem->Mode != Counting)
\r
990 CacheCtrItem->Mode = Counting;
\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
1001 CTC.Direction=1; //Up
\r
1002 // SET counter CWR
\r
1003 StoreCachedDword(Ptr2, 0x10*channel,*(__int32 *)&CTC, (__int32 *)&CacheCtrItem->CCache);
\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
1013 static double MF624CtrGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
1015 Counter *pCacheCtrItem;
\r
1017 if (channel>=CTR_CHANNELS) return WRONG_VALUE;
\r
1018 pCacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];
\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
1054 case HudaqPwmNUMCHANNELS:
\r
1055 case HudaqCtrNUMCHANNELS:
\r
1056 return CTR_CHANNELS;
\r
1058 case HudaqPwmTHRESHOLD:
\r
1059 return(pCacheCtrItem->CtrFixPWM/MASTERFREQUENCY);
\r
1063 return WRONG_VALUE;
\r
1067 static HUDAQSTATUS MF624CtrSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
1069 Counter *pCacheCtrItem;
\r
1071 pCacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];
\r
1072 if (channel>=CTR_CHANNELS) return HUDAQBADARG;
\r
1074 if (pCacheCtrItem->Mode != Counting)
\r
1075 MF624CtrReset(DevRecord,channel); // Enforce switching into a counter mode
\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
1086 pCacheCtrItem->CCache.Repetition = (int)value;
\r
1087 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1088 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1095 pCacheCtrItem->CCache.LoadToggle = (int)value;
\r
1096 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1097 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1104 pCacheCtrItem->CCache.OutputToggle = (int)value;
\r
1105 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1106 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1114 pCacheCtrItem->CCache.ClockSource = (int)value;
\r
1115 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1116 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1124 pCacheCtrItem->CCache.OutputControl = (int)value;
\r
1125 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1126 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1133 pCacheCtrItem->CCache.Direction = (int)value;
\r
1134 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1135 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
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
1146 pCacheCtrItem->CCache.TriggerSource = (int)value;
\r
1147 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1148 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1155 pCacheCtrItem->CCache.TriggerType = (int)value;
\r
1156 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1157 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1164 pCacheCtrItem->CCache.ReTrigger = (int)value;
\r
1165 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1166 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1174 pCacheCtrItem->CCache.GateSource = (int)value;
\r
1175 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1176 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1184 pCacheCtrItem->CCache.GatePolarity = (int)value;
\r
1185 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1186 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\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
1194 pCacheCtrItem->CCache.Filter = (int)value;
\r
1195 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1196 0x10*channel,*(__int32 *)&pCacheCtrItem->CCache);
\r
1198 return HUDAQSUCCESS;
\r
1200 case HudaqPwmTHRESHOLD:
\r
1201 pCacheCtrItem->CtrFixPWM = (value==0)? 0 : MASTERFREQUENCY/value;
\r
1202 return HUDAQSUCCESS;
\r
1205 return HUDAQNOTSUPPORTED;
\r
1209 static int MF624CtrRead(const DeviceRecord *DevRecord, unsigned channel)
\r
1212 Counter *CacheCtrItem;
\r
1215 if (channel>=CTR_CHANNELS) return 0;
\r
1217 CacheCtrItem = &((MF624_Private *)DevRecord->DrvRes.DriverData)->counter[channel];
\r
1218 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;
\r
1220 if (CacheCtrItem->Mode != Counting)
\r
1222 MF624CtrReset(DevRecord,channel);
\r
1226 RetVal = GetDword(Ptr2, 0x10*channel+4);
\r
1228 if (CacheCtrItem->CtrResetCounter) // Reset a counter after each read
\r
1230 StoreDword(Ptr2, CTRXCTRL, (GLOB_CTR_RESET)<<(6*channel));
\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
1246 MF624_Private *Cache;
\r
1247 CounterControl CTC;
\r
1248 unsigned __int32 N;
\r
1250 double NewFrequency;
\r
1251 Stepper *CacheStep;
\r
1253 NewTime=timeGetTime();
\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
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
1264 *(unsigned *)&CTC=0;
\r
1266 CTC.OutputToggle=1;
\r
1267 CTC.GateSource=3; //OUT(n+1)
\r
1268 CTC.GatePolarity=0;
\r
1270 // SET counter CWR
\r
1271 StoreCachedDword(Ptr2, 0x10*(2*channel),*(__int32 *)&CTC, (__int32 *)&Cache->counter[2*channel].CCache);
\r
1273 //set a real frequency
\r
1274 N = (unsigned __int32)(MASTERFREQUENCY / (2*CacheStep->Fmin));
\r
1277 StoreCachedDword(Ptr2, 0x10*(2*channel)+4, N, &Cache->counter[2*channel].ACache);
\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
1285 *(unsigned *)&CTC=0;
\r
1287 CTC.OutputToggle=0;
\r
1288 CTC.ClockSource=10; //Fall N-1
\r
1290 // SET counter CWR
\r
1291 StoreCachedDword(Ptr2, 0x10*(2*channel+1),*(__int32 *)&CTC, (__int32 *)&Cache->counter[2*channel+1].CCache);
\r
1296 if (State!=0) //check for the first time initialization
\r
1298 CacheStep->Direction=0;
\r
1299 CacheStep->LastPosition=0;
\r
1300 CacheStep->ChainedPosition=0;
\r
1301 CacheStep->Frequency=0;
\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
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
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
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
1322 CacheStep->LastPosition -= NewSteps * CacheStep->Direction; //actualize real position
\r
1324 CacheStep->Frequency = 0;
\r
1325 CacheStep->Direction = 0;
\r
1326 CacheStep->TimeStamp = NewTime;
\r
1327 return HUDAQSUCCESS;
\r
1330 //***** actualize step count ******
\r
1332 if (CacheStep->LastPosition!=position)
\r
1333 { //amount of steps to do
\r
1334 if ( (State&3)!=0 ) //the first time initialization
\r
1338 else // counter already runs
\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
1347 CacheStep->Direction=0; //motor is already stopped.
\r
1348 CacheStep->Frequency=0;
\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
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
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
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
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
1384 //CacheStep->LastPosition; does not need to be actualised
\r
1385 CacheStep->Direction=0;
\r
1390 CacheStep->ChainedPosition = position; // chained position makes sense only if direction is reversed
\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
1397 if (CacheStep->Frequency==0)
\r
1399 CacheStep->Frequency = CacheStep->Fmin;
\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
1406 NewFrequency = CacheStep->Frequency + CacheStep->Acc*abs((long)NewTime-(long)CacheStep->TimeStamp)/1000.0;
\r
1408 if (NewFrequency > CacheStep->Fmax) NewFrequency = CacheStep->Fmax;
\r
1409 if (NewFrequency < CacheStep->Fmin) NewFrequency = CacheStep->Fmin;
\r
1411 if (NewFrequency>(CacheStep->Frequency+MAXIMAL_STEP_INCREMENT)) //maximal increment threshold
\r
1413 CacheStep->Frequency += MAXIMAL_STEP_INCREMENT;
\r
1416 CacheStep->Frequency = NewFrequency;
\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
1425 StoreCachedDword(Ptr2, 0x10*(2*channel)+4, N, &Cache->counter[2*channel].ACache);
\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
1432 if ( (State&4) != 0 )
\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
1438 CacheStep->TimeStamp = NewTime; //refresh timestamp
\r
1439 return HUDAQSUCCESS;
\r
1443 static HUDAQSTATUS MF624StepSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
1445 MF624_Private *Cache;
\r
1447 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
1448 if (channel>=STEP_CHANNELS) return HUDAQBADARG;
\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
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
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
1476 return HUDAQNOTSUPPORTED;
\r
1480 static double MF624StepGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
1482 MF624_Private *Cache;
\r
1486 Cache = (MF624_Private *)DevRecord->DrvRes.DriverData;
\r
1487 if (channel>=STEP_CHANNELS) return WRONG_VALUE;
\r
1489 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;
\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
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
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
1511 case HudaqStepTARGETPOSITION:
\r
1512 return(Cache->step[channel].ChainedPosition);
\r
1513 case HudaqStepNUMCHANNELS:
\r
1514 return STEP_CHANNELS;
\r
1517 return WRONG_VALUE;
\r
1521 /****************************************************************
\r
1523 * MF625 specific code *
\r
1525 ****************************************************************/
\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
1530 return MF624CtrRead(DevRecord,channel+4);
\r
1534 static HUDAQSTATUS MF625CtrReset(const DeviceRecord *DevRecord, unsigned channel)
\r
1536 return MF624CtrReset(DevRecord,channel+4);
\r
1540 static double MF625CtrGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
1542 if (channel>=1) return WRONG_VALUE;
\r
1546 case HudaqCtrNUMCHANNELS:
\r
1548 default: return MF624CtrGetParameter(DevRecord,channel+4,param);
\r
1550 return WRONG_VALUE;
\r
1554 static HUDAQSTATUS MF625CtrSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
1556 if (channel>=1) return HUDAQBADARG;
\r
1557 return MF624CtrSetParameter(DevRecord,channel+4,param,value);
\r
1561 ////////////////////////////////////////////////////////////////////////////
\r
1563 #define MAX24BITUINT 0xFFFFFF ///< Maximal range of MF625 counters.
\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
1570 DirtyDIVIDER = 0x100,
\r
1571 PwmSTOPPED = 0x200,
\r
1572 AnotherMODE = 0x400,
\r
1573 NeedRELOAD = 0x800
\r
1575 double T, CounterValue=-1;
\r
1576 PWMMultiple *pCachePWM;
\r
1578 unsigned dirty = 0;
\r
1580 int Timeout = 120; //Timeout for waiting to edge
\r
1582 pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;
\r
1584 Ptr2 = DevRecord->DrvRes.Resources.MemResources[2].Base;
\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
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
1599 switch (pCachePWM->CTR.ClockSource) // convert T to time units
\r
1601 case HudaqCtrCLOCK100kHz:T *= 500.0/MASTERFREQUENCY;
\r
1603 case HudaqCtrCLOCK1MHz: T *= 50.0/MASTERFREQUENCY;
\r
1605 case HudaqCtrCLOCK10MHz: T *= 5.0/MASTERFREQUENCY;
\r
1607 case HudaqCtrCLOCK50MHz: T *= 1.0/MASTERFREQUENCY;
\r
1609 default: T *= 1.0/MASTERFREQUENCY;
\r
1610 Timeout = -1; //Don't wait for external unknown clock.
\r
1613 if (T>=8e-6) break; // more than 8us is sufficient enough for feeding 7 registers.
\r
1615 if (Timeout-- < 0) break;
\r
1616 *(__int32 *)&(pCachePWM->CTR) = GetDword(DevRecord->DrvRes.Resources.MemResources[2].Base, 0x0);
\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
1622 else // A counter is switched in another mode, stop it.
\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
1629 /* Write frequency divider */
\r
1630 if (NewDivider != pCachePWM->Divider)
\r
1632 pCachePWM->Divider = NewDivider;
\r
1633 StoreDword(Ptr2, 4, pCachePWM->Divider);
\r
1634 dirty |= DirtyDIVIDER;
\r
1637 /* Write comparator values for particular channels. */
\r
1638 for(i=0; i<PWM_625_SUBCHANNELS; i++)
\r
1640 if (CompareVals[i]!=pCachePWM->Comparators[i])
\r
1642 StoreDword(Ptr2, 0x10+4*i, CompareVals[i]);
\r
1643 pCachePWM->Comparators[i] = CompareVals[i];
\r
1648 /* Are we doing initialization? */
\r
1649 if (pCachePWM->Mode!=Phase3PWM && pCachePWM->Mode!=Phase6PWM)
\r
1651 if (pCachePWM->Mode==Phase3PWM_prep)
\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
1659 pCachePWM->CTR.Inversions = 0; // don't invert anything
\r
1660 pCachePWM->Mode=Phase6PWM;
\r
1662 pCachePWM->CTR.Filter = 1; // Turn on a filter, for safety. User could turn a filter off by ::SetParameter.
\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
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
1672 if (pCachePWM->CtrFixPWM!=0 && (dirty & 0x100)) //Fix relatively big value change (only when divider changed)
\r
1674 if (CounterValue==-1) CounterValue = (unsigned __int32)GetDword(Ptr2, 0x4);
\r
1675 if (CounterValue > pCachePWM->Divider+pCachePWM->CtrFixPWM)
\r
1676 dirty |= NeedRELOAD;
\r
1679 /* When dirty, shedule update on down->up counter switch. */
\r
1682 StoreDword(Ptr2, 0x0, *(__int32*)(&pCachePWM->CTR) |MF625_SHEDULE_UPDATE|MF625_START
\r
1683 | (((dirty&NeedRELOAD) ==0) ? 0 : MF625_LOAD) );
\r
1686 return HUDAQSUCCESS;
\r
1690 static HUDAQSTATUS MF625PWMMultiphaseWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned phasenum, const unsigned *phases, double frequency, const double *duties)
\r
1692 unsigned CompareVals[6];
\r
1694 PWMMultiple *pCachePWM;
\r
1696 unsigned DefaultChannels[PWM_625_SUBCHANNELS] = {0,1,2,3,4,5};
\r
1698 if (channel>=1) return HUDAQBADARG;
\r
1699 pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;
\r
1703 if (phasenum>PWM_625_SUBCHANNELS) return HUDAQBADARG;
\r
1704 phases=DefaultChannels;
\r
1707 if (frequency<0) return HUDAQBADARG;
\r
1709 /* Check for valid channel numbers. */
\r
1710 for (i=0; i<phasenum; i++)
\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
1717 /* Feed old values. */
\r
1718 for(i=0;i<PWM_625_SUBCHANNELS;i++)
\r
1720 CompareVals[i] = pCachePWM->Comparators[i];
\r
1723 if (pCachePWM->Mode!=Phase6PWM)
\r
1725 pCachePWM->Mode=Phase6PWM_prep;
\r
1726 pCachePWM->CTR.ClockSource=HudaqCtrCLOCK50MHz;
\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
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
1740 if (frequency == 0) T = MAX24BITUINT-2;
\r
1742 T = max((T/frequency/2.0) - 1.5, 0.0);
\r
1743 T = min(T,(double)MAX24BITUINT);
\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
1750 phases++; /* Toss away these arrays. */
\r
1755 return iMF625PWMMultiphaseWrite(DevRecord, (unsigned)T, CompareVals);
\r
1759 HUDAQSTATUS MF625PWM3Write(const DeviceRecord *DevRecord, unsigned channel, double frequency, double duty1, double duty2, double duty3)
\r
1761 PWMMultiple *pCachePWM;
\r
1762 int DeadBandTicks;
\r
1764 unsigned CompareVals[6];
\r
1766 if (channel>=1) return HUDAQBADARG;
\r
1767 pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;
\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
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
1786 if (pCachePWM->Mode!=Phase3PWM)
\r
1788 pCachePWM->Mode=Phase3PWM_prep;
\r
1789 pCachePWM->CTR.ClockSource=HudaqCtrCLOCK50MHz;
\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
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
1803 DeadBandTicks = (int)(T*pCachePWM->DeadBand + 0.5);
\r
1805 if (frequency == 0) T = MAX24BITUINT-2;
\r
1807 T = max((T/frequency/2.0) - 1.5, 0.0);
\r
1808 T = min(T,(double)MAX24BITUINT);
\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
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
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
1821 return iMF625PWMMultiphaseWrite(DevRecord, (unsigned)T, CompareVals);
\r
1825 static HUDAQSTATUS MF625PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)
\r
1827 if (channel>=1) return HUDAQBADARG;
\r
1828 return MF625PWMMultiphaseWrite(DevRecord, 0, 1, &channel, frequency, &dutycycle);
\r
1832 static double MF625PwmGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
1834 PWMMultiple *pCachePWM;
\r
1837 if (channel>=PWM_625_CHANNELS) return WRONG_VALUE;
\r
1838 pCachePWM = &((MF624_Private *)DevRecord->DrvRes.DriverData)->pwm;
\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
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
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
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
1882 case HudaqPwmTHRESHOLD:
\r
1883 return(pCachePWM->CtrFixPWM/MASTERFREQUENCY);
\r
1886 return WRONG_VALUE;
\r
1890 static HUDAQSTATUS MF625PwmSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
1892 PWMMultiple *pCachePWM;
\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
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
1906 case HudaqPwmFILTER:
\r
1907 if (value>=2 || value<0) return HUDAQBADARG;
\r
1908 if (pCachePWM->CTR.Filter != (int)value)
\r
1910 pCachePWM->CTR.Filter = (int)value;
\r
1911 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1912 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1914 return HUDAQSUCCESS;
\r
1916 case HudaqPwmOUTPUTCONTROL:
\r
1917 if (value>=4 || value<0) return HUDAQBADARG;
\r
1918 if (pCachePWM->CTR.OutputControl != (int)value)
\r
1920 pCachePWM->CTR.OutputControl = (int)value;
\r
1921 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1922 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1924 return HUDAQSUCCESS;
\r
1926 case HudaqPwmOUTPUTUDCONTROL:
\r
1927 if (value>=4 || value<0) return HUDAQBADARG;
\r
1928 if (pCachePWM->CTR.OutputUDCtrl != (int)value)
\r
1930 pCachePWM->CTR.OutputUDCtrl = (int)value;
\r
1931 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1932 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1934 return HUDAQSUCCESS;
\r
1936 case HudaqPwmCLOCKSOURCE:
\r
1937 if (value>=16 || value<0) return HUDAQBADARG;
\r
1938 if (pCachePWM->CTR.ClockSource != (int)value)
\r
1940 pCachePWM->CTR.ClockSource = (int)value;
\r
1941 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1942 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1944 return HUDAQSUCCESS;
\r
1946 case HudaqPwmGATEPOLARITY:
\r
1947 if (value>=2 || value<0) return HUDAQBADARG;
\r
1948 if (pCachePWM->CTR.GatePolarity != (int)value)
\r
1950 pCachePWM->CTR.GatePolarity = (int)value;
\r
1951 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1952 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1954 return HUDAQSUCCESS;
\r
1956 case HudaqPwmGATESOURCE:
\r
1957 if (value>=4 || value<0) return HUDAQBADARG;
\r
1958 if (pCachePWM->CTR.GateSource != (int)value)
\r
1960 pCachePWM->CTR.GateSource = (int)value;
\r
1961 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1962 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1964 return HUDAQSUCCESS;
\r
1966 case HudaqPwmTRANSPARENT:
\r
1967 if (value>=2 || value<0) return HUDAQBADARG;
\r
1968 if (pCachePWM->CTR.Transparent != (int)value)
\r
1970 pCachePWM->CTR.Transparent = (int)value;
\r
1971 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1972 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1974 return HUDAQSUCCESS;
\r
1976 case HudaqPwmEMERGENCY:
\r
1977 if (value>=4 || value<0) return HUDAQBADARG;
\r
1978 if (pCachePWM->CTR.Emergency != (int)value)
\r
1980 pCachePWM->CTR.Emergency = (int)value;
\r
1981 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1982 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1984 return HUDAQSUCCESS;
\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
1990 pCachePWM->CTR.Inversions = (int)value;
\r
1991 StoreDword(DevRecord->DrvRes.Resources.MemResources[2].Base,
\r
1992 0x0,*(__int32 *)&pCachePWM->CTR);
\r
1994 return HUDAQSUCCESS;
\r
1996 case HudaqPwmTHRESHOLD:
\r
1997 pCachePWM->CtrFixPWM = (value==0)? 0 : MASTERFREQUENCY/value;
\r
1998 return HUDAQSUCCESS;
\r
2002 return HUDAQNOTSUPPORTED;
\r
2006 /****************************************************************
\r
2008 * GET/SET PARAMETERS *
\r
2010 ****************************************************************/
\r
2012 static HUDAQSTATUS MF624SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
2014 switch(param & HudaqSubsystemMASK)
\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
2022 case HudaqCtr: return MF624CtrSetParameter(DevRecord,channel,param,value);
\r
2023 case HudaqStep:return MF624StepSetParameter(DevRecord,channel,param,value);
\r
2025 return HUDAQNOTSUPPORTED;
\r
2029 static HUDAQSTATUS MF625SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
2031 switch(param & HudaqSubsystemMASK)
\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
2042 return HUDAQNOTSUPPORTED;
\r
2046 static HUDAQSTATUS AD622SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)
\r
2048 switch(param & HudaqSubsystemMASK)
\r
2052 case HudaqAI: return MF624AISetParameter(DevRecord,channel,param,value);
\r
2053 case HudaqAO: return MF624AOSetParameter(DevRecord,channel,param,value);
\r
2058 return HUDAQNOTSUPPORTED;
\r
2063 static double MF624GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
2065 switch(param & HudaqSubsystemMASK)
\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
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
2077 return WRONG_VALUE;
\r
2081 static double MF625GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
2083 switch(param & HudaqSubsystemMASK)
\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
2094 return WRONG_VALUE;
\r
2098 static double AD622GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)
\r
2100 switch(param & HudaqSubsystemMASK)
\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
2111 return WRONG_VALUE;
\r
2115 static const HudaqRange *MF624QueryRange(const DeviceRecord *DevRecord, HudaqSubsystem S, unsigned item)
\r
2117 if (item>=1) return NULL;
\r
2118 switch(S & HudaqSubsystemMASK)
\r
2120 case HudaqAI: return &MF624RangeAIO;
\r
2121 case HudaqAO: return &MF624RangeAIO;
\r
2127 /****************************************************************
\r
2129 * INITIALIZATION *
\r
2131 ****************************************************************/
\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
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
2142 Cache->AIOptions[i] = 0;
\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
2148 StoreWord(Ptr1,DADATA+2*i,0x2000);
\r
2149 Cache->DA[i].Cache = 0x2000;
\r
2150 Cache->DA[i].Options = 0;
\r
2152 StoreDword(Ptr0, GPIOC, GPIOC_BASE|GPIOC_DACEN); //enable DACen
\r
2154 /* Initialization of digital outputs */
\r
2155 Cache->DoutCache = 0;
\r
2156 StoreWord(Ptr1, DOUT, 0);
\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
2165 size_t Ptr1, Ptr2;
\r
2167 MF624_Private *Cache;
\r
2169 if (DevRecord == NULL)
\r
2172 if (DevRecord->DrvRes.Resources.NumMemResources < 3)
\r
2173 return -2; //insufficient amount of resources
\r
2175 Cache = DevRecord->DrvRes.DriverData;
\r
2176 if (Cache == NULL)
\r
2177 { /*NO CACHE FOUND, cache==NULL*/
\r
2180 if (DevRecord->DrvRes.DriverDataSize < sizeof(MF624_Private))
\r
2183 DevRecord->pCT = &CT624;
\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
2190 /* Initialization of AD's, DA's, DI and DO. */
\r
2191 InitAiAoDiDo(DevRecord->DrvRes.Resources.MemResources[0].Base, Ptr1, Cache);
\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
2199 Cache->EncOptions[i] = 0;
\r
2202 /* Initialization of counters */
\r
2203 for(i = 0; i < CTR_CHANNELS; i++)
\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
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
2224 /* Initialization of stepper motors courtesy */
\r
2225 for(i = 0; i < 2; i++)
\r
2227 Cache->step[i].LastPosition = 0;
\r
2228 Cache->step[i].ChainedPosition = 0;
\r
2229 Cache->step[i].Direction = 0;
\r
2231 Cache->step[i].Fmin = 1000;
\r
2232 Cache->step[i].Fmax = 5000;
\r
2233 Cache->step[i].Acc = 1000;
\r
2239 // if (exclusive) return -5; // exclusive access failed, resources.c calls HudaqCloseDevice
\r
2242 return 1; //success
\r
2246 /** Internal cleanup procedure for MF624 & MF625 & AD622 */
\r
2247 static void Done624(DeviceRecord *DevRecord)
\r
2249 if (DevRecord == NULL)
\r
2252 DevRecord->pCT = &CtDummy;
\r
2256 const CallTable CT624 =
\r
2258 "MF624", 0x186C, 0x0624,
\r
2262 MF624SetParameter,
\r
2263 MF624GetParameter,
\r
2266 // INITIALIZE DI callers
\r
2268 GenericDIReadBit, //Generic implementation
\r
2269 GenericOneDIReadMultiple,
\r
2270 // INITIALIZE DO callers
\r
2273 MF624DOWriteMultipleBits,
\r
2274 GenericDOWriteMultiple,
\r
2275 // INITIALIZE AI callers
\r
2277 MF624AIReadMultiple,
\r
2278 // INITIALIZE AO callers
\r
2280 MF624AOWriteMultiple,
\r
2281 // INITIALIZE Enc callers
\r
2284 // INITIALIZE Ctr callers
\r
2287 // INITIALIZE PWM callers
\r
2291 // INITIALIZE Step callers
\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
2300 size_t Ptr1, Ptr2;
\r
2302 MF624_Private *Cache;
\r
2304 if (DevRecord==NULL) return -1;
\r
2306 if (DevRecord->DrvRes.Resources.NumMemResources<3) return -2; //insufficient amount of resources
\r
2307 Cache = DevRecord->DrvRes.DriverData;
\r
2310 //printf("\nNO CACHE FOUND, cache==NULL!");
\r
2313 if (DevRecord->DrvRes.DriverDataSize<sizeof(MF624_Private)) return -4;
\r
2315 DevRecord->pCT = &CT625;
\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
2322 /* Initialization of AD's, DA's, DI and DO. */
\r
2323 InitAiAoDiDo(DevRecord->DrvRes.Resources.MemResources[0].Base,Ptr1,Cache);
\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
2331 Cache->EncOptions[i]=0;
\r
2334 /* Initialization of counters, MF625 supports only counter 4 */
\r
2335 for(i=4;i<CTR_CHANNELS;i++)
\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
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
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
2357 Cache->pwm.Divider = 0;
\r
2358 StoreDword(Ptr2, 0x4, 0);
\r
2360 for(i=0;i<PWM_625_SUBCHANNELS;i++)
\r
2362 Cache->pwm.Comparators[i] = 0;
\r
2363 StoreDword(Ptr2, 0x10+4*i, 0);
\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
2372 return 1; //success
\r
2375 const CallTable CT625 =
\r
2377 "MF625", 0x186C, 0x0625,
\r
2381 MF625SetParameter,
\r
2382 MF625GetParameter,
\r
2385 // INITIALIZE DI callers
\r
2387 GenericDIReadBit, //Generic implementation
\r
2388 GenericOneDIReadMultiple,
\r
2389 // INITIALIZE DO callers
\r
2392 MF624DOWriteMultipleBits,
\r
2393 GenericDOWriteMultiple,
\r
2394 // INITIALIZE AI callers
\r
2396 MF624AIReadMultiple,
\r
2397 // INITIALIZE AO callers
\r
2399 MF624AOWriteMultiple,
\r
2400 // INITIALIZE Enc callers
\r
2403 // INITIALIZE Ctr callers
\r
2406 // INITIALIZE PWM callers
\r
2408 MF625PWMMultiphaseWrite,
\r
2410 // INITIALIZE Step callers
\r
2411 NULL, // Not available
\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
2420 MF624_Private *Cache;
\r
2422 if (DevRecord==NULL) return -1;
\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
2429 DevRecord->pCT = &CT622;
\r
2431 if ((IniOptions & HudaqOpenNOINIT)==0)
\r
2432 { //initialize hardware only if no other application is running
\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
2440 return 1; //success
\r
2444 const CallTable CT622 =
\r
2446 "AD622", 0x186C, 0x0622,
\r
2450 AD622SetParameter,
\r
2451 AD622GetParameter,
\r
2454 // INITIALIZE DI callers
\r
2456 GenericDIReadBit, //Generic implementation
\r
2457 GenericOneDIReadMultiple,
\r
2458 // INITIALIZE DO callers
\r
2461 MF624DOWriteMultipleBits,
\r
2462 GenericDOWriteMultiple,
\r
2463 // INITIALIZE AI callers
\r
2465 MF624AIReadMultiple,
\r
2466 // INITIALIZE AO callers
\r
2468 MF624AOWriteMultiple,
\r
2469 // INITIALIZE Enc callers
\r
2470 NULL, // Not available
\r
2472 // INITIALIZE Ctr callers
\r
2473 NULL, // Not available
\r
2475 // INITIALIZE PWM callers
\r
2476 NULL, // Not available
\r
2479 // INITIALIZE Step callers
\r
2480 NULL, // Not available
\r