629 lines
15 KiB
C
629 lines
15 KiB
C
/*--------------------------------------------------------------------------
|
|
This is a driver for the TDC histogram memory as supplied with the
|
|
Risoe instruments. This system uses a Z80 processor internally.
|
|
Communication is via a GPIB interface. For more information on this
|
|
device consult documentation available from Risoe.
|
|
|
|
The TDC needs a separate counter for controlling the count operation.
|
|
The counter is connected to the TDC and inhibits histogramming when the
|
|
counter is not ready. This is alos why many of the functions in this
|
|
file chain down to a counter.
|
|
|
|
copyright: see file COPYRIGHT
|
|
|
|
Mark Koennecke, February 2003
|
|
--------------------------------------------------------------------------*/
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <fortify.h>
|
|
#include <sics.h>
|
|
#include <countdriv.h>
|
|
#include <counter.h>
|
|
#include <HistMem.h>
|
|
#include <stringdict.h>
|
|
#include <HistDriv.i>
|
|
#include "ecb.h"
|
|
|
|
/*-------------------------- private definitions -----------------------*/
|
|
typedef enum { HMX, HMY, HMXY } TDCModes;
|
|
|
|
#define MAXTDCCHANNELS (128*128)
|
|
|
|
#define COMMERROR -301
|
|
#define MAXEXCEEDED -302
|
|
|
|
/*------------------------- private data structure ---------------------*/
|
|
|
|
typedef struct {
|
|
TDCModes mode;
|
|
int map;
|
|
int bank;
|
|
unsigned char fillByte;
|
|
int range;
|
|
int n;
|
|
int errorCode;
|
|
pCounter counter;
|
|
pECB tdc;
|
|
} Tdc, *pTdc;
|
|
|
|
/*---------------------- Monitor event data structure (from counter.c) */
|
|
typedef struct {
|
|
float fPreset;
|
|
float fCurrent;
|
|
char *pName;
|
|
} MonEvent, *pMonEvent;
|
|
|
|
/*=======================================================================*/
|
|
static int downloadConfiguration(pTdc self)
|
|
{
|
|
int status;
|
|
Z80_reg in, out;
|
|
int function;
|
|
|
|
/*
|
|
map
|
|
*/
|
|
in.d = (unsigned char) self->map;
|
|
status = ecbExecute(self->tdc, 134, in, &out);
|
|
if (status != 1) {
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
mode
|
|
*/
|
|
switch (self->mode) {
|
|
case HMX:
|
|
function = 129;
|
|
break;
|
|
case HMY:
|
|
function = 130;
|
|
break;
|
|
case HMXY:
|
|
function = 128;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
status = ecbExecute(self->tdc, function, in, &out);
|
|
if (status != 1) {
|
|
return status;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int TDCConfigure(pHistDriver self, SConnection * pCon,
|
|
pStringDict pOpt, SicsInterp * pSics)
|
|
{
|
|
pTdc tdc = NULL;
|
|
char pValue[80];
|
|
float value;
|
|
int status;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
status = StringDictGet(pOpt, "mode", pValue, 79);
|
|
assert(status); /* defaults should have been set in driver creation */
|
|
if (strcmp(pValue, "HMX") == 0) {
|
|
tdc->mode = HMX;
|
|
} else if (strcmp(pValue, "HMY") == 0) {
|
|
tdc->mode = HMY;
|
|
} else if (strcmp(pValue, "HMXY") == 0) {
|
|
tdc->mode = HMXY;
|
|
} else {
|
|
SCWrite(pCon, "ERROR: invalid HM mode defaulted to HMXY", eError);
|
|
tdc->mode = HMXY;
|
|
}
|
|
|
|
status = StringDictGetAsNumber(pOpt, "map", &value);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: invalid map value", eError);
|
|
} else {
|
|
tdc->map = (int) rint(value);
|
|
}
|
|
|
|
status = StringDictGetAsNumber(pOpt, "bank", &value);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: invalid bank value", eError);
|
|
} else {
|
|
tdc->bank = (int) rint(value);
|
|
if (tdc->bank != 0 && tdc->bank != 1) {
|
|
SCWrite(pCon, "ERROR: invalid bank value defaulted to 0", eError);
|
|
tdc->bank = 0;
|
|
}
|
|
}
|
|
|
|
status = StringDictGetAsNumber(pOpt, "range", &value);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: invalid range value", eError);
|
|
} else {
|
|
tdc->range = (int) rint(value);
|
|
}
|
|
|
|
status = StringDictGetAsNumber(pOpt, "n", &value);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: invalid n value", eError);
|
|
} else {
|
|
tdc->n = (int) rint(value);
|
|
}
|
|
|
|
status = StringDictGet(pOpt, "counter", pValue, 79);
|
|
/*
|
|
ignore errors here, in order to support operations without counter
|
|
*/
|
|
if (!status) {
|
|
tdc->counter = NULL;
|
|
}
|
|
tdc->counter = FindCommandData(pSics, pValue, "SingleCounter");
|
|
|
|
tdc->fillByte = 0;
|
|
|
|
status = StringDictGet(pOpt, "ecb", pValue, 79);
|
|
assert(status);
|
|
tdc->tdc = FindCommandData(pSics, pValue, "ECB");
|
|
if (tdc->tdc == NULL) {
|
|
SCWrite(pCon, "ERROR: failed to locate ECB system, critical!", eError);
|
|
return 0;
|
|
}
|
|
|
|
status = downloadConfiguration(tdc);
|
|
if (!status) {
|
|
tdc->errorCode = status;
|
|
return 0;
|
|
}
|
|
|
|
|
|
self->iReconfig = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*=======================================================================*/
|
|
static int clearTdc(pTdc self, unsigned char fill)
|
|
{
|
|
Z80_reg in, out;
|
|
|
|
in.b = self->bank;
|
|
in.c = fill;
|
|
in.d = 0;
|
|
return ecbExecute(self->tdc, 133, in, &out);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
static int enableTdc(pTdc self)
|
|
{
|
|
Z80_reg in, out;
|
|
|
|
return 1;
|
|
|
|
/*
|
|
range and n are obscure parameters
|
|
*/
|
|
in.c = 0;
|
|
in.b = self->range;
|
|
in.d = (char) ((self->n >> 8) & 255);
|
|
in.e = (char) (self->n & 255);
|
|
|
|
return ecbExecute(self->tdc, 131, in, &out);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int TDCStart(pHistDriver self, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
tdc->errorCode = 0;
|
|
|
|
status = clearTdc(tdc, 0);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
|
|
status = enableTdc(tdc);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
|
|
/*
|
|
start the counter if available
|
|
*/
|
|
if (tdc->counter != NULL) {
|
|
tdc->counter->pDriv->fPreset = self->fCountPreset;
|
|
tdc->counter->pDriv->eMode = self->eCount;
|
|
InvokeCallBack(tdc->counter->pCall,COUNTSTART,NULL);
|
|
return tdc->counter->pDriv->Start(tdc->counter->pDriv);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int disableTdc(pTdc self)
|
|
{
|
|
Z80_reg in, out;
|
|
|
|
return 1;
|
|
|
|
return ecbExecute(self->tdc, 132, in, &out);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
static int TDCHalt(pHistDriver self)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
tdc->errorCode = 0;
|
|
|
|
if (tdc->counter != NULL) {
|
|
tdc->counter->pDriv->Halt(tdc->counter->pDriv);
|
|
}
|
|
|
|
status = disableTdc(tdc);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int TDCCountStatus(pHistDriver self, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int tdcstatus, status = HWFault;
|
|
float fControl, fOldControl;
|
|
MonEvent sMon;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
if (tdc->counter != NULL) {
|
|
fOldControl = tdc->counter->pDriv->fLastCurrent;
|
|
status =
|
|
tdc->counter->pDriv->GetStatus(tdc->counter->pDriv, &fControl);
|
|
if(fOldControl != fControl) {
|
|
sMon.fCurrent = fControl;
|
|
sMon.fPreset = tdc->counter->pDriv->fPreset;
|
|
sMon.pName = tdc->counter->name;
|
|
InvokeCallBack(tdc->counter->pCall, MONITOR, &sMon);
|
|
}
|
|
}
|
|
/*
|
|
The TDC does not seem to have a means to figure if it is counting or not
|
|
or to do some sort of progress report. So it has to have an associated
|
|
counter in order to stop it at the end.
|
|
*/
|
|
|
|
/*
|
|
This is no proper fix. The proper fix would be to keep the state
|
|
in the private data structure as well and disable the TDC
|
|
on HWNoBeam and enable the TDC again if the
|
|
beam comes on. However, this is done in hardware at SANS-II. So we
|
|
leave it for now.
|
|
*/
|
|
if (status == HWIdle || status == HWFault) {
|
|
if(tdc->counter != NULL){
|
|
tdc->counter->pDriv->ReadValues(tdc->counter->pDriv);
|
|
}
|
|
InvokeCallBack(tdc->counter->pCall,COUNTEND,NULL);
|
|
tdcstatus = disableTdc(tdc);
|
|
if (tdcstatus != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int TDCGetError(pHistDriver self, int *iCode, char *perror,
|
|
int iErrlen)
|
|
{
|
|
pTdc tdc = NULL;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
*iCode = tdc->errorCode;
|
|
switch (tdc->errorCode) {
|
|
case COMMERROR:
|
|
strlcpy(perror, "Communication problem with TDC", iErrlen);
|
|
break;
|
|
case MAXEXCEEDED:
|
|
strlcpy(perror, "Requested to many channels for read", iErrlen);
|
|
break;
|
|
default:
|
|
if (tdc->counter != NULL) {
|
|
tdc->counter->pDriv->GetError(tdc->counter->pDriv,
|
|
iCode, perror, iErrlen);
|
|
} else {
|
|
strlcpy(perror, "Undefined error code", iErrlen);
|
|
}
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int TDCFixIt(pHistDriver self, int iCode)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int result;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
switch (iCode) {
|
|
case COMMERROR:
|
|
ecbClear(tdc->tdc);
|
|
result = COREDO;
|
|
break;
|
|
default:
|
|
if (tdc->counter != NULL) {
|
|
result = tdc->counter->pDriv->TryAndFixIt(tdc->counter->pDriv,
|
|
iCode);
|
|
} else {
|
|
result = COTERM;
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int TDCGetData(pHistDriver self, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
if (tdc->counter != NULL) {
|
|
return tdc->counter->pDriv->ReadValues(tdc->counter->pDriv);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*======================================================================*/
|
|
static long TDCGetMonitor(pHistDriver self, int i, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
if (tdc->counter != NULL) {
|
|
return GetMonitor(tdc->counter, i, pCon);
|
|
}
|
|
return -9999; /* no monitor available */
|
|
}
|
|
|
|
/*======================================================================*/
|
|
static float TDCGetTime(pHistDriver self, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
if (tdc->counter != NULL) {
|
|
return GetCountTime(tdc->counter, pCon);
|
|
}
|
|
return -9999.99; /* no time available */
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int TDCPause(pHistDriver self, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
status = disableTdc(tdc);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
|
|
|
|
if (tdc->counter != NULL) {
|
|
return tdc->counter->pDriv->Pause(tdc->counter->pDriv);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*=====================================================================*/
|
|
static int TDCContinue(pHistDriver self, SConnection * pCon)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
status = enableTdc(tdc);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
|
|
|
|
if (tdc->counter != NULL) {
|
|
return tdc->counter->pDriv->Continue(tdc->counter->pDriv);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*=======================================================================*/
|
|
static int TDCFree(pHistDriver self)
|
|
{
|
|
free(self->pPriv);
|
|
return 1;
|
|
}
|
|
|
|
/*======================================================================*/
|
|
static int TDCSetHistogram(pHistDriver self, SConnection * pCon,
|
|
int i, int iStart, int iEnd, HistInt * pData)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
char pBueffel[256];
|
|
Ecb_pack data;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
sprintf(pBueffel,
|
|
"WARNING: SetHistogram can only fill HM with a fill byte %s",
|
|
"\n using firts value for that");
|
|
SCWrite(pCon, pBueffel, eWarning);
|
|
|
|
data.result = pData[0];
|
|
status = clearTdc(tdc, data.b.byt0);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*======================================================================*/
|
|
static int TDCPreset(pHistDriver self, SConnection * pCon, HistInt iVal)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
char pBueffel[256];
|
|
Ecb_pack data;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
sprintf(pBueffel,
|
|
"WARNING: Preset can only fill HM with a fill byte %s",
|
|
"\n using first value for that");
|
|
SCWrite(pCon, pBueffel, eWarning);
|
|
|
|
data.result = iVal;
|
|
status = clearTdc(tdc, data.b.byt0);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*======================================================================*/
|
|
static int TDCGetHistogram(pHistDriver self, SConnection * pCon,
|
|
int bank, int iStart, int length,
|
|
HistInt * pData)
|
|
{
|
|
pTdc tdc = NULL;
|
|
int status;
|
|
unsigned short address, byteCount;
|
|
|
|
assert(self);
|
|
tdc = (pTdc) self->pPriv;
|
|
assert(tdc);
|
|
|
|
if (length > MAXTDCCHANNELS) {
|
|
tdc->errorCode = MAXEXCEEDED;
|
|
return HWFault;
|
|
}
|
|
|
|
address = (unsigned short) iStart;
|
|
/*
|
|
this is a fix because the TDC cannot send more then 64KB
|
|
*/
|
|
if (length >= 16384) {
|
|
length = 16383;
|
|
}
|
|
byteCount = length * 4;
|
|
status = ecbDMARead(tdc->tdc, address, pData, byteCount);
|
|
if (status != 1) {
|
|
tdc->errorCode = COMMERROR;
|
|
return HWFault;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
pHistDriver MakeTDCHM(pStringDict pOption)
|
|
{
|
|
pHistDriver pNew = NULL;
|
|
pTdc tdc = NULL;
|
|
|
|
/* create the general driver */
|
|
pNew = CreateHistDriver(pOption);
|
|
if (!pNew) {
|
|
return NULL;
|
|
}
|
|
|
|
/* add our options */
|
|
StringDictAddPair(pOption, "ecb", "blub");
|
|
StringDictAddPair(pOption, "range", "0");
|
|
StringDictAddPair(pOption, "n", "0");
|
|
StringDictAddPair(pOption, "bank", "0");
|
|
StringDictAddPair(pOption, "mode", "HMXY");
|
|
StringDictAddPair(pOption, "counter", "Gwendolin");
|
|
StringDictAddPair(pOption, "map", "9");
|
|
|
|
/* initialise our private data structure */
|
|
tdc = (pTdc) malloc(sizeof(Tdc));
|
|
if (tdc == NULL) {
|
|
free(pNew);
|
|
return NULL;
|
|
}
|
|
memset(tdc, 0, sizeof(Tdc));
|
|
tdc->map = 9;
|
|
tdc->mode = HMXY;
|
|
pNew->pPriv = tdc;
|
|
|
|
/* configure all those functions */
|
|
pNew->Configure = TDCConfigure;
|
|
pNew->Start = TDCStart;
|
|
pNew->Halt = TDCHalt;
|
|
pNew->GetCountStatus = TDCCountStatus;
|
|
pNew->GetError = TDCGetError;
|
|
pNew->TryAndFixIt = TDCFixIt;
|
|
pNew->GetData = TDCGetData;
|
|
pNew->GetHistogram = TDCGetHistogram;
|
|
pNew->SetHistogram = TDCSetHistogram;
|
|
pNew->GetMonitor = TDCGetMonitor;
|
|
pNew->GetTime = TDCGetTime;
|
|
pNew->Preset = TDCPreset;
|
|
pNew->SubSample = DefaultSubSample;
|
|
pNew->FreePrivate = TDCFree;
|
|
pNew->Pause = TDCPause;
|
|
pNew->Continue = TDCContinue;
|
|
|
|
return pNew;
|
|
}
|