Controlled pulse shortener

Hmmm... ran into a hurdle trying to upload this code. Receive this error message:

C:\Users\AppData\Local\Temp\arduino_build_684152/..\arduino_cache_84243\core\core_teensy_avr_teensy31_usb_serial,speed_96,opt_o2std,keys_en-us_4939f5412a75a8560acb0f9e98268f02.a(main.cpp.o): In function `main':
C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/main.cpp:53: undefined reference to `loop'
collect2.exe: error: ld returned 1 exit status
Error compiling for board Teensy 3.2 / 3.1.


Previous code releases from you still compile OK.
 
Hmmm... ran into a hurdle trying to upload this code. Previous code releases from you still compile OK.

Please try this one. I might have been in the middle of a change when I did my last copy/paste. I confirmed that this builds for T3.2, though I'm testing on T3.5

Code:
/**********************************************************************
* Controlled Pulse Shortener 
* FTM input capture on pin 6 (ch4) with interrupt on rising edges
* FTM COMBINE-mode PWM on pins 9,10 (ch2,3)
* T3X pins for FTM0 channels 0-7 = 22, 23, 9, 10, 6, 20, 21, 5
***********************************************************************/
#define PWM_SIGNAL_PIN (3) // must be a pin that is NOT on FTM0

#define INPUT_PIN (6) // FTM0 ch4 for input capture
#define OUTPUT_PIN (9) // FTM0 ch2,3 COMBINE -> output on ch2

#define F_FTM (F_CPU/2) // FTM base clock frequency is F_CPU/2
#define FTM_ONE_US (F_FTM/1'000'000)

#define PWM_FREQUENCY (50000) // 50000 -> 20-us period
#define PWM_DUTYCYCLE (256/2) // 50% -> 10-us high time

volatile uint16_t capture, capture_prev = 0; // use 16-bit for roll-over
volatile uint16_t input_period = 0; // use 16-bit for correct roll-over
volatile uint16_t output_width = 0;
volatile uint32_t capture_count = 0;
volatile uint32_t ftm0_sc_save;

volatile float output_width_pct = 10.0; // modify to set output pulse width 

// FTM0 ISR -- each FTM module has a single interrupt vector, shared by all 
// channels. ISR function ftm0_isr() is declared in cores\teensy3\kinetis.h and
// defined in cores\teensy3\mk20dx128.c, with the attribute "weak", and included
// in the interrupt vector table. The "weak" attribute allows this function to
// "override" the version in mkd20dx128.c, and we don't have to do anything but
// define the function here for it to be used. The modifier FASTRUN causes the
// ISR to be copied to RAM at run-time for fastest operation.
FASTRUN void ftm0_isr()
{
  // handle interrupts for FTM0 channel 2 (input capture on rising edge of input)
  if (FTM0_STATUS & (1<<4)) {
    // clear interrupt flag by read CnSC register, then write 0 to CHF
    FTM0_C4SC &= ~FTM_CSC_CHF;
    // read input capture value from CnV register
    capture = FTM0_C4V;
    switch (capture_count) {
      case 0: // 1st capture
        break;
      case 1: // 2nd capture
        input_period = capture - capture_prev;
        output_width = output_width_pct/100.0f * input_period + 0.5;
        // disable FTM0, load new C2V/C3V, re-enable (do NOT set FTM0_CNT=0)
        ftm0_sc_save = FTM0_SC;
        FTM0_SC = 0; // disable FTM clock
        //FTM0_CNT = 0; // for some reason, setting CNT=0 here causes extra pulse 
        FTM0_C2V = 0; // rising edge time 
        FTM0_C3V = output_width; // falling edge time
        FTM0_SC = ftm0_sc_save;
        break;
      default: // subsequent captures (reset FTM0 count to sync to input edge)
        FTM0_CNT = 0;
        break;
    }
    // save capture value for next pass and increment counter for debug use
    capture_prev = capture;
    capture_count++;
  }
}

void setup()
{
  // init USB serial and wait for ready
  Serial.begin(9600);
  while (!Serial) {}
  
  // configure pins
  pinMode( PWM_SIGNAL_PIN, OUTPUT );
  pinMode( LED_BUILTIN, OUTPUT );

  // init the LED pin LOW
  digitalWriteFast( LED_BUILTIN, LOW );

  // configure pin 3 for 20 Hz frequency and 50% duty cycle
  analogWriteFrequency( PWM_SIGNAL_PIN, PWM_FREQUENCY );
  analogWrite( PWM_SIGNAL_PIN, PWM_DUTYCYCLE );

  // configure pin 6 to be FTM0 ch4
  CORE_PIN6_CONFIG = PORT_PCR_MUX(4);

  // configure pins 9,10 to be FTM0 ch2,3
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(4);
  CORE_PIN10_CONFIG = PORT_PCR_MUX(4);
  
  // disable FTM0 interrupt
  NVIC_DISABLE_IRQ(IRQ_FTM0);
  
  // init FTM0 registers
  FTM0_SC = 0; // disable
  FTM0_MOD = 0xFFFF; // count to max 16-bit and roll over to 0
  FTM0_CNTIN = 0; // reload value
  FTM0_CNT = 0; // sets CNT = CNTIN
  FTM0_MODE = FTM_MODE_WPDIS; // allow reconfiguring the CSC on the fly

  // enable ch4 (pin 6) for input capture on RISING edges and enable interrupt
  FTM0_C4SC = FTM_CSC_CHIE | FTM_CSC_ELSA;

  // set registers for COMBINE of ch2,3
  FTM0_MODE |= FTM_MODE_FTMEN; // FTMEN = 1
  FTM0_COMBINE |= FTM_COMBINE_COMBINE1; // COMBINE ch2,3
  
  // enable ch2 (pin 9) for high-true pulses (high on C2V, low on C3V)
  FTM0_C2SC = FTM_CSC_ELSB;
  FTM0_C3SC = 0;

  // set ch2,3 compare values for max value to delay match
  FTM0_C2V = 0xF000; // start at 0
  FTM0_C3V = 0xF800; // start at 0

  FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // internal clock, prescaler=0 (div by 1)

  // raise FTM0 interrupt level to 0 (highest priority)
  NVIC_SET_PRIORITY(IRQ_FTM0,0);
  // enable FTM0 interrupt
  NVIC_ENABLE_IRQ(IRQ_FTM0);
}

elapsedMillis loop_delay = 0;
uint32_t loop_count = 0;

void loop()
{
  // wait 1 second and print interrupt count, just to confirm frequency 
  if (loop_delay >= 1000) {
    loop_delay = 0;
    loop_count++;
    Serial.printf( "%10lu  %10lu  %5hu\n", loop_count, capture_count, input_period );
    digitalWriteFast( LED_BUILTIN, (loop_count % 2) ? HIGH : LOW );    
  }
}
 
The latest release looks very good. Pulses are at consistent starting points with respect to the input pulse, and widths between 2% and 50% work great. I tried a couple of non-integer values as well and they worked OK.

Thanks for your time and effort on helping me out with this little project !!
 
The latest release looks very good. Pulses are at consistent starting points with respect to the input pulse, and widths between 2% and 50% work great. I tried a couple of non-integer values as well and they worked OK.

Thanks for your time and effort on helping me out with this little project !!

You're welcome. I was kinda glad to have a reason to understand the FTM module better. The way that I ended up doing it is not obvious in looking at the code. FTM0 ch4 (pin 6) does input capture on rising edges, and that's how the input period is measured. FTM0 channels 2,3 (pins 9,10) are configured in the COMBINE mode to generate a PWM signal. The period of the PWM is configured to be the max (65536 clocks). On each rising edge of the input, the FTM0 counter gets reset to 0 and the match values are set to provide the desired pulse width. The next rising edge occurs before the PWM period expires, so the PWM period never reaches its end, and the output gets "syncrhonized" with the input on each rising edge. Now that I got this working, I can see that it can be done more simply with the edge-aligned PWM mode, which would require only one channel (and pin) instead of the two channels/pins required for the COMBINE mode.
 
Last edited:
@joe, I'm running into some difficulty in changing the response pulse width. The code you supplied starts with a default of 10%; I would like to be able to change this percentage real-time to other values.

I noticed that capture_count starts at a high value and increases dramatically as the app runs. If it never goes back to 1, the switch-case will not allow for a width change.
 
Hi Biff. Here's an update that does a couple of new things. It processes serial input from the Arduino console and looks for valid numerical characters. When newline is found (user hits enter), the input string is converted to a float and checked for range 2.0 - 98.0. Any time an input value is received (even if it's not between 2 and 98), the input width measurement is re-initialized, and the new output_width_pct is used. There is a new variable called capture_state that is used in the ISR so that capture_count rolling over won't have any effect.

Code:
/**********************************************************************
* Controlled Pulse Shortener 
* FTM input capture on pin 6 (ch4) with interrupt on rising edges
* FTM COMBINE-mode PWM on pins 9,10 (ch2,3)
* T3X pins for FTM0 channels 0-7 = 22, 23, 9, 10, 6, 20, 21, 5
***********************************************************************/
#define PWM_SIGNAL_PIN (3) // must be a pin that is NOT on FTM0

#define INPUT_PIN (6) // FTM0 ch4 for input capture
#define OUTPUT_PIN (9) // FTM0 ch2,3 COMBINE -> output on ch2

#define F_FTM (F_CPU/2) // FTM base clock frequency is F_CPU/2
#define FTM_ONE_US (F_FTM/1'000'000)

#define PWM_FREQUENCY (50000) // 50000 -> 20-us period
#define PWM_DUTYCYCLE (256/2) // 50% -> 10-us high time

volatile uint16_t capture, capture_prev = 0; // use 16-bit for roll-over
volatile uint16_t input_period = 0; // use 16-bit for correct roll-over
volatile uint16_t output_width = 0;
volatile uint32_t capture_count = 0;
volatile uint32_t capture_state = 0;

volatile float output_width_pct = 10.0; // modify to set output pulse width 

// FTM0 ISR -- each FTM module has a single interrupt vector, shared by all 
// channels. ISR function ftm0_isr() is declared in cores\teensy3\kinetis.h and
// defined in cores\teensy3\mk20dx128.c, with the attribute "weak", and included
// in the interrupt vector table. The "weak" attribute allows this function to
// "override" the version in mkd20dx128.c, and we don't have to do anything but
// define the function here for it to be used. The modifier FASTRUN causes the
// ISR to be copied to RAM at run-time for fastest operation.
FASTRUN void ftm0_isr()
{
  // handle interrupts for FTM0 channel 2 (input capture on rising edge of input)
  if (FTM0_STATUS & (1<<4)) {
    // clear interrupt flag by read CnSC register, then write 0 to CHF
    FTM0_C4SC &= ~FTM_CSC_CHF;
    // read input capture value from CnV register
    capture = FTM0_C4V;
    switch (capture_state) {
      case 0: // 1st capture
        capture_state++;
        break;
      case 1: // 2nd capture
        input_period = capture - capture_prev;
        output_width = output_width_pct/100.0f * input_period + 0.5;
        // disable FTM0, load new C2V/C3V, re-enable (do NOT set FTM0_CNT=0)
        FTM0_SC = 0; // disable FTM clock
        //FTM0_CNT = 0; // for some reason, setting CNT=0 here causes extra pulse 
        FTM0_C2V = 0; // rising edge time 
        FTM0_C3V = output_width; // falling edge time
        FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // internal clock, prescaler=0 (div by 1)
        capture_state++;
        break;
      default: // subsequent captures (reset FTM0 count to sync to input edge)
        FTM0_CNT = 0;
        break;
    }
    // save capture value for next pass and increment counter for debug use
    capture_prev = capture;
    capture_count++;
  }
}

void setup()
{
  // init USB serial and wait for ready
  Serial.begin(9600);
  while (!Serial) {}
  
  // configure pins
  pinMode( PWM_SIGNAL_PIN, OUTPUT );
  pinMode( LED_BUILTIN, OUTPUT );

  // init the LED pin LOW
  digitalWriteFast( LED_BUILTIN, LOW );

  // configure pin 3 for 20 Hz frequency and 50% duty cycle
  analogWriteFrequency( PWM_SIGNAL_PIN, PWM_FREQUENCY );
  analogWrite( PWM_SIGNAL_PIN, PWM_DUTYCYCLE );

  // configure pin 6 to be FTM0 ch4
  CORE_PIN6_CONFIG = PORT_PCR_MUX(4);

  // configure pins 9,10 to be FTM0 ch2,3
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(4);
  CORE_PIN10_CONFIG = PORT_PCR_MUX(4);
  
  // disable FTM0 interrupt
  NVIC_DISABLE_IRQ(IRQ_FTM0);
  
  // init FTM0 registers
  FTM0_SC = 0; // disable
  FTM0_MOD = 0xFFFF; // count to max 16-bit and roll over to 0
  FTM0_CNTIN = 0; // reload value
  FTM0_CNT = 0; // sets CNT = CNTIN
  FTM0_MODE = FTM_MODE_WPDIS; // allow reconfiguring the CSC on the fly

  // enable ch4 (pin 6) for input capture on RISING edges and enable interrupt
  FTM0_C4SC = FTM_CSC_CHIE | FTM_CSC_ELSA;

  // set registers for COMBINE of ch2,3
  FTM0_MODE |= FTM_MODE_FTMEN; // FTMEN = 1
  FTM0_COMBINE |= FTM_COMBINE_COMBINE1; // COMBINE ch2,3
  
  // enable ch2 (pin 9) for high-true pulses (high on C2V, low on C3V)
  FTM0_C2SC = FTM_CSC_ELSB;
  FTM0_C3SC = 0;

  // set ch2,3 compare values for max value to delay match
  FTM0_C2V = 0xF000; // start at 0
  FTM0_C3V = 0xF800; // start at 0

  FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // internal clock, prescaler=0 (div by 1)

  // raise FTM0 interrupt level to 0 (highest priority)
  NVIC_SET_PRIORITY(IRQ_FTM0,0);
  // enable FTM0 interrupt
  NVIC_ENABLE_IRQ(IRQ_FTM0);
}

elapsedMillis loop_delay = 0;
uint32_t loop_count = 0;
uint32_t char_count = 0;
char buf[32];

void loop()
{
  // get input from console
  while (Serial.available()) {
    char c = Serial.read();
    if ((c >= '0' && c <= '9') || (c == '.')) {
      // append valid numerical characters to input string
      buf[char_count++] = c;
    }
    else if (c == '\n') {
      // on newline, null-terminate string and convert to integer
      buf[char_count] = '\0';
      float value = atof( buf );
      // if valid % (2-98), update output_width_pct
      if (value >= 2 && value <= 98) 
        output_width_pct = value;
      // set capture_state = 0 to reset input edge processing
      capture_state = 0;
      // set capture_count = 0 to provide feedback that input was received
      capture_count = 0;
      // set char_count = 0 to reset console input processing
      char_count = 0;
    }
  }
  // every 1 second -- print interrupt count and input width 
  if (loop_delay >= 1000) {
    loop_delay = 0;
    loop_count++;
    Serial.printf( "%10lu %10lu %5hu %6.2f\n", loop_count, capture_count, input_period, output_width_pct );
    digitalWriteFast( LED_BUILTIN, (loop_count % 2) ? HIGH : LOW );    
  }
}
 
@joe, I loaded the new code, and the response pulse width adjusts just fine to serial port input. Thanks again for your quick response!
 
@joe, I loaded the new code, and the response pulse width adjusts just fine to serial port input. Thanks again for your quick response!

You're welcome. Yes, it seems a good idea to be able to modify the output pulse width and also to be able to reinitialize the input period measurement. In this case we can do that with a single command, but if you ever need a more complex command capability, the link below is for a serial command library I have used a few times. There are many out there, and somehow I settled on this one because I like the syntax. With this library, you can define a set of commands and keep all of the command parsing and processing out of your loop() function. You can define commands without arguments, such as "start", or with arguments, such as "width 33.33".

https://github.com/kroimon/Arduino-SerialCommand
 
@joe, I have one more (new) request -- we'd like to be able to adjust the starting time of the response pulse. Right now, the response pulse is set to begin shortly after the rising edge of the source pulse. How can we make that delay adjustable?

Many thanks!

BTW, I expect it has to do with adjusting FTM0_C2V, but I can't seem to get mods to this register to stick.
 
Last edited:
@joe, I have one more (new) request -- we'd like to be able to adjust the starting time of the response pulse. Right now, the response pulse is set to begin shortly after the rising edge of the source pulse. How can we make that delay adjustable?

Many thanks!

BTW, I expect it has to do with adjusting FTM0_C2V, but I can't seem to get mods to this register to stick.

Give the code below a try. In addition to the "output_width_pct", there is a new "output_delay_pct". You're right that C2V defines the rising edge time, and in this update it is modified on line #54 of the sketch. That line was previously set C2V=0, and now it is based on output_delay_pct. Note that the setting of C2V and C3V in the ISR are bracketed by disable/enable of the FTM clock. If you just write to C2V without disabling the clock, then you're right, the update doesn't "stick". There are different requirements/possibilities for updating the various registers depending on whether you are doing output compare versus PWM.

Code:
/**********************************************************************
* Controlled Pulse Shortener 
* FTM input capture on pin 6 (ch4) with interrupt on rising edges
* FTM COMBINE-mode PWM on pins 9,10 (ch2,3)
* T3X pins for FTM0 channels 0-7 = 22, 23, 9, 10, 6, 20, 21, 5
***********************************************************************/
#define PWM_SIGNAL_PIN (3) // must be a pin that is NOT on FTM0

#define INPUT_PIN (6) // FTM0 ch4 for input capture
#define OUTPUT_PIN (9) // FTM0 ch2,3 COMBINE -> output on ch2

#define F_FTM (F_CPU/2) // FTM base clock frequency is F_CPU/2
#define FTM_ONE_US (F_FTM/1'000'000)

#define PWM_FREQUENCY (50000) // 50000 -> 20-us period
#define PWM_DUTYCYCLE (256/2) // 50% -> 10-us high time

volatile float output_delay_pct = 0.0; // delay from input high to output high 
volatile float output_width_pct = 10.0; // output pulse width 

volatile uint16_t capture, capture_prev = 0; // use 16-bit for roll-over
volatile uint16_t input_period = 0; // use 16-bit for correct roll-over
volatile uint16_t output_delay = 0;
volatile uint16_t output_width = 0;
volatile uint32_t capture_count = 0;
volatile uint32_t capture_state = 0;

// FTM0 ISR -- each FTM module has a single interrupt vector, shared by all 
// channels. ISR function ftm0_isr() is declared in cores\teensy3\kinetis.h and
// defined in cores\teensy3\mk20dx128.c, with the attribute "weak", and included
// in the interrupt vector table. The "weak" attribute allows this function to
// "override" the version in mkd20dx128.c, and we don't have to do anything but
// define the function here for it to be used. The modifier FASTRUN causes the
// ISR to be copied to RAM at run-time for fastest operation.
FASTRUN void ftm0_isr()
{
  // handle interrupts for FTM0 channel 2 (input capture on rising edge of input)
  if (FTM0_STATUS & (1<<4)) {
    // clear interrupt flag by read CnSC register, then write 0 to CHF
    FTM0_C4SC &= ~FTM_CSC_CHF;
    // read input capture value from CnV register
    capture = FTM0_C4V;
    switch (capture_state) {
      case 0: // 1st capture
        capture_state++;
        break;
      case 1: // 2nd capture
        input_period = capture - capture_prev;
        output_delay = output_delay_pct/100.0f * input_period + 0.5;
        output_width = output_width_pct/100.0f * input_period + 0.5;
        // disable FTM0, load new C2V/C3V, re-enable (do NOT set FTM0_CNT=0)
        FTM0_SC = 0; // disable FTM clock
        //FTM0_CNT = 0; // for some reason, setting CNT=0 here causes extra pulse 
        FTM0_C2V = output_delay; // rising edge time 
        FTM0_C3V = output_delay + output_width; // falling edge time
        FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // internal clock, prescaler=0 (div by 1)
        capture_state++;
        break;
      default: // subsequent captures (reset FTM0 count to sync to input edge)
        FTM0_CNT = 0;
        break;
    }
    // save capture value for next pass and increment counter for debug use
    capture_prev = capture;
    capture_count++;
  }
}

void setup()
{
  // init USB serial and wait for ready
  Serial.begin(9600);
  while (!Serial) {}
  
  // configure pins
  pinMode( PWM_SIGNAL_PIN, OUTPUT );
  pinMode( LED_BUILTIN, OUTPUT );

  // init the LED pin LOW
  digitalWriteFast( LED_BUILTIN, LOW );

  // configure pin 3 for 20 Hz frequency and 50% duty cycle
  analogWriteFrequency( PWM_SIGNAL_PIN, PWM_FREQUENCY );
  analogWrite( PWM_SIGNAL_PIN, PWM_DUTYCYCLE );

  // configure pin 6 to be FTM0 ch4
  CORE_PIN6_CONFIG = PORT_PCR_MUX(4);

  // configure pins 9,10 to be FTM0 ch2,3
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(4);
  CORE_PIN10_CONFIG = PORT_PCR_MUX(4);
  
  // disable FTM0 interrupt
  NVIC_DISABLE_IRQ(IRQ_FTM0);
  
  // init FTM0 registers
  FTM0_SC = 0; // disable
  FTM0_MOD = 0xFFFF; // count to max 16-bit and roll over to 0
  FTM0_CNTIN = 0; // reload value
  FTM0_CNT = 0; // sets CNT = CNTIN
  FTM0_MODE = FTM_MODE_WPDIS; // allow reconfiguring the CSC on the fly

  // enable ch4 (pin 6) for input capture on RISING edges and enable interrupt
  FTM0_C4SC = FTM_CSC_CHIE | FTM_CSC_ELSA;

  // set registers for COMBINE of ch2,3
  FTM0_MODE |= FTM_MODE_FTMEN; // FTMEN = 1
  FTM0_COMBINE |= FTM_COMBINE_COMBINE1; // COMBINE ch2,3
  
  // enable ch2 (pin 9) for high-true pulses (high on C2V, low on C3V)
  FTM0_C2SC = FTM_CSC_ELSB;
  FTM0_C3SC = 0;

  // set ch2,3 compare values for max value to delay match
  FTM0_C2V = 0xF000; // start at 0
  FTM0_C3V = 0xF800; // start at 0

  FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // internal clock, prescaler=0 (div by 1)

  // raise FTM0 interrupt level to 0 (highest priority)
  NVIC_SET_PRIORITY(IRQ_FTM0,0);
  // enable FTM0 interrupt
  NVIC_ENABLE_IRQ(IRQ_FTM0);
}

elapsedMillis loop_delay = 0;
uint32_t loop_count = 0;
uint32_t char_count = 0;
char buf[32];

void loop()
{
  // get input from console
  while (Serial.available()) {
    char c = Serial.read();
    if ((c >= '0' && c <= '9') || (c == '.')) {
      // append valid numerical characters to input string
      buf[char_count++] = c;
    }
    else if (c == '\n') {
      // on newline, null-terminate string and convert to integer
      buf[char_count] = '\0';
      float value = atof( buf );
      // if valid % (2-98), update output_width_pct
      if (value >= 2 && value <= 98) 
        output_width_pct = value;
      // set capture_state = 0 to reset input edge processing
      capture_state = 0;
      // set capture_count = 0 to provide feedback that input was received
      capture_count = 0;
      // set char_count = 0 to reset console input processing
      char_count = 0;
    }
  }
  // every 1 second -- print interrupt count and input width 
  if (loop_delay >= 1000) {
    loop_delay = 0;
    loop_count++;
    Serial.printf( "%10lu %10lu %5hu %6.2f\n", loop_count, capture_count, input_period, output_width_pct );
    digitalWriteFast( LED_BUILTIN, (loop_count % 2) ? HIGH : LOW );    
  }
}
 
New code works well - Thanks.

Looks like I was forgetting to update FTM0_C3V as well, and that is required to maintain the desired pulse width.
 
New code works well - Thanks.

Looks like I was forgetting to update FTM0_C3V as well, and that is required to maintain the desired pulse width.

For edge-align PWM, the pulse always begins when the channel count register CNT=0 and ends when CNT=CxV, where X is the channel number. Combine-mode PWM gives you the added flexibility of locating the pulse anywhere in the PWM period. It "combines" two channels to define one output. In this case the channels are 2 and 3, so C2V defines pulse start and C3V defines pulse end. The output is always on the pin associated with the lower-numbered channel.
 
Back
Top