/***************************************************************
 * PrADCTRC.cpp
 *
 * This is an implementation of the TRC A/D converter class.
 *
 ****************************************************************/

/*
 * modification history
 *----------------------
 *
 * 10/26/97: K.C. Chang: created.
 * 02/22/03: Weyn Ong  : Version for QNX 6.  I/O permissions must be enabled
 *                       for port access.
 */

#include "PrADCTRC.h"
#include "PrTRCDefn.h"

#include "PrUtility.h"
#include <assert.h>
#include <sys/mman.h>
#include <sys/neutrino.h>

// ADCTRC 8bits
// constructor sets flag
// flag() should be called before using
PrADCTRC::PrADCTRC(int &discreteReg)
: PrDriverIntf( TRC_ADC_CHANNEL_COUNT,TRC_ADC_BIT_COUNT), 
  discreteReg_(discreteReg)
{
    utility_ = new PrUtility;
    flag(zero());

    // Enable permissions for I/O access
    ThreadCtl( _NTO_TCTL_IO, 0 );
    mmap_device_io( TRC_ADDR_SPACE_SIZE, TRC_BASE_ADDR );
}

PrADCTRC::~PrADCTRC()
{
    delete utility_;
}

// hardware reset: calls ResetDiscreteBit()
int PrADCTRC::zero()
{
    if (ResetDiscreteBit() != PR_DRIVER_OK)
      return PR_DRIVER_ERROR;
    
    return PR_DRIVER_OK;
}

// read a channel value to 'volt': calls ReadValue()
int PrADCTRC::value(int channel,Float &volt)
{
    assert(channel>=0 && channel<channelCount());

    volt = ReadValue(channel);

    return PR_DRIVER_OK;
}

// private methods

// select the channel to read
inline int PrADCTRC::SelectMuxChannel(int channel)
{
    assert(channel>=0 && channel<channelCount());

    // mux register is 1 byte

    if (hwIO().output1B(TRC_BASE_ADDR + TRC_ADC_MUX_SELECT, 
			     channel & bitMask()) != channel)
      return PR_DRIVER_ERROR;
	
    return PR_DRIVER_OK;
}

// sets the trigger reg to initiate reading
inline void PrADCTRC::TriggerToRead()
{
    // start register is 1 byte

    hwIO().input1B(TRC_BASE_ADDR + TRC_ADC_START_PULSE);
}

// toggle the reset discrete bit (interrupt)
inline int PrADCTRC::ResetDiscreteBit()
{
    discreteReg_ &= (~TRC_ADC_DISCRETE_MASK);

    // discrete output register is 2 bytes

    if (hwIO().output2B(TRC_BASE_ADDR + TRC_DISCRETE_OUT_REG, 
			discreteReg_ & TRC_DISCRETE_OUT_REG_BITS_MASK) != discreteReg_)
      return PR_DRIVER_ERROR;

    discreteReg_ |= TRC_ADC_DISCRETE_MASK;

    if (hwIO().output2B(TRC_BASE_ADDR + TRC_DISCRETE_OUT_REG, 
			discreteReg_ & TRC_DISCRETE_OUT_REG_BITS_MASK) != discreteReg_)
      return PR_DRIVER_ERROR;

    return PR_DRIVER_OK;
}

// wait for ~128msec while the value settles down
inline void PrADCTRC::WaitForStatusBit()
{
    // status input register is 2 bytes

    while((hwIO().input2B(TRC_BASE_ADDR + TRC_STATUS_IN_REG) & TRC_ADC_STATUS_MASK)!=0)
      {
	  utility_->microBlock(10000); // block for 10 msec
      }
}

// returns the channel value
inline Float PrADCTRC::ReadValue(int channel)
{
    if (SelectMuxChannel(channel) != PR_DRIVER_OK)
      return PR_DRIVER_ERROR;

    if (ResetDiscreteBit() != PR_DRIVER_OK)
      return PR_DRIVER_ERROR;

    TriggerToRead();

    WaitForStatusBit();

    // ADC register is 1 byte

    return ((hwIO().input1B(TRC_BASE_ADDR + TRC_ADC_VALUE) & bitMask()) 
	    * TRC_ADC_VOLT_PER_BIT);
}

