kstmApp/src/drvAdcPi.c
author Heinz Junkes <junkes@fhi-berlin.mpg.de>
Tue, 19 Jan 2016 10:54:27 +0100
changeset 2 3b3af1ff2783
permissions -rw-r--r--
add driver abd device support for adcPi

/** @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);