Help generate fixed frequency with FTM2 on Teensy 3.2

Jp3141

Well-known member
I have a measurement instrument that is already using FTM0 and FTM1. I want to generate a 48 kHz (FBUS/1000) signal on the Teensy's pin #25 (PTB18), the duty cycle doesn't really matter (squareish).

I can't use the timers because their PWM output is slightly jittery (20 ns; occasionally more) because the actual output is driven from an interrupt and there is variable interrupt latency (because of caching ? If the function was copied to RAM would it disappear ?).

So I tried to modify an FTM example from the manual, but can't get anything. Note this is on pin 25 (internal pin on the back of Teensy 3.2)

Code:
void setup() {
  pinMode(25, OUTPUT);
  PWMPhaseShift();
}

void loop() {
}

void PWMPhaseShift(void) {
  SIM_SCGC3 |= 0x01000000; //enable FTM2 clock
  SIM_SCGC5 = SIM_SCGC5|0x3E00; //enable port A/B/C/D/E clock
  FTM2_FMS = 0x00; //clear the WPEN so that WPDIS is set in FTM2_MODE reg
  FTM2_MODE |= 0x05; //enable write the FTM CnV register
  FTM2_SC = 0b00001000; //clk src = System;
  FTM2_CONF = 0x0;
  FTM2_MOD = 1000;
  FTM2_C0SC = 0x28; //High-Low_high for combined and complementary mode
  FTM2_C0SC = 0b00010100;   // Toggle
  FTM2_COMBINE = 0; //no combine
  FTM2_DEADTIME = 0; 
  FTM2_C1V = 750;
  FTM2_C0V = 250;
  FTM2_CNTIN = 0x00;
  
// PT B18 = MCU #41, Teensy  #25
// PT B19 = MCU #42, Teensy  #32
  // Bits | Value | Description
  // 10-8 |   100 | MUX: Alt 3 attach to FTM2_CH0
  //  7   |     0 | Reserved
  //  6   |     0 | DSE: Low drive strength
  //  5   |     0 | open drain = off
  //  4   |     0 | PFE: Disable input filter
  //  3   |     0 | Reserved
  //  2   |     0 | SRE: Fast slew rate if output
  //  1   |     0 | PE: Enable pull-up/down
  //  0   |     0 | PS: Internal pull-up

  *portConfigRegister(25) = PORT_PCR_MUX(3); // FTM3_CH0 == Alt3
}

What am I missing ?
 
Last edited:
As noted on T3.2 card, backside pins 25 and 32 are PWM. So the simplest solution is to use analogWrite()
Code:
analogWriteFrequency(25,48000);
analogWrite(25,128);  //  50% duty
Here is what the pin 25 PWM registers contain
Code:
FTM2_SC 0x88
FTM2_MOD 0x3E7   (that's 999 decimal, see ref manual regarding n-1)
FTM2_C1V 0x1F4   (that's 500 decimal)
FTM2_C1SC 0xA8
CORE_PIN25_CONFIG 0x344

teensy core configures pin with
CORE_PIN25_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
works for me on T3.2 with scope on pad for pin 25.

Your sketch has some problems, if you use toggle mode (Teensy core uses Edge pwm FTM2_C1SC = 0x28) you'd need to change your MOD from 1000-1 to 500 -1 (and C1V would be 250). I would use edge PWM mode instead of toggle mode. Teensy core uses edge PWM, FTM2_C1SC = 0x28

pin 25 is PTB19 and the timer in ALT3 mode is FTM2_CH1, your sketch is using channel 0 ?? FTM2_C0SC should be C1SC

teensy core PWM code is in hardware/teensy/avr/cores/teensy3/pins_teensy.c
 
Last edited:
Use of functions like analogWriteFrequency() and analogWrite() also has the advantage that your code will "just work" when / if you wish to use Teensy 4.0 or some other Teensy model.
 
Thanks all,

After I realized I was looking at the wrong pin, and using Manitou's guidance, I whittled my code to:

Code:
void PWMPhaseShift(void) {
// will use FTM2_CH1
  FTM2_SC = 0b00001000; //clk src = System; div 1
  FTM2_MOD = (1000-1); // F_BUS/1000
  
  FTM2_C1V = (1000/2-1); // Duty Cycle
  FTM2_CNTIN = 0;
 

// PT B18 = MCU #41, Teensy  #25
// PT B19 = MCU #42, Teensy  #32 (-- use FTM2_Ch1)
  // Set PORTB_PCR? register (Teensy 3.2 - pin 20)
  // Bits | Value | Description
  // 10-8 |   100 | MUX: Alt 3 attach to FTM2_CH0
  //  7   |     0 | Reserved
  //  6   |     0 | DSE: Low drive strength
  //  5   |     0 | open drain = off
  //  4   |     0 | PFE: Disable input filter
  //  3   |     0 | Reserved
  //  2   |     0 | SRE: Fast slew rate if output
  //  1   |     0 | PE: Enable pull-up/down
  //  0   |     0 | PS: Internal pull-up
  //  PORTD_PCR5 = 0b10000000000;
  *portConfigRegister(25) = PORT_PCR_MUX(3); // FTM2_CH1 == Alt3
}

which works. analogWriteFrequency() works equivalently, but I like to roll my own:).
 
Back
Top