/* * asynMasterOscillatorDrv.cpp * * Asyn driver for Hall D master oscillator board. The driver allows to change * the prescale factor and the duty cycle for each channel of the MO board. * * Created on: Sep 14, 2015 * Author: Hovanes Egiyan */ #include "asynMasterOscillatorDrv.hh" #include "moVAsynPar.hh" #include "moAsynPar.hh" using namespace std; // Driver name string asynMasterOscillatorDrv::amodDriverName = "asynMasterOscillatorDrv"; // Number of parameter for MO boards unsigned asynMasterOscillatorDrv::amodParamNumber = 5; // Number of channels on the MO board unsigned asynMasterOscillatorDrv::amodMaxChannels = 10; //// Number of uSeconds for repeating the set requests. //unsigned long asynMasterOscillatorDrv::amodWaitTime = 0; // Open VME window at the loading time volatile static int dummyInt = asynMasterOscillatorDrv::initVME(); // Constructor creates the parameter vector, connects the parameters to the driver, // checks that the board can be accessed. asynMasterOscillatorDrv::asynMasterOscillatorDrv( const string asynPortName, const unsigned baseAddr ) : asynPortDriver( asynPortName.c_str(), amodMaxChannels, amodParamNumber, asynUInt32DigitalMask | asynFloat64Mask | asynDrvUserMask , asynUInt32DigitalMask | asynFloat64Mask , ASYN_CANBLOCK | ASYN_MULTIDEVICE, 1, 0, 0 ), amodExists(false), amodBaseAddr(baseAddr) { cout << "Starting driver for " << asynPortName << " at base address " << amodBaseAddr << endl; initMutex(); string functionName = "asynMasterOscillatorDrv::asynMasterOscillatorDrv"; // uint32_t bAddr = reinterpret_cast( baseAddr ); { MtxLock objLock(mcMutex); this->amodEventID = epicsEventCreate(epicsEventEmpty); } // Uncomment this line to enable asynTraceFlow during the constructor // pasynTrace->setTraceMask( pasynUserSelf, 0x11 ); // pasynTrace->setTraceMask(pasynUserSelf, 0x255); // Initialize the Linux driver for the MO board. // At this time there can only be one MO board per process. // Otherwise the Linux driver by Bryan M. will need to be rewritten, and I would need // to write class for instances of MO boards and VME crate. // At this time we do not have plan to use more than one board per VME crate. uint32_t initFlag = 1<<0; // uint32_t initFlag = 0; moInit( amodBaseAddr, initFlag ) ; cout << "Checking address " << hex << amodBaseAddr<< dec << endl; // Check if there is a board at that base address stringstream ssMessage; if( moTestAccess() != 0 ) { ssMessage << "Bad base address " << baseAddr ; throw ssMessage.str(); } // Print the configuration of the MO board moConfigPrint(); // Bind parameter functions using the address of this object. this->createParVector(); cout << "Will connect the parameters for ASYN port " << asynPortName << endl; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = amodParVec.size(); } cout << "Parameter vector size is " << nPars << endl; // Connect all parameters in the parameter vector to this driver for( unsigned ix = 0; ix < nPars; ix++ ) { moVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = amodParVec[ix]; } cout << "At parameter number " << ix << endl; cout << functionName << ": Connecting parameter " << parPtr->GetName() << endl; parPtr->Connect2Driver(this); usleep(10000); cout << "Parameter " << parPtr->GetName() << " is connected " << endl; } cout << "Launching a thread to periodically read parameter values" << endl; int status = 0; /* Create the thread that reads the values from the registers in the background */ status = (asynStatus)( epicsThreadCreate(portName, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), (EPICSTHREADFUNC) asynMasterOscillatorDrv::threadFunction, this) == NULL); if ( status ) { printf( "%s: epicsThreadCreate failure\n", functionName.c_str() ); ssMessage << "Could not start thread from " << asynMasterOscillatorDrv::getDriverName() << " for asyn port " << portName ; throw ssMessage.str(); } { MtxLock objLock( mcMutex ); amodExists = true; } return; } // Destructor closes the mutexes asynMasterOscillatorDrv::~asynMasterOscillatorDrv() { closeMutex(); } asynStatus asynMasterOscillatorDrv::readUInt32Digital( asynUser* pasynUser, epicsUInt32* value, epicsUInt32 mask ) { string functionName = "asynMasterOscillatorDrv::readUInt32Digital"; // cout << "In function " << functionName << endl; int function = pasynUser->reason; int chanNum; getAddress( pasynUser, &chanNum ); epicsUInt32 tempMask = mask; asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = amodParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { moVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = amodParVec[ix]; } if( function == parPtr->GetNumber() ) { if( asynPortDriver::readUInt32Digital( pasynUser, value, mask ) == asynError ) { return asynError; } aStat = parPtr->Read( this, chanNum, value, &tempMask ); // aStat = asynError; if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d address=%d, function=%d, value=%d\n", asynMasterOscillatorDrv::getDriverName().c_str(), functionName.c_str(), aStat, chanNum, function, *value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: address=%d, function=%d, value=%d\n", asynMasterOscillatorDrv::getDriverName().c_str(), functionName.c_str(), chanNum, function, *value ); callParamCallbacks( chanNum ); return aStat; } } } return aStat; } // Override the method for writing unsigned 32-bit integers asynStatus asynMasterOscillatorDrv::writeUInt32Digital( asynUser* pasynUser, epicsUInt32 value, epicsUInt32 mask ) { int command = pasynUser->reason; int channel; getAddress( pasynUser, &channel ); string functionName = "asynMasterOscillatorDrv::writeUInt32Digital"; // printf( "Now inside %s , command is %d, value for port %s channel %d is %d \n", // functionName.c_str(), command, portName, channel, value ); moVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); if ( !amodExists ) return asynError; parPtr = amodParVec[command]; if( parPtr == 0 ) return asynError; } asynStatus aStat = parPtr->Write( this, static_cast(channel), static_cast(&value), mask ); if ( aStat == asynSuccess ) { // printf("Calling callbacks after setting parameter %d named %s for channel %d \n ", command, parPtr->GetName().c_str(), channel ); aStat = callParamCallbacks( channel ); } return aStat; } asynStatus asynMasterOscillatorDrv::readFloat64( asynUser* pasynUser, epicsFloat64* value ) { string functionName = "asynMasterOscillatorDrv::readFloat64"; int function = pasynUser->reason; int chanNum; getAddress( pasynUser, &chanNum ); asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = amodParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { moVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = amodParVec[ix]; } if( function == parPtr->GetNumber() ) { if( asynPortDriver::readFloat64( pasynUser, value ) == asynError ) { return asynError; } aStat = parPtr->Read( this, chanNum, value ); // aStat = asynError; // cout << "Read value " << value << " for parameter " << parPtr->GetName() << // " for port " << portName << " , channel " << chanNum << endl; if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d address=%d, function=%d, value=%f\n", asynMasterOscillatorDrv::getDriverName().c_str(), functionName.c_str(), aStat, chanNum, function, *value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: address=%d, function=%d, value=%f\n", asynMasterOscillatorDrv::getDriverName().c_str(), functionName.c_str(), chanNum, function, *value ); callParamCallbacks( chanNum ); return aStat; } } } return aStat; } // Override the method for writing 64bit double asynStatus asynMasterOscillatorDrv::writeFloat64( asynUser* pasynUser, epicsFloat64 value ) { int command = pasynUser->reason; int channel; getAddress( pasynUser, &channel ); string functionName = "asynMasterOscillatorDrv::writeFloat64"; // printf( "Now inside %s , command is %d, value for port %s channel %d is %f \n", // functionName.c_str(), command, portName, channel, value ); moVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); if ( !amodExists ) return asynError; parPtr = amodParVec[command]; } asynStatus aStat = parPtr->Write( this, static_cast(channel), static_cast(&value) ); if ( aStat == asynSuccess ) { // printf("Calling callbacks after setting parameter %d named %s for channel %d \n ", command, parPtr->GetName().c_str(), channel ); aStat = callParamCallbacks( channel ); } return aStat; } // Initialize VME windows int asynMasterOscillatorDrv::initVME() { int vmeOpenStat = vmeOpenDefaultWindows(); // if( vmeOpenStat == OK ) { // vmeBusKillLockShm( 1 ); // } return vmeOpenStat; } // This function reads the MO board parameters and calls the callbacks // It is supposed to run in a separate thread for each VME crate. // Because the VME board parameters are read here, we do not need to read // in our new readUInt32Digital function, just call the base version in // case the SCAN field is set to some fixed rate scanning or for initialization. // The values read here will be moved to the appropriate records. void asynMasterOscillatorDrv::readMasterOscillator() { string functionName = "asynMasterOscillatorDrv::readMasterOscillator()"; // cout << "In function " << functionName << endl; asynStatus aStat = asynError; // Set updating time to value in seconds double updateTime = 1.0; while( 1 ) { epicsEventId eventID; { MtxLock objLock( mcMutex ); eventID = this->amodEventID; } epicsEventWaitWithTimeout( eventID, updateTime ); // Loop over all asyn parameters and read them out for( unsigned iChan = 0; iChan < static_cast(this->maxAddr); iChan++ ) { unsigned nPars = 0; { MtxLock objLock( mcMutex ); nPars = amodParVec.size(); } for( unsigned iPar = 0; iPar < nPars; iPar++ ) { moVAsynPar* parPtr = 0; { MtxLock objLock( mcMutex ); parPtr = amodParVec[iPar]; } epicsUInt32 tmpMask = moVAsynPar::GetAllBitMask(); // Create a buffer to hold the values, they will not be longer than uint32_t. uint32_t valueLocation; aStat = parPtr->Read( this, iChan, &valueLocation, &tmpMask ); // Do not do anything with the returned buffer, everything related to // assigning value to the records is taken care in the parPtr->Read function. // Just go ahead and deallocate the buffer without using it. } callParamCallbacks( iChan ); } } } // This function simply calls the member function of the driver whose pointer // is passed as the argument. This function is to be called from a separate // thread and is specified in the argument when launching that thread void asynMasterOscillatorDrv::threadFunction( void *drvPvt ) { asynMasterOscillatorDrv* pPvt = static_cast( drvPvt ); pPvt->readMasterOscillator(); return; } // Method to create the asyn parameter vector for the MO board void asynMasterOscillatorDrv::createParVector() { string functionName = "asynMasterOscillatorDrv::createParVector()"; cout << "In function " << functionName << endl; // Reset { boost::function readFun = boost::bind( &asynMasterOscillatorDrv::getZero, this, 0 ); boost::function writeFun = boost::bind( &asynMasterOscillatorDrv::resetBoard, this, 0, 1 ); moVAsynPar* newPar = new moAsynPar( "RESET", amodParVec.size(), readFun, writeFun ); amodParVec.push_back( newPar ); } // Prescale { boost::function readFun = boost::bind( &asynMasterOscillatorDrv::getPrescale, this, _1 ); boost::function writeFun = boost::bind( &asynMasterOscillatorDrv::setPrescale, this, _1, _2 ); moVAsynPar* newPar = new moAsynPar( "PRESCALE", amodParVec.size(), readFun, writeFun ); amodParVec.push_back( newPar ); } // Initial Prescale { boost::function readFun = boost::bind( &asynMasterOscillatorDrv::getInitialPrescale, this, 0 ); boost::function writeFun = boost::bind( &asynMasterOscillatorDrv::setInitialPrescale, this, 0, _2 ); moVAsynPar* newPar = new moAsynPar( "INITIAL", amodParVec.size(), readFun, writeFun, true ); amodParVec.push_back( newPar ); } // Duty mode { boost::function readFun = boost::bind( &asynMasterOscillatorDrv::getDutyMode, this, _1 ); boost::function writeFun = boost::bind( &asynMasterOscillatorDrv::setDutyMode, this, _1, _2 ); moVAsynPar* newPar = new moAsynPar( "DUTY_MODE", amodParVec.size(), readFun, writeFun ); amodParVec.push_back( newPar ); } // Duty cycle { boost::function readFun = boost::bind( &asynMasterOscillatorDrv::getDutyCycle, this, _1 ); boost::function writeFun = 0; moVAsynPar* newPar = new moAsynPar( "DUTY_CYCLE", amodParVec.size(), readFun, writeFun ); amodParVec.push_back( newPar ); } return; } // Reset the board to initial state with default values unsigned asynMasterOscillatorDrv::resetBoard( unsigned channel, const unsigned prescale ) { MtxLock classLock( mcClassMutex ); // Call moInit to completely reset the board to its default state unsigned initFlagDefault = 0; VMELock vmeLockObject(); int status = moInit( amodBaseAddr, initFlagDefault ); if ( status != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::resetBoard: bad status from moInit for initFlag " << initFlagDefault << endl; throw runtime_error( ssMsg.str() ); } moConfigPrint(); return prescale; } // Get the prescale factor from the driver unsigned asynMasterOscillatorDrv::getPrescale( const unsigned channel ) { MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); // Get the secondary prescale factor uint32_t prescaleFactor; int status = moGetPrescale( channel, &prescaleFactor ); if ( status != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::getPrescale: bad status from moGetPrescale for arguments (channel= " << channel << " , pointer " << hex << showbase << &prescaleFactor << dec; throw runtime_error( ssMsg.str() ); } // Get the initial prescale factor unsigned initialPrescale; if ( moGetInitialPrescale( &initialPrescale ) != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::getPrescale: bad status from moGetInitialPrescale for arguments (channel= " << channel << " , pointer " << hex << showbase << &initialPrescale << dec; throw runtime_error( ssMsg.str() ); } // Return the product of the initial and secondary prescale factors as the total prescale return prescaleFactor * initialPrescale; } // Set the prescale factor using the driver unsigned asynMasterOscillatorDrv::setPrescale( unsigned channel, const unsigned prescale ) { // cout << "Setting prescale to " << prescale << " for " << channel << endl; MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); // First get the initial prescale, supposed to be 16 unsigned initialPrescale; if ( moGetInitialPrescale( &initialPrescale ) != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::setPrescale: bad status from moGetInitialPrescale for arguments (channel= " << channel << " , pointer " << hex << showbase << &initialPrescale << dec; throw runtime_error( ssMsg.str() ); } // Set the secondary prescale factor by dividing the requested value by the initial prescale unsigned prescale4driver = static_cast( round( prescale / initialPrescale ) ); int status = moSetPrescale( channel, prescale4driver ); if ( status != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::setPrescale: bad status from moSetPrescale for arguments (channel= " << channel << " , prescale) " << prescale << " with initial prescale " << initialPrescale; throw runtime_error( ssMsg.str() ); } return prescale; } // Get the initial prescale factor from the driver unsigned asynMasterOscillatorDrv::getInitialPrescale( const unsigned channel ) { MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); // Get the initial prescale factor unsigned initialPrescale; if ( moGetInitialPrescale( &initialPrescale ) != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::getInitialPrescale: bad status from moGetInitialPrescale" << endl; throw runtime_error( ssMsg.str() ); } // Return the product of the initial and secondary prescale factors as the total prescale return initialPrescale; } // Set the initial prescale factor using the driver. the first parameter is dummy, it's just // there to make things look more uniform. unsigned asynMasterOscillatorDrv::setInitialPrescale( unsigned channel, const unsigned initialPrescale ) { MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); // cout << "Setting initial prescale to " << initialPrescale << endl; // First get the initial prescale, supposed to be 16 if ( moSetInitialPrescale( initialPrescale ) != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::setInitialPrescale: bad status from moSetInitialPrescale for argument " << initialPrescale << endl; throw runtime_error( ssMsg.str() ); } // cout << "Initial prescale has been set to " << initialPrescale << endl; return initialPrescale; } // Get the duty mode from the driver unsigned asynMasterOscillatorDrv::getDutyMode( const unsigned channel ) { MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); uint32_t dutyMode; int status = moGetDutyMode( channel, &dutyMode); if( status != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::getDutyMode: bad status from moGetDutyMode for arguments (channel= " << channel << " , pointer " << hex << showbase << &dutyMode << dec ; throw runtime_error( ssMsg.str()); } return dutyMode; } // Set the duty mode using the driver unsigned asynMasterOscillatorDrv::setDutyMode( unsigned channel, const unsigned dutyMode ) { MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); int status = moSetDutyMode( channel, dutyMode ); if ( status != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::setDutyMode: bad status from moSetDutyMode for arguments (channel= " << channel << " , duty mode " << dutyMode; throw runtime_error( ssMsg.str() ); } return dutyMode; } // Get the duty cycle from the driver double asynMasterOscillatorDrv::getDutyCycle( const unsigned channel ) { MtxLock classLock( mcClassMutex ); VMELock vmeLockObject(); float dutyCycle =0; // cout << "Will check the duty cycle for channel " << channel << " value is " << dutyCycle << endl; int status = moGetDutyCycle( channel, &dutyCycle ); if( status != 0 ) { stringstream ssMsg; ssMsg << "asynMasterOscillatorDrv::getDutyCycle: bad status from moGetDutyModefor arguments (channel= " << channel << " , pointer " << hex << showbase << &dutyCycle << dec ; throw runtime_error( ssMsg.str()); } return static_cast(dutyCycle); } /***********************/ /* Definitions for IOC */ /***********************/ extern "C" { // Define IOC function arguments static const iocshArg asynMOConfigArg0 = { "Port Name" , iocshArgString }; static const iocshArg asynMOConfigArg1 = { "Base Address" , iocshArgInt }; static const iocshArg * const asynMOConfigArgs[] = {&asynMOConfigArg0, &asynMOConfigArg1}; // Define IOC functions for MO board char* asynMOConfig( const char *portName, const unsigned baseAddr ) { // Now create the asyn driver cout << "Creating the driver for MO board at base address " << hex << showbase << baseAddr << " with port name " << portName << endl; asynMasterOscillatorDrv* pDrv = new asynMasterOscillatorDrv( portName, baseAddr ); cout << "asynPortDriver object has been created at address " << hex << pDrv << dec << endl; return reinterpret_cast(pDrv); } static const iocshFuncDef asynMOConfigFuncDef = {"asynMOConfig", 2, asynMOConfigArgs }; static void asynMOConfigCallFunc(const iocshArgBuf *args) { asynMOConfig( args[0].sval, args[1].ival ); } void asynMORegister(void) { iocshRegister(&asynMOConfigFuncDef, asynMOConfigCallFunc); } epicsExportRegistrar( asynMORegister ); } // extern "C"