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