4.1.6 - Timers & Counters [UE9 Datasheet]
All timer/counter feature configurations should be set with a single transaction using using eTCConfig; AddRequest, Go/GoOne, and GetResult/GetNextResult; or eAddGoGet. Timer/counter data can be obtained using any of AddRequest, Go/GoOne, and GetResult/GetNextResult; or eAddGoGet. In applicable functions such as eGet/ePut, the x1 parameter is unused for timers/counters, so it can be set to 0.
There are eight IOTypes used to write or read timer and counter information:
LJ_ioGET_COUNTER
LJ_ioPUT_COUNTER_ENABLE //UpdateConfig will be set.
LJ_ioGET_COUNTER_ENABLE
LJ_ioPUT_COUNTER_RESET
LJ_ioGET_TIMER
LJ_ioPUT_TIMER_VALUE
LJ_ioPUT_TIMER_MODE //UpdateConfig will be set.
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 //UpdateConfig will be set if writing.
LJ_chTIMER_CLOCK_BASE //UpdateConfig will be set if writing.
LJ_chTIMER_CLOCK_DIVISOR //UpdateConfig will be set if writing.
With the clock base special channel above, the following constants are passed in the value parameter to select the frequency:
LJ_tc750KHZ //Fixed 750 kHz clock base.
LJ_tcSYS //System clock: 48 MHz.
Following is example pseudocode for configuring various timers and a hardware counter:
//First, an add/go/get block to configure the timers and counters.
//Enable all 6 timers. Timer0-Timer5 will appear on FIO0-FIO5.
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chNUMBER_TIMERS_ENABLED, 6, 0, 0);
//Enable Counter0. It will use the next available line, FIO6.
AddRequest (lngHandle, LJ_ioPUT_COUNTER_ENABLE, 0, 1, 0, 0);
//All output timers use the same timer clock, which is
//determined by the base clock divided by the clock divisor.
//Set the timer clock base to 48 MHz.
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chTIMER_CLOCK_BASE, LJ_tcSYS, 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 frequency output.
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 1, LJ_tmFREQOUT, 0, 0);
//Initialize frequency output at 1M/(2*5) = 100 kHz.
AddRequest (lngHandle, LJ_ioPUT_TIMER_VALUE, 1, 5, 0, 0);
//Configure Timer2 as a firmware counter with debounce.
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 2, LJ_tmFIRMCOUNTERDEBOUNCE, 0, 0);
//Configure Timer2 for negative edges (bit 8 of value clear) with
//a debounce period of 87 ms.
AddRequest (lngHandle, LJ_ioPUT_TIMER_VALUE, 2, 1, 0, 0);
//Configure Timer3 as duty cycle input.
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 3, LJ_tmDUTYCYCLE, 0, 0);
//Configure Timers 4 & 5 as quadrature input. Two timers
//are needed for phases A & B.
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 4, LJ_tmQUAD, 0, 0);
AddRequest (lngHandle, LJ_ioPUT_TIMER_MODE, 5, LJ_tmQUAD, 0, 0);
//Execute the requests.
GoOne (lngHandle);
The LabJackUD driver uses the low-level TimerCounter function. That function has a single UpdateConfig bit that must be set to change modes, clock configuration, or enabled/disabled status. When the UpdateConfig bit is set, all timers and counters are re-initialized. The following pseudocode demonstrates reading input timers/counters and updating the values of output timers, which does not cause the UpdateConfig bit to be set. The e-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);
//Change Timer1 frequency output to 1M/(2*50) = 10 kHz.
ePut (lngHandle, LJ_ioPUT_TIMER_VALUE, 1, 50, 0);
//Read count from Timer2. This is an unsigned 32-bit value.
eGet (lngHandle, LJ_ioGET_TIMER, 2, &dblValue, 0);
//Read duty-cycle from Timer3.
eGet (lngHandle, LJ_ioGET_TIMER, 3, &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 quadrature count from Timer4. Timer5 would return the
//same value. This is a signed 32-bit value.
eGet (lngHandle, LJ_ioGET_TIMER, 4, &dblValue, 0);
//Read the count from Counter0. This is an unsigned 32-bit value.
eGet (lngHandle, LJ_ioGET_COUNTER, 0, &dblValue, 0);
Following is pseudocode to reset the input timers and the counter:
//Reset the firmware counter (Timer2) to zero, by writing a
//value of zero.
ePut (lngHandle, LJ_ioPUT_TIMER_VALUE, 2, 0, 0);
//Reset the duty-cycle measurement (Timer3) 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, 3, 0, 0);
//Reset the quadrature counters (Timer4 & Timer5) to zero, by
//writing a value of zero to either one.
ePut (lngHandle, LJ_ioPUT_TIMER_VALUE, 4, 0, 0);
//Reset Counter0 to zero.
ePut (lngHandle, LJ_ioPUT_COUNTER_RESET, 0, 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.