--- /dev/null
+/****************************************************************/\r
+/**@file PCD7004.c:\r
+ * Description: API layer for TEDIA PCD-7004 card. *\r
+ * Dependency: Windows 32, Windows 64 or Linux *\r
+ * Copyright 2009-2010 Jaroslav Fojtik *\r
+ ****************************************************************/\r
+\r
+#if defined(_WIN32) || defined(_WIN64)\r
+#include <windows.h>\r
+#else\r
+#include <sys/io.h>\r
+#endif\r
+#include <malloc.h>\r
+#include <math.h>\r
+\r
+#include "hudaqlib.h"\r
+#include "hudaq_internal.h"\r
+\r
+\r
+#define DI_CHANNELS 4 ///< Amount of Digital Outputs\r
+#define DO_CHANNELS 4 ///< Amount of Digital Inputs\r
+#define DA_CHANNELS 0 ///< Amount of Analog Outputs\r
+#define AD_CHANNELS 0 ///< Amount of Analog Inputs\r
+#define ENC_CHANNELS 0 ///< Amount of Encoders\r
+#define CTR_CHANNELS 1 ///< Amount of counters\r
+#define STEP_CHANNELS 0 ///< Amount of steppers\r
+\r
+\r
+#define MASTERFREQUENCY 1000 ///< Internal oscillator frequency is 1kHz\r
+\r
+\r
+/** Cache of PCD-7004 state */\r
+typedef struct\r
+{\r
+ UserDataHeader Hdr; ///< General device setup\r
+\r
+ unsigned __int8 DIOReg[4]; ///< Port configuration register\r
+ unsigned __int8 DIOCfgReg;\r
+ unsigned __int8 IRQCfgReg;\r
+ unsigned __int8 INTEnReg;\r
+ unsigned __int8 TimerReg;\r
+} PCD_7004_Private;\r
+\r
+\r
+/** Symbolic aliases for available registers inside BADR4 read. */\r
+typedef enum\r
+{\r
+ DIOReg0 = 0,\r
+ DIOReg1 = 1, //4\r
+ DIOReg2 = 2, //8,\r
+ DIOReg3 = 3, //0xC,\r
+\r
+ DIOCfgReg = 0x80/4,\r
+\r
+ IRQCfgReg = 0x200/4, // WR only\r
+ IRQStatusReg = 0x200/4, // RD only\r
+ IRQClrReg = 0x204/4, // WR only\r
+\r
+ TimerReg = 0x208/4, // RW\r
+ INTEnReg = 0x20C/4,\r
+\r
+} MEM4IO;\r
+\r
+static __int8 Ports[DI_CHANNELS] = {DIOReg0, DIOReg1, DIOReg2, DIOReg3};\r
+\r
+\r
+/** write to memory mapped device byte wise, bytes are stored on every 4th position. */\r
+static __inline void StoreByte(size_t Ptr, int Offset, unsigned __int8 value)\r
+{\r
+ ((volatile unsigned __int8 *)(Ptr))[4*Offset] = value;\r
+}\r
+\r
+/** write to memory mapped device byte wise through cache. */\r
+static __inline void StoreCachedByte(size_t Ptr, int Offset, unsigned __int8 value, unsigned __int8 *CachedValue)\r
+{\r
+ if (*CachedValue != value)\r
+ {\r
+ *CachedValue = value;\r
+ StoreByte(Ptr,Offset,value);\r
+ }\r
+}\r
+\r
+\r
+/** This inline is used for reading tightly packed OX9162 local configuration registers. */\r
+static __inline unsigned __int8 GetBytePlain(size_t Ptr, int Offset)\r
+{\r
+ return ((volatile unsigned __int8 *)(Ptr))[4*Offset];\r
+}\r
+\r
+\r
+/****************************************************************\r
+ * *\r
+ * DIGITAL INPUTS & OUTPUTS *\r
+ * *\r
+ ****************************************************************/\r
+\r
+\r
+/** Get data from digital input. */\r
+static int PCD7004DIRead(const DeviceRecord *DevRecord, unsigned channel)\r
+{\r
+ if (channel>=DI_CHANNELS) return HUDAQBADARG;\r
+ return GetBytePlain(DevRecord->DrvRes.Resources.MemResources[1].Base,Ports[channel]);\r
+}\r
+\r
+\r
+/** Write data to digital output. */\r
+static HUDAQSTATUS PCD7004DOWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned value)\r
+{\r
+ if (channel>=DO_CHANNELS) return HUDAQBADARG;\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, Ports[channel], value,\r
+ &((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel] );\r
+ return HUDAQSUCCESS;\r
+}\r
+\r
+\r
+static HUDAQSTATUS PCD7004DIReadMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned *channels, unsigned *values)\r
+{\r
+ unsigned FlagX = 0;\r
+ unsigned i;\r
+ size_t Base;\r
+ unsigned __int8 Cache[DI_CHANNELS+1]; //allocate more bytes for padding ind\r
+\r
+ if (number==0 || channels==NULL || values==NULL) return HUDAQBADARG;\r
+ if(DevRecord->pCT->HudaqDIRead==NULL) return HUDAQFAILURE;\r
+\r
+ Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
+\r
+ /* Schedule what to read. */\r
+ for(i=0; i<number; i++)\r
+ {\r
+ if (channels[i]<DI_CHANNELS)\r
+ {\r
+ FlagX |= 1<<(channels[i]);\r
+ }\r
+ else\r
+ {\r
+ values[i] = 0;\r
+ FlagX |= 0x8000;\r
+ }\r
+ }\r
+\r
+ /* Read isolated DIs */\r
+ for(i=0; i<DI_CHANNELS; i++)\r
+ {\r
+ if(FlagX & (1<<i))\r
+ {\r
+ Cache[i] = GetBytePlain(Base,Ports[i]);\r
+ }\r
+ } \r
+\r
+ /* Final store of numbers read. */\r
+ for(i=0; i<number; i++)\r
+ {\r
+ if(channels[i]<DI_CHANNELS)\r
+ {\r
+ values[i] = Cache[channels[i]];\r
+ }\r
+ } \r
+\r
+ if(FlagX == 0x8000) return HUDAQFAILURE; //no valid value returned\r
+ if(FlagX & 0x8000) return HUDAQPARTIAL; //several values are valid and several are not\r
+ return HUDAQSUCCESS; //all values are valid (or no channel asked)\r
+}\r
+\r
+\r
+static HUDAQSTATUS PCD7004DOWriteMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned *channels, const unsigned *values)\r
+{\r
+ unsigned FlagX = 0;\r
+ unsigned i;\r
+ size_t Base;\r
+ unsigned __int8 *Cache;\r
+\r
+ if (number==0 || channels==NULL || values==NULL) return HUDAQBADARG;\r
+ if(DevRecord->pCT->HudaqDIRead==NULL) return HUDAQFAILURE;\r
+\r
+ Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
+ Cache = ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg;\r
+\r
+ /* schedule what to write */\r
+ for(i=0; i<number; i++)\r
+ {\r
+ if (channels[i]<DI_CHANNELS)\r
+ {\r
+ if(Cache[i] != values[i])\r
+ {\r
+ FlagX |= 1<<(channels[i]);\r
+ Cache[i] = values[i];\r
+ }\r
+ }\r
+ else\r
+ {\r
+ FlagX |= 0x8000;\r
+ }\r
+ }\r
+\r
+ /* Final store of DOs */\r
+ for(i=0; i<DI_CHANNELS; i++)\r
+ {\r
+ if(FlagX & (1<<i))\r
+ {\r
+ StoreByte(Base, Ports[i], Cache[i]);\r
+ }\r
+ } \r
+\r
+ if(FlagX == 0x8000) return HUDAQFAILURE; //no valid value returned\r
+ if(FlagX & 0x8000) return HUDAQPARTIAL; //several values are valid and several are not\r
+ return HUDAQSUCCESS; //all values are valid (or no channel asked)\r
+}\r
+\r
+\r
+/** Write one bit to digital output. */\r
+static void PCD7004DOWriteBit(const DeviceRecord *DevRecord, unsigned channel, unsigned bit, int value)\r
+{\r
+ __int8 DoutByte;\r
+\r
+ if (channel>=DO_CHANNELS) return;\r
+ if (bit>=8) return; /* There are only 8 bits available in PCD7004. */\r
+\r
+ DoutByte = (((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel]);\r
+ if (value) DoutByte |= (1<<bit);\r
+ else DoutByte &= ~(1<<bit);\r
+\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, Ports[channel], DoutByte,\r
+ & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel]);\r
+}\r
+\r
+\r
+static void PCD7004DOWriteMultipleBits(const DeviceRecord *DevRecord, unsigned channel, unsigned mask, unsigned value)\r
+{\r
+ __int8 DoutByte;\r
+\r
+ if (channel>=DO_CHANNELS) return;\r
+\r
+ DoutByte = (((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel]);\r
+ DoutByte = (DoutByte & ~mask) | (value & mask);\r
+\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, Ports[channel], DoutByte,\r
+ & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel] );\r
+}\r
+\r
+\r
+static double PCD7004DIOGetParameter(unsigned channel, HudaqParameter param)\r
+{\r
+ switch((int)param)\r
+ {\r
+ case HudaqDINUMBITS:\r
+ return (channel<DI_CHANNELS) ? 8 : WRONG_VALUE;\r
+ case HudaqDONUMBITS:\r
+ return (channel<DO_CHANNELS) ? 8 : WRONG_VALUE;\r
+ case HudaqDINUMCHANNELS:\r
+ return DI_CHANNELS;\r
+ case HudaqDONUMCHANNELS:\r
+ return DO_CHANNELS;\r
+ }\r
+\r
+ return WRONG_VALUE;\r
+}\r
+\r
+\r
+static HUDAQSTATUS PCD7004DIOSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, int value)\r
+{\r
+ unsigned __int8 chval;\r
+\r
+ switch((int)param)\r
+ {\r
+ case HudaqDINUMBITS:\r
+ if(channel>=DI_CHANNELS) return WRONG_VALUE;\r
+ return (value==8) ? 0 : WRONG_VALUE;\r
+ case HudaqDONUMBITS:\r
+ if(channel>=DI_CHANNELS) return WRONG_VALUE;\r
+ return (value==8) ? 0 : WRONG_VALUE;\r
+ case HudaqDINUMCHANNELS:\r
+ return DI_CHANNELS;\r
+ case HudaqDONUMCHANNELS:\r
+ return DO_CHANNELS;\r
+ case HudaqDOMODE:\r
+ if(channel>=DO_CHANNELS) return WRONG_VALUE;\r
+ chval = ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOCfgReg;\r
+ //printf("chval = %d\n",chval); \r
+\r
+ switch(channel)\r
+ {\r
+ case 0: if(value==1) chval &= ~0x1;\r
+ if(value==0) chval |= 0x1;\r
+ break;\r
+ case 1: if(value==1) chval &= ~0x02;\r
+ if(value==0) chval |= 0x02;\r
+ break;\r
+ case 2: if(value==1) chval &= ~0x04;\r
+ if(value==0) chval |= 0x04;\r
+ break;\r
+ case 3: if(value==1) chval &= ~0x08;\r
+ if(value==0) chval |= 0x08;\r
+ break;\r
+ }\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, DIOCfgReg, chval,\r
+ & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOCfgReg );\r
+ return HUDAQSUCCESS;\r
+ }\r
+\r
+ return WRONG_VALUE;\r
+}\r
+\r
+\r
+/****************************************************************\r
+ * *\r
+ * COUNTERS PWM + Count + Step *\r
+ * *\r
+ ****************************************************************/\r
+\r
+static HUDAQSTATUS PCD7004PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)\r
+{\r
+ double T;\r
+ HUDAQSTATUS ret = HUDAQSUCCESS;\r
+\r
+ if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
+\r
+ if (frequency<=0) return HUDAQBADARG; //Allow duty cycle 0 and 1 for f=0.\r
+\r
+ T = MASTERFREQUENCY/frequency;\r
+ if(T<1) \r
+ {\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, 1,\r
+ & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
+ return HUDAQPARTIAL;\r
+ }\r
+ if(T>255)\r
+ {\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, 255,\r
+ & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
+ return HUDAQPARTIAL;\r
+ }\r
+\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, \r
+ (unsigned __int8)T,\r
+ &((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
+\r
+ return HUDAQSUCCESS;\r
+}\r
+\r
+\r
+static HUDAQSTATUS PCD7004CtrReset(const DeviceRecord *DevRecord, unsigned channel)\r
+{\r
+ if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, \r
+ 0, &((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
+ return HUDAQSUCCESS;\r
+}\r
+\r
+\r
+static int PCD7004CtrRead(const DeviceRecord *DevRecord, unsigned channel)\r
+{\r
+ if (channel>=CTR_CHANNELS) return 0;\r
+ return GetBytePlain(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+/********************************************************************/\r
+\r
+/* Initialization procedure for PCD-7004 card. */\r
+static int InitPCD7004(DeviceRecord *DevRecord, int IniOptions)\r
+{\r
+ PCD_7004_Private *Cache;\r
+ int i;\r
+ size_t Base;\r
+\r
+ if (DevRecord==NULL) return -1;\r
+\r
+ if (DevRecord->DrvRes.Resources.NumMemResources<2) return -2; //insufficient amount of resources\r
+ Cache = DevRecord->DrvRes.DriverData;\r
+ if (Cache==NULL) return -3;\r
+ if (DevRecord->DrvRes.DriverDataSize<sizeof(PCD_7004_Private)) return -4;\r
+\r
+ DevRecord->pCT = &CTPCD7004;\r
+\r
+ if ((IniOptions & HudaqOpenNOINIT)==0)\r
+ { //initialize hardware only if no other application is running\r
+ Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
+\r
+ /* disable interrupts */\r
+ Cache->INTEnReg = 0;\r
+ StoreByte(Base, INTEnReg, 0);\r
+\r
+ /* Initialization of DI and DO. */\r
+ for(i=0; i<4; i++)\r
+ {\r
+ Cache->DIOReg[i] = 0;\r
+ StoreByte(Base, Ports[i], 0);\r
+ }\r
+\r
+ Cache->DIOCfgReg = 0;\r
+ StoreByte(Base, DIOCfgReg, 0);\r
+\r
+ Cache->IRQCfgReg = 0;\r
+ StoreByte(Base, IRQCfgReg, 0);\r
+\r
+ Cache->TimerReg = 0;\r
+ StoreByte(Base, TimerReg, 0); /* Stop timer. */\r
+ }\r
+\r
+ return 1; //success\r
+}\r
+\r
+\r
+/** Internal cleanup procedure for PCD7004 */\r
+static void DonePCD7004(DeviceRecord *DevRecord)\r
+{\r
+ if (DevRecord==NULL) return;\r
+\r
+ DevRecord->pCT = &CtDummy;\r
+}\r
+\r
+\r
+\r
+/****************************************************************\r
+ * *\r
+ * GET/SET PARAMETERS *\r
+ * *\r
+ ****************************************************************/\r
+\r
+\r
+static HUDAQSTATUS PCD7004IRQSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, int value)\r
+{\r
+ size_t Base;\r
+\r
+ Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
+\r
+ switch((int)param)\r
+ {\r
+ case HudaqIRQ+0:\r
+ {\r
+ if(value)\r
+ {\r
+ StoreCachedByte(Base, IRQCfgReg, 1, &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->IRQCfgReg);\r
+ StoreByte(Base, IRQClrReg, 0xFF);\r
+ StoreCachedByte(Base, INTEnReg, 0x80, &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->INTEnReg);\r
+ }\r
+ else\r
+ { \r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, INTEnReg, 0,\r
+ &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->INTEnReg);\r
+ StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, IRQCfgReg, 0,\r
+ &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->IRQCfgReg);\r
+ }\r
+ return HUDAQSUCCESS;\r
+ }\r
+ case HudaqIRQ+1:\r
+ return HUDAQSUCCESS;\r
+ }\r
+\r
+ return WRONG_VALUE;\r
+}\r
+\r
+\r
+static double PCD7004IRQGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
+{\r
+ switch((int)param)\r
+ {\r
+ case HudaqIRQ+0: \r
+ return ((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->Hdr.IRQcounter;\r
+ case HudaqIRQ+1:\r
+ return GetBytePlain(DevRecord->DrvRes.Resources.MemResources[1].Base,IRQStatusReg);\r
+ }\r
+ return WRONG_VALUE;\r
+}\r
+\r
+\r
+static HUDAQSTATUS PCD7004SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
+{\r
+ switch(param & HudaqSubsystemMASK)\r
+ {\r
+ case HudaqDI:\r
+ case HudaqDO: return PCD7004DIOSetParameter(DevRecord,channel,param,(int)value);\r
+ // case HudaqAI: \r
+ // case HudaqAO: \r
+ // case HudaqEnc: \r
+ // case HudaqPWM:\r
+ // case HudaqCtr: \r
+ // case HudaqStep:\r
+ case HudaqIRQ: return PCD7004IRQSetParameter(DevRecord,channel,param,(int)value);\r
+ }\r
+ return HUDAQNOTSUPPORTED;\r
+}\r
+\r
+static double PCD7004GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
+{\r
+ switch(param & HudaqSubsystemMASK)\r
+ {\r
+ case HudaqDI:\r
+ case HudaqDO: return PCD7004DIOGetParameter(channel,param);\r
+ //case HudaqAI: \r
+ //case HudaqAO: \r
+ //case HudaqEnc:\r
+ //case HudaqPWM:\r
+ //case HudaqCtr:\r
+ //case HudaqStep:\r
+ case HudaqIRQ: return PCD7004IRQGetParameter(DevRecord,channel,param);\r
+ }\r
+ return WRONG_VALUE;\r
+}\r
+\r
+\r
+const CallTable CTPCD7004 =\r
+{\r
+ "PCD7004", 0x1760, 0x0101,\r
+ InitPCD7004,\r
+ DonePCD7004,\r
+\r
+ PCD7004SetParameter,\r
+ PCD7004GetParameter,\r
+ NULL,\r
+\r
+ // INITIALIZE DI callers\r
+ PCD7004DIRead,\r
+ GenericDIReadBit, //Generic implementation\r
+ PCD7004DIReadMultiple,\r
+ // INITIALIZE DO callers\r
+ PCD7004DOWrite,\r
+ PCD7004DOWriteBit,\r
+ PCD7004DOWriteMultipleBits,\r
+ PCD7004DOWriteMultiple,\r
+ // INITIALIZE AI callers\r
+ NULL,\r
+ NULL,\r
+ // INITIALIZE AO callers\r
+ NULL,\r
+ NULL,\r
+ // INITIALIZE Enc callers\r
+ NULL, // Not available\r
+ NULL,\r
+ // INITIALIZE Ctr callers\r
+ PCD7004CtrRead,\r
+ PCD7004CtrReset,\r
+ // INITIALIZE PWM callers\r
+ PCD7004PWMWrite, \r
+ NULL, // Not available\r
+ NULL,\r
+ // INITIALIZE Step callers\r
+ NULL, // Not available\r
+};\r
+\r