/**********************************************************************
* 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 );
}
}