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

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

#include "PrDACTRC.h"
#include "PrTRCDefn.h"

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

// DAC 12bits
// constructor sets flag
// flag() should be called before using
PrDACTRC::PrDACTRC()
: PrDriverIntf( TRC_DAC_CHANNEL_COUNT,TRC_DAC_BIT_COUNT)
{
    const Float offsetVolt[] = TRC_DAC_VOLT_OFFSET;

    offsetVolt_ = new Float[channelCount()];
    for(int i=0;i<channelCount();i++)
      offsetVolt_[i] = offsetVolt[i];
    flag(zero());

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

PrDACTRC::~PrDACTRC()
{
    delete[] offsetVolt_;
}

// hardware reset: calls value() with zero for all channels
int PrDACTRC::zero()
{
    for(int i=0;i<channelCount();i++)
      {
	  Float num=0.0;
	  if (value(i,num) != PR_DRIVER_OK)
	    return PR_DRIVER_ERROR;
      }
   return PR_DRIVER_OK;
}

// write a channel value to 'volt': calls WriteValue()
int PrDACTRC::value(int channel,Float &volt)
{
    assert(channel>=0 && channel<channelCount());

    if (volt<TRC_DAC_VOLT_MIN)
	volt = TRC_DAC_VOLT_MIN;
    else if (volt>TRC_DAC_VOLT_MAX)
	volt = TRC_DAC_VOLT_MAX;

    return WriteValue(ChannelToAddress(channel),VoltsToBits(channel,volt));
}

// private methods

// convert volts to bits with limits
inline unsigned int PrDACTRC::VoltsToBits(int channel, Float volt)
{
    int bits = (int)((volt + offsetVolt_[channel])*TRC_DAC_BITS_PER_VOLT
                     + TRC_DAC_BITS_ZERO);
    if (bits > TRC_DAC_BITS_MIN) 
      bits = TRC_DAC_BITS_MIN;  // bits = 0xFFF
    else if (bits < TRC_DAC_BITS_MAX) 
      bits = TRC_DAC_BITS_MAX;  // bits = 0x000 

    return (unsigned int) bits;
}

// returns the hw address of channel
inline int PrDACTRC::ChannelToAddress(int channel)
{
    return (TRC_BASE_ADDR + TRC_DAC_BASE_ADDR + 2*channel);
}

// writes the bits to the hw address
inline int PrDACTRC::WriteValue(int address,unsigned int bits)
{
    if (hwIO().output2B(address,bits & bitMask()) != bits)
      return PR_DRIVER_ERROR;

    return PR_DRIVER_OK;
}
