« Close

Datasheets and User Guides

App Notes

Software & Driver

 

4.3.6 - Timers & Counters

There are eight IOTypes used to write or read timer and counter information:


LJ_ioGET_COUNTER
LJ_ioPUT_COUNTER_ENABLE
LJ_ioGET_COUNTER_ENABLE
LJ_ioPUT_COUNTER_RESET    //Sets flag to reset on next read.

LJ_ioGET_TIMER
LJ_ioPUT_TIMER_VALUE
LJ_ioPUT_TIMER_MODE
LJ_ioGET_TIMER_MODE

In addition to specifying the channel number, the following mode constants are passed in the value parameter when doing a request with the timer mode IOType:


LJ_tmPWM16                //16-bit PWM output
LJ_tmPWM8                 //8-bit PWM output
LJ_tmRISINGEDGES32        //Period input (32-bit, rising edges)
LJ_tmFALLINGEDGES32       //Period input (32-bit, falling edges)
LJ_tmDUTYCYCLE            //Duty cycle input
LJ_tmFIRMCOUNTER          //Firmware counter input
LJ_tmFIRMCOUNTERDEBOUNCE  //Firmware counter input (with debounce)
LJ_tmFREQOUT              //Frequency output
LJ_tmQUAD                 //Quadrature input
LJ_tmTIMERSTOP            //Timer stop input (odd timers only)
LJ_tmSYSTIMERLOW          //System timer low read
LJ_tmSYSTIMERHIGH         //System timer high read
LJ_tmRISINGEDGES16        //Period input (16-bit, rising edges)
LJ_tmFALLINGEDGES16       //Period input (16-bit, falling edges)

The following are special channels, used with the get/put config IOTypes, to configure a parameter that applies to all timers/counters:


LJ_chNUMBER_TIMERS_ENABLED     //0-4
LJ_chTIMER_CLOCK_BASE          //Value constants below
LJ_chTIMER_CLOCK_DIVISOR       //0-255, where 0=256
LJ_chTIMER_COUNTER_PIN_OFFSET  //0-8

With the clock base special channel above, the following constants are passed in the value parameter to select the frequency:


LJ_tc4MHZ       //4 MHz clock base
LJ_tc12MHZ      //12 MHz clock base
LJ_tc48MHZ      //48 MHz clock base
LJ_tc1MHZ_DIV   //1 MHz clock base w/ divisor (no Counter0)
LJ_tc4MHZ_DIV   //4 MHz clock base w/ divisor (no Counter0)
LJ_tc12MHZ_DIV  //12 MHz clock base w/ divisor (no Counter0)
LJ_tc48MHZ_DIV  //48 MHz clock base w/ divisor (no Counter0)
LJ_tcSYS        //Equivalent to LJ_tc48MHZ

Following is example pseudocode for configuring various timers and a hardware counter:


//First, an add/go/get block to configure the timers and counters.

//Set the pin offset to 0, which causes the timers to start on FIO0.
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chTIMER_COUNTER_PIN_OFFSET, 0, 0, 0);

//Enable 2 timers.  They will use FIO0-FIO1
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chNUMBER_TIMERS_ENABLED, 2, 0, 0);

//Make sure Counter0 is disabled.
AddRequest (lngHandle, LJ_ioPUT_COUNTER_ENABLE, 0, 0, 0, 0);

//Enable Counter1.  It will use the next available line, FIO2.
AddRequest (lngHandle, LJ_ioPUT_COUNTER_ENABLE, 1, 1, 0, 0);

//All output timers use the same timer clock, configured here. The
//base clock is set to 48MHZ_DIV, meaning that the clock divisor
//is supported and Counter0 is not available.
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chTIMER_CLOCK_BASE, LJ_tc48MHZ_DIV, 0, 0);

//Set the timer clock divisor to 48, creating a 1 MHz timer clock.
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chTIMER_CLOCK_DIVISOR, 48, 0, 0);

//Configure Timer0 as 8-bit PWM.  It will have a frequency
//of 1M/256 = 3906.25 Hz.
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 0, LJ_tmPWM8, 0, 0);

//Initialize the 8-bit PWM with a 50% duty cycle.
AddRequest (lngHandle, LJ_ioPUT_TIMER_VALUE, 0, 32768, 0, 0);

//Configure Timer1 as duty cycle input.
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 1, LJ_tmDUTYCYCLE, 0, 0);

//Execute the requests.
GoOne (lngHandle);

The following pseudocode demonstrates reading input timers/counters and updating the values of output timers. The simple ePut/eGet functions are used in the following pseudocode, but some applications might combine the following calls into a single add/go/get block so that a single low-level call is used.


//Change Timer0 PWM duty cycle to 25%.
ePut (lngHandle, LJ_ioPUT_TIMER_VALUE, 0, 49152, 0);

//Read duty-cycle from Timer1.
eGet (lngHandle, LJ_ioGET_TIMER, 1, &dblValue, 0);

//The duty cycle read returns a 32-bit value where the
//least significant word (LSW) represents the high time
//and the most significant word (MSW) represents the low
//time.  The times returned are the number of cycles of
//the timer clock.  In this case the timer clock was set
//to 1 MHz, so each cycle is 1 microsecond.
dblHighCycles = (double)(((unsigned long)dblValue) % (65536));
dblLowCycles = (double)(((unsigned long)dblValue) / (65536));
dblDutyCycle = 100 * dblHighCycles / (dblHighCycles + dblLowCycles));
dblHighTime = 0.000001 * dblHighCycles;
dblLowTime = 0.000001 * dblLowCycles;

//Read the count from Counter1.  This is an unsigned 32-bit value.
eGet (lngHandle, LJ_ioGET_COUNTER, 1, &dblValue, 0);

Following is pseudocode to reset the input timer and the counter:


//Reset the duty-cycle measurement (Timer1) to zero, by writing
//a value of zero.  The duty-cycle measurement is continuously
//updated, so a reset is normally not needed, but one reason
//to reset to zero is to detect whether there has been a new
//measurement or not.
ePut (lngHandle, LJ_ioPUT_TIMER_VALUE, 1, 0, 0);

//Read & reset Counter1.  Note that with the U6 reset is just
//setting a driver flag to reset on the next read, so reset
//is generally combined with a read in an add/go/get block.
//The order of the read & reset within the block does not
//matter ... the read will always happen right before the reset.
AddRequest (lngHandle, LJ_ioGET_COUNTER, 1, 0, 0, 0);
AddRequest (lngHandle, LJ_ioPUT_COUNTER_RESET, 1, 1, 0, 0);
GoOne (lngHandle);
GetResult (lngHandle, LJ_ioGET_COUNTER, 1, &dblValue);
GetResult (lngHandle, LJ_ioPUT_COUNTER_RESET, 1, 0);

Note that if a timer/counter is read and reset at the same time (in the same Add/Go/Get block), the read will return the value just before reset.

2 comments

ElectroLund's picture

I believe there's a bug above in the last few lines of sample code:

AddRequest (lngHandle, LJ_ioGET_COUNTER, 1, &dblValue, 0, 0);

The fourth parameter is not a pointer, but a value.  From the LabJackUD.h header description:

"// Value: For output channels, the value to set to."

So in this example, for an input  counter, this value should be zero.

LabJack Support's picture

Thank you for catching that. The call has been corrected.

Note for LJioGET_COUNTER the Value is ignored so technically any value is fine and we generally use zero for a default value to pass.