add driver abd device support for adcPi
authorHeinz Junkes <junkes@fhi-berlin.mpg.de>
Tue, 19 Jan 2016 10:54:27 +0100
changeset 2 3b3af1ff2783
parent 1 7029db7ac3db
child 3 ec018606c207
add driver abd device support for adcPi
iocBoot/iockstm/st.cmd
kstmApp/Db/adcPi.db
kstmApp/src/Makefile
kstmApp/src/adcPi.dbd
kstmApp/src/devAdcPi.c
kstmApp/src/drvAdcPi.c
kstmApp/src/drvAdcPi.h
--- a/iocBoot/iockstm/st.cmd	Wed Sep 09 18:06:59 2015 +0200
+++ b/iocBoot/iockstm/st.cmd	Tue Jan 19 10:54:27 2016 +0100
@@ -38,6 +38,11 @@
 drvAsynIPPortConfigure(${PGC2_MC_LINK}, ${PGC2_MC_INET}, 0, 0, 0)
 drvAsynIPPortConfigure(${PGC2_PC_LINK}, ${PGC2_PC_INET}, 0, 0, 0)
 
+#drvAsynI2CConfigure( "I2C", "/dev/i2c-1" )
+
+adcPiConfigure ("1", "1")
+var adcPiDebug 0
+
 # Set asyn trace IO format
 #define ASYN_TRACEIO_NODATA 0x0000
 #define ASYN_TRACEIO_ASCII 0x0001
@@ -67,9 +72,14 @@
 dbLoadRecords("db/pgc2.db","PORT=PGC2_MC, P=FHI4KSTM, Q=PGC2_MC")
 dbLoadRecords("db/pgc2.db","PORT=PGC2_PC, P=FHI4KSTM, Q=PGC2_PC")
 
+#dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_0,C=0")
 #dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_1,C=1")
-#dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_2,C=2")
+dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_2,C=2")
 dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_3,C=3")
+#dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_4,C=4")
+#dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_5,C=5")
+#dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_6,C=6")
+#dbLoadRecords("db/adcPi.db","P=FHI4KSTM:ADCPI:CH_7,C=7")
 
 iocInit()
 
--- a/kstmApp/Db/adcPi.db	Wed Sep 09 18:06:59 2015 +0200
+++ b/kstmApp/Db/adcPi.db	Tue Jan 19 10:54:27 2016 +0100
@@ -1,9 +1,10 @@
 record(ai,"$(P)"){
-  field(DTYP,"AdcPi")
+  field(DTYP,"adcPi")
   field(DESC,"ADC Pi")
   field(SCAN,"$(SCAN=1 second)")
-  field(INP,"$(C)")
+  field(INP,"#C1 S$(C) @dumm")
   field(LINR,"LINEAR")
-  field (ESLO, "0.000154")
+  field (EGUF, "1.024")
+  field (EGUL, "-1.024")
   field(EGU,"V")
 }
--- a/kstmApp/src/Makefile	Wed Sep 09 18:06:59 2015 +0200
+++ b/kstmApp/src/Makefile	Tue Jan 19 10:54:27 2016 +0100
@@ -22,7 +22,7 @@
 kstm_DBD += drvAsynIPPort.dbd
 kstm_DBD += stream.dbd
 kstm_DBD += adcPi.dbd
-
+#kstm_DBD += drvAsynI2C.dbd
 
 # Add all the support libraries needed by this IOC
 #kstm_LIBS += xxx 
@@ -38,7 +38,10 @@
 kstm_SRCS += pi_2_dht_read.c
 kstm_SRCS += aSubReadDHT.c
 #kstm_SRCS += dbSubReadADC.c
-kstm_SRCS += devadcpi.c
+#kstm_SRCS += devadcpi.c
+kstm_SRCS += drvAdcPi.c
+kstm_SRCS += devAdcPi.c
+#kstm_SRCS += drvAsynI2C.cpp  
 
 # Build the main IOC entry point on workstation OSs.
 kstm_SRCS_DEFAULT += kstmMain.cpp
--- a/kstmApp/src/adcPi.dbd	Wed Sep 09 18:06:59 2015 +0200
+++ b/kstmApp/src/adcPi.dbd	Tue Jan 19 10:54:27 2016 +0100
@@ -1,1 +1,4 @@
-device(ai,CONSTANT,devAiAdcPi,"AdcPi")
+driver (adcPi)
+variable (adcPiDebug, int)
+registrar (adcPiRegister)
+device (ai, VME_IO, adcPiAi, "adcPi")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/devAdcPi.c	Tue Jan 19 10:54:27 2016 +0100
@@ -0,0 +1,124 @@
+/** @brief device support adcPi
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <devSup.h>
+#include <recGbl.h>
+#include <alarm.h>
+#include <errlog.h>
+#include <aiRecord.h>
+#include <epicsExport.h>
+#include "drvAdcPi.h"
+
+typedef struct {
+	adcPiCard* card;
+	int signal;
+} adcPiAiPrivate;
+
+long adcPiInitRecordAi (aiRecord *record);
+long adcPiReadAi (aiRecord *record);
+long adcPiSpecialLinconvAi (aiRecord *record, int after);
+
+struct {
+	long		number;
+	DEVSUPFUN	report;
+	DEVSUPFUN	init;
+	DEVSUPFUN	init_record;
+	DEVSUPFUN	get_ioint_info;
+	DEVSUPFUN	read_ai;
+	DEVSUPFUN	special_linconv;
+} adcPiAi = {
+	6,
+	NULL,
+	NULL,
+	adcPiInitRecordAi,
+	NULL,
+	adcPiReadAi,
+	adcPiSpecialLinconvAi
+};
+epicsExportAddress (dset, adcPiAi);
+
+/** @brief init record
+ *
+ */
+long adcPiInitRecordAi (aiRecord *record)
+{
+	adcPiAiPrivate *priv;
+	epicsUInt16 initval;
+	adcPiCard* card;
+	int signal;
+	
+	if (record->inp.type != VME_IO) {
+		errlogSevPrintf (errlogFatal, "adcPiInitRecordAi %s: illegal INP link type\n", record->name);
+		return -1;
+	}
+	
+	card = adcPiOpen (record->inp.value.vmeio.card);
+	if (!card) {
+		errlogSevPrintf (errlogFatal, "adcPiInitRecordAi %s: invalid card number %d\n", record->name, record->inp.value.vmeio.card);
+		return S_dev_noDevice;
+	}
+	
+	signal = record->inp.value.vmeio.signal;
+	if (signal < 0 || signal >= ADC_PI_MAX_CHANNEL) {
+		errlogSevPrintf (errlogFatal, "adcPiInitRecordAi %s: invalid signal number %d\n", record->name, signal);
+		return S_dev_badSignalNumber;
+	}
+	
+	priv = (adcPiAiPrivate*) malloc (sizeof (adcPiAiPrivate));
+	if (!priv) {
+		errlogSevPrintf (errlogFatal, "adcPiInitRecordAi %s: out of memory\n", record->name);
+		return S_dev_noMemory;
+	}
+	
+	priv->card = card;
+	priv->signal = signal;
+	record->dpvt = priv;
+	
+	// printf(" priv at @%p [%d]\n", priv, sizeof (adcPiAiPrivate) );
+	
+	adcPiSpecialLinconvAi (record, TRUE);
+	adcPiGet (card, signal, &initval);
+	
+	record->rval = initval;
+	return 0;
+}
+
+long adcPiReadAi (aiRecord *record)
+{
+	adcPiAiPrivate *priv = (adcPiAiPrivate*) record->dpvt;
+	int status;
+	
+	// printf("ReadAi:  priv at @%p [%d]\n", priv, sizeof (adcPiAiPrivate) );
+	
+	if (!priv) {
+		recGblSetSevr (record, UDF_ALARM, INVALID_ALARM);
+		errlogSevPrintf (errlogFatal, "adcPiReadAi %s: record not initialized correctly\n", record->name);
+		return -1;
+	}
+	unsigned short value;
+	status = adcPiGet (priv->card, priv->signal, &value);
+	if (status) {
+		errlogSevPrintf (errlogFatal, "adcPiReadAi %s: adcPiGet failes: error code 0x%x\n", record->name, status);
+		recGblSetSevr (record, READ_ALARM, INVALID_ALARM);
+	}
+	record->rval = value;
+	return status; // 2 do not convert
+}
+
+long adcPiSpecialLinconvAi (aiRecord *record, int after)
+{
+	if (after) {
+		// printf(" In convert ....\n");
+		// Check/Check ...
+		// MAX_CODE_12BIT / 2048 Volt
+		//record->eoff = ( 8191 * record->egul - -8192 * record->eguf) / (8191 - 8192);
+		record->eoff = 0;
+		record->eslo = 0.000250; // 205 uV (record->eguf - record->egul)/ (8191 - 8192);
+			}
+	return 0;
+}
+	
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/drvAdcPi.c	Tue Jan 19 10:54:27 2016 +0100
@@ -0,0 +1,297 @@
+/** @brief driver for Pi ADC card xxx
+ *
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+
+#include <epicsThread.h>
+#include <epicsTime.h>
+#include <epicsTypes.h>
+#include <devLib.h>
+#include <drvSup.h>
+#include <epicsExport.h>
+#include <errlog.h>
+
+#include <iocsh.h>
+
+#include "drvAdcPi.h"
+
+/* crc("adcPi") */
+#define MYMAGIC 1729550187U 
+
+
+
+
+#define MCP3424_NEW_CONV  0x80
+#define MCP3424_CHANNEL_1 0x00
+#define MCP3424_CHANNEL_2 0x20
+#define MCP3424_CHANNEL_3 0x40
+#define MCP3424_CHANNEL_4 0x60
+#define MCP3424_CONT_CONV 0x10
+#define MCP3424_ONE_SHOT_CONV 0x00
+#define MCP3424_12BIT_240SPS 0x00
+#define MCP3424_14BIT_60SPS 0x04
+#define MCP3424_16BIT_15SPS 0x08
+#define MCP3424_18BIT_3_75SPS 0x0c
+#define MCP3424_GAIN_1 0x00
+#define MCP3424_GAIN_2 0x01
+#define MCP3424_GAIN_4 0x02
+#define MCP3424_GAIN_8 0x03
+#define MCP3424_CONT_MASK 0x6f
+ 
+// adcPi definitions, on i2c-bus
+#define ADC_1 		0x68
+#define ADC_2 		0x69
+#define ADC_CHANNEL1	(MCP3424_NEW_CONV | MCP3424_CHANNEL_1 | MCP3424_CONT_CONV | MCP3424_14BIT_60SPS | MCP3424_GAIN_1 )
+#define ADC_CHANNEL2	(MCP3424_NEW_CONV | MCP3424_CHANNEL_2 | MCP3424_CONT_CONV | MCP3424_14BIT_60SPS | MCP3424_GAIN_1 )
+#define ADC_CHANNEL3	(MCP3424_NEW_CONV | MCP3424_CHANNEL_3 | MCP3424_CONT_CONV | MCP3424_14BIT_60SPS | MCP3424_GAIN_1 )
+#define ADC_CHANNEL4	(MCP3424_NEW_CONV | MCP3424_CHANNEL_4 | MCP3424_CONT_CONV | MCP3424_14BIT_60SPS | MCP3424_GAIN_1 )
+
+/** @brief linked list of card structures
+ *
+ */
+struct adcPiCard {
+	epicsUInt32 magic;
+	adcPiCard *next;
+	int cardNumber;
+	unsigned int i2cAddress;
+	char* baseAddress; // not used with i2c
+	unsigned int fh; // file handle
+
+};
+static adcPiCard *firstCard = NULL;
+
+int adcPiDebug = 1;
+epicsExportAddress (int, adcPiDebug);
+
+static long adcPiReport (int detail);
+
+typedef struct {
+	long 		number;
+	DRVSUPFUN	report;
+	DRVSUPFUN	init;
+} drvAdcPi_drvet;
+drvAdcPi_drvet adcPi = {
+	2,
+	adcPiReport,
+	NULL
+};
+
+epicsExportAddress (drvet, adcPi);
+
+static epicsUInt16 getAdc(adcPiCard* card, int signal)
+{
+static __u8  res[4];
+static unsigned int dummy; 
+static __u8 adc, adc_channel;
+static int ret;
+//static unsigned int threadPrio;
+//epicsTimeStamp startTime, endTime;
+//double elapsedTime;
+//static int loopCounter;
+
+	if (signal < 0 || signal > ADC_PI_MAX_CHANNEL) {
+		errMessage (-1, "Signal Number out of range");
+		return 0;
+	}
+	//threadPrio = epicsThreadGetPrioritySelf();
+	//epicsThreadSetPriority( epicsThreadGetIdSelf(),epicsThreadPriorityMin );
+	
+	//printf(" card->fh : %d, signal : %d (prio: %d)\n", card->fh, signal, epicsThreadGetPrioritySelf());
+	
+	switch (signal){
+    	case 0: { adc=ADC_1; adc_channel=ADC_CHANNEL1; }; break;
+		case 1: { adc=ADC_1; adc_channel=ADC_CHANNEL2; }; break;
+		case 2: { adc=ADC_1; adc_channel=ADC_CHANNEL3; }; break;
+		case 3: { adc=ADC_1; adc_channel=ADC_CHANNEL4; }; break;
+		case 4: { adc=ADC_2; adc_channel=ADC_CHANNEL1; }; break;
+		case 5: { adc=ADC_2; adc_channel=ADC_CHANNEL2; }; break;
+		case 6: { adc=ADC_2; adc_channel=ADC_CHANNEL3; }; break;
+		case 7: { adc=ADC_2; adc_channel=ADC_CHANNEL4; }; break;
+		default: { adc=ADC_1; adc_channel=ADC_CHANNEL1; }; break;
+	}
+
+		
+	// choose device
+	if (ioctl(card->fh,I2C_SLAVE,adc) != 0) errMessage (0, "ioctl failed");
+	//if (usleep (50000) < 0) errMessage (0, "usleep failed");
+	//printf ("adc_channel = 0x%02X \n", adc_channel);
+	//epicsTimeGetCurrent(&startTime);
+	if (i2c_smbus_write_byte (card->fh, adc_channel) != 0) errMessage (0, "i2c_smbus_write_byte failed");
+	// wait a little bit for end of conversion
+	if (usleep (50000) < 0) errMessage (0, "usleep failed");
+	// 3 for != 18bit mode
+	//loopCounter = 0;
+	do {
+		ret = i2c_smbus_read_i2c_block_data(card->fh, adc_channel, 3, res);
+		// printf(" res[0] = 0x%02X, res[1] = 0x%02X, res[2] = 0x%02X\n", res[0], res[1], res[2]);
+		if (ret != 3) {
+			errMessage (0, "i2c_smbus_read_i2c_block_data");
+			fprintf (stderr, "i2c_smbus_read_i2c_block_data returns %d bytes != 3\n", ret);
+		}
+		//if (res[2] & 0x80) {
+		//	fprintf (stderr, "i2c_smbus_read_i2c_block_data, output register (0x%02X) not updated yet\n", res[2]);
+		//}
+		//loopCounter++;
+	} while (res[2] & 0x80);
+		
+	//epicsTimeGetCurrent(&endTime);
+	//elapsedTime = epicsTimeDiffInSeconds(&endTime, &startTime);
+	//printf("Elapsed time %f\n", elapsedTime);
+	//printf(" res[0] = 0x%02X, res[1] = 0x%02X, res[2] = 0x%02X, loopCounter = %d\n", res[0], res[1], res[2], loopCounter);
+    
+    
+    dummy = (res[0]<<8|res[1]);
+	if (dummy>=32768) dummy=65536-dummy;
+	//val = dummy * 0.000154;
+	// printf(" dummy = %d, val = %lf\n", dummy, val);
+	//return val;
+	//if (adcPiDebug) printf ("dummy = 0x%x\n", dummy);
+	// epicsThreadSetPriority( epicsThreadGetIdSelf(), threadPrio);
+	return dummy;
+}
+
+
+/** @brief configure function
+ *
+ * The configure function is called from the startup script
+ */
+int adcPiConfigure (int cardNumber, unsigned int i2cAddress)
+{
+	// long status;
+	// volatile void* baseAddress;
+	// char id[6];
+	adcPiCard **pCard;
+	
+	/** @brief Check card number for sanity */
+	if (cardNumber != 1) {
+		fprintf (stderr, "adcPiConfigure: cardNumber %d must be 1\n", cardNumber);
+		return S_dev_noDevice;
+	}
+	
+	/** @brief Find end of card list and check for duplicates */
+	for (pCard = &firstCard; *pCard; pCard=&(*pCard)->next) {
+		if ((*pCard)->cardNumber == cardNumber) {
+			fprintf (stderr, "adcPiConfigure: cardnumber %d already in use\n", cardNumber);
+			return S_dev_identifyOverlap;
+		}
+	} // pCard points to the end of the list
+
+
+    /** @brief Check address for sanity
+     *
+     * We use /dev/i2c-1, should be fixed/solved, so a "1" should be used*/
+	if (i2cAddress != 1) {
+		fprintf (stderr, "adcPiConfigure: i2cAddress %d must be 1\n", i2cAddress);
+		return S_dev_noDevice;
+	}
+
+	// check VMEaddress for sanity
+	
+	// translate address to local address, check for overlap
+	
+	// check that correct hardware is installed, devReadProbe
+	// read i2c?
+	
+	/** @brief create new card structure
+	 *
+	 */
+	 *pCard = malloc (sizeof(adcPiCard));
+	 if (!*pCard) {
+		 fprintf (stderr, "adcPiConfigure: out of memory\n");
+		 return S_dev_noMemory;
+	 }
+	 
+	 (*pCard)->fh = open("/dev/i2c-1", O_RDWR); // "1" hardcoded!
+	 if ((*pCard)->fh <= 0) {
+		fprintf (stderr, "adcPiConfigure: could not open /dev/i2c-1\n");
+		return S_dev_noDevice;
+	}
+
+	(*pCard)->magic = MYMAGIC;
+	(*pCard)->next = NULL;
+	(*pCard)->cardNumber = cardNumber;
+	(*pCard)->i2cAddress = i2cAddress;
+	
+	return 0;
+}
+
+adcPiCard* adcPiOpen (int cardNumber)
+{
+	adcPiCard* card;
+	for (card = firstCard; card; card=card->next)
+		if (card->cardNumber == cardNumber) return card;
+	return NULL;
+}
+
+int adcPiGet (adcPiCard* card, int signal, epicsUInt16* pValue)
+{
+	
+	if (adcPiDebug>1)
+		printf("adcPiGet card %d signal %d (adcPiDebug: %d)\n",
+		  card->cardNumber, signal, adcPiDebug);
+	
+	if (!card) return S_dev_noDevice;
+	if (card->magic != MYMAGIC) return S_dev_wrongDevice;
+		
+	if (signal < 0 || signal > ADC_PI_MAX_CHANNEL) return S_dev_badSignalNumber;
+		
+	*pValue = getAdc(card, signal);
+	
+	if (adcPiDebug>1)
+		printf("adcPiGet card %d signal %d @%p = 0x%4X\n",
+		  card->cardNumber, signal, (void *)&getAdc, (epicsUInt16)*pValue);
+	return 0;
+}
+
+/** @brief report function
+ *
+ */
+static long adcPiReport (int detail)
+{
+	adcPiCard *card;
+	for (card = firstCard; card; card = card->next) {
+		printf ("  card %d at address %p \n", card->cardNumber, card->baseAddress);
+		if (detail >= 1) {
+			int signal;
+			unsigned short value;
+			for (signal = 0; signal < ADC_PI_MAX_CHANNEL; signal++) {
+				value = getAdc(card, signal);
+				printf("     ADC %d = 0x%04x = %+8.4f V\n", signal, value, value * 0.000154); // tobedef
+			}
+		}
+	}
+	return 0;
+}
+
+
+
+
+static const iocshArg adcPiConfigureArg0 = { "cardNumber", iocshArgInt };
+static const iocshArg adcPiConfigureArg1 = { "i2cAddress", iocshArgInt };
+
+static const iocshArg * const adcPiConfigureArgs[] = {
+	&adcPiConfigureArg0,
+	&adcPiConfigureArg1
+};
+static const iocshFuncDef adcPiConfigureDef = { "adcPiConfigure", 2, adcPiConfigureArgs };
+static void adcPiConfigureFunc (const iocshArgBuf *args)
+{
+	adcPiConfigure (args[0].ival, args[1].ival);
+}
+
+static void adcPiRegister ()
+{
+	iocshRegister (&adcPiConfigureDef, adcPiConfigureFunc);
+}
+
+epicsExportRegistrar (adcPiRegister);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/drvAdcPi.h	Tue Jan 19 10:54:27 2016 +0100
@@ -0,0 +1,35 @@
+#ifndef drvAdcPi_h
+#define drvAdcPi_h
+#include <epicsTypes.h>
+#include <devLib.h>
+
+
+#define ADC_PI_MAX_CHANNEL 8
+
+#define MAX_CODE_12BIT 8191
+
+/** @brief Initialize the card  
+ *
+ * Initialize the card(called from startup script)
+ */
+int adcPiConfigure (int cardnumber, unsigned int i2cAddress);
+
+/** @brief card handle
+ *
+ * Define a type for the card handle
+ */
+typedef struct adcPiCard adcPiCard;
+
+/** @brief open card
+ *
+ * Get card handle for card number
+ */
+adcPiCard* adcPiOpen (int cardnumber);
+
+/** @brief get values
+ *
+ * Read values from channels
+ */
+int adcPiGet (adcPiCard* card, int signal, epicsUInt16* pvalue);
+
+#endif
\ No newline at end of file