/**********************************************************************
* 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 float output_width_pct = 2.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) {
default:
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;
break;
case 2: // 3rd capture
FTM0_SC = 0;
FTM0_MOD = input_period-1;
FTM0_CNTIN = 0;
FTM0_CNT = 0;
FTM0_C2V = 0;
FTM0_C3V = output_width; // (output_width_pct * input_period + 50)/100;
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // internal clock, prescaler=0 (div by 1)
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 = 0; // start at 0
FTM0_C3V = 0; // 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 );
}
}