The class declaration is placed in a .h file and the implementation is placed in a .cpp file. The names of the files follow the pattern rbyydd_name. For example: rdcl_rb8510_dac12.h and rdcl_rb8510_dac12.cpp
These sourcefiles are located in the subdirectory {project-root}/rulbus-rdcl/src/.
Please take a look at the creation of the RB8510_Dac12 driver. The doxygen documentation is omitted for clarity, see section Rulbus Device Class Template of the Style Guide for that.
First we declare the class for the DAC and add inline implementation for the simplest accessor methods.
#include "rdcl/RulbusDevice.h" // classes RulbusDevice, RulbusInterface, RulbusError namespace Rulbus { DECLARE_CLASS( RB8510_Dac12 ); // the 12-bit DAC class type class RB8510_Dac12 : public RulbusDevice { public: // public methods typedef Int Value; RB8510_Dac12( NameCref aName, Addr aAddr = DEF_ADDR, Rack aRack = DEF_RACK, bool aBipolarFlag = DEF_BIP, Real aVoltPerBit = DEF_VPB ); ~RB8510_Dac12(); Volt voltage() const; Value value() const; bool isBipolar() const; float voltperbit() const; void setVoltage ( Volt aVoltage ); void setValue ( Value aValue ); protected: // protected methods RB8510_Dac12(); RB8510_Dac12( RB8510_Dac12Cref rhs ); RB8510_Dac12Ref operator= ( RB8510_Dac12Cref rhs ); public: // public data static const Addr DEF_ADDR = 0xD0; static const Rack DEF_RACK = 0; static const bool DEF_BIP = true; static const Real DEF_VPB /* = 5e-3 */; static const Value LIM_MINVAL = 0x0000; static const Value LIM_MAXVAL = 0x0FFF; static const Value LIM_BIPVAL = 0x0800; private: // private data Value theRegister; bool theBipolarFlag; Real theVoltPerBit; static const int OFF_MSB = 0; static const int OFF_LSB = 1; static const int ADR_WIDTH = OFF_LSB + 1; }; /* the current DAC-register value. */ inline RB8510_Dac12::Value RB8510_Dac12::value() const { return theRegister; } /* the current DAC output voltage. */ inline bool RB8510_Dac12::isBipolar() const { return theBipolarFlag; } /* the DAC's output sensitivity configuration. */ inline Real RB8510_Dac12::voltperbit() const { return theVoltPerBit; } } // namespace Rulbus
The class derives from class RulbusDevice and it contains a constructor to create an object of this class in a defined state, it contains selector methods and modifier methods and it contains state information.
Candidates for constructor parameters are characteristics fixed at production-time, like the Rulbus address and, for the DAC, the ouput polarity (bipolar / unipolar) and sensitivity (volt-per-bit, or vpb).
Candidates for selector and modifier methods are the modifiable characteristics of the Rulbus module, like the output voltage of a DAC.
The datamembers of the class hold the state information for the Rulbus module that it cannot memorize itself. Most Rulbus modules cannot be queried for their settings at all.
Further the class holds constant values we need for the class methods, like register offsets and minimum and maximum values. Constants are declared static const type name.
The constructor above is defined inline. It saves the fixed characteristics polarity and sensitivity to the respective datamembers, delegates the name and address information to the constructor of the RulbusDevice base class and finally sets the DAC ouput voltage to zero volt.
Finally, we define the contents of the non-inline method bodies in the .cpp file, contained in the Rulbus namespace.
namespace Rulbus { // the implementation goes here... }
Data that requires a location or requires initialization in the .cpp file.
const Real RB8510_Dac12::DEF_VPB = 5e-3f; ///< default bipolar, -10.24..+10.23V
The constructor.
RB8510_Dac12::RB8510_Dac12( NameCref aName, Addr aAddr /* = DEF_ADDR */, Rack aRack /* = DEF_RACK */, bool aBipolarFlag /* = DEF_BIP */, Real aVoltPerBit /* = DEF_VPB */) : RulbusDevice ( aName, aAddr, aRack ), theBipolarFlag( aBipolarFlag ), theVoltPerBit ( aVoltPerBit ) { setVoltage( 0 ); }
The destructor.
RB8510_Dac12::~RB8510_Dac12()
{
; // do nothing
}
Method voltage() returns the saved DAC-register value transformed into the voltage.
Volt RB8510_Dac12::voltage() const { Value code = value(); if ( isBipolar() ) code -= LIM_BIPVAL; return voltperbit() * code; }
Method setValue() first checks if the value specified is valid, saves the value in the datamember theRegister and updates the DAC-register. If the value specified is invalid, setValue() throws a RulbusRangeError.
void RB8510_Dac12::setValue( Value aValue ) { if ( aValue < LIM_MINVAL || aValue > LIM_MAXVAL ) throw RulbusRangeError( name(), "value", aValue, LIM_MINVAL, LIM_MAXVAL ); theRegister = aValue; putByte( OFF_MSB, aValue / 256 ); putByte( OFF_LSB, aValue % 256 ); }
Method setVoltage() transforms the specified voltage into a DAC-code and delegates the remaining work to method setValue(). If the voltage specified is not within the capablilities if the DAC, setValue() will throw a RulbusRangeError. setVoltage() catches this error and transforms it into a new RulbusRangeError now specifying the range error in terms of voltage.
void RB8510_Dac12::setVoltage( Volt aVoltage ) { Value code = ( aVoltage < 0 ? -0.5 : +0.5) + aVoltage / voltperbit(); if ( isBipolar() ) code += LIM_BIPVAL; try { setValue( code ); } catch ( RulbusRangeErrorCref e ) { Volt offset = isBipolar() ? -LIM_BIPVAL * voltperbit(): 0; throw RulbusRangeError( name(), "voltage", aVoltage, offset + voltperbit() * LIM_MINVAL, offset + voltperbit() * LIM_MAXVAL ); } ensure( std::fabs( voltage() - aVoltage ) < voltperbit() / 2 + std::numeric_limits<Volt>::epsilon() ); }