4.1.6 - Timers & Counters [U3 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; eGet/ePut; 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
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_tmLINETOLINE //Line-to-line input
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-2
LJ_chTIMER_CLOCK_BASE //Set with the constant values below. Gets the low-level value.
LJ_chTIMER_CLOCK_DIVISOR //0-255, where 0=256
LJ_chTIMER_COUNTER_PIN_OFFSET //4-8 only starting with hardware rev 1.30.
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
Constant values used to set (put) the timer clock base differ from the low-level timer clock base retrieved (get) from the device. Constant value - 20 = low-level value. Table 2.9-2 documents these values.
Following is example pseudocode for configuring various timers and a hardware counter:
//Execute the pin_configuration_reset IOType so that all
//pin assignments are in the factory default condition.
//The ePut function is used, which combines the add/go/get.
ePut (lngHandle, LJ_ioPIN_CONFIGURATION_RESET, 0, 0, 0);
//First, an add/go/get block to configure the timers and counters.
//Set the pin offset to 4, which causes the timers to start on FIO4.
AddRequest (lngHandle, LJ_ioPUT_CONFIG, LJ_chTIMER_COUNTER_PIN_OFFSET, 4, 0, 0);
//Enable both timers. They will use FIO4-FIO5
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, FIO6.
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. Note that this timer
//clock base is not valid with U3 hardware version 1.20.
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 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);
//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 U3 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.