Teensy 4.1 Fixed-Frequency PWM Mode

Fred_B

Member
Good morning everybody,

I'm playing with TMR1 on my Teensy 4, and following the NXP reference manuel.
For now I trying to make a fix frequency PWM like documentation describe on page 54.4.5.13 "Fixed-Frequency PWM Mode "
But I only have a high level signal instead having a 50% duty cycle.
Code:
#include <Arduino.h>

void resetTimer()
{
// enable timer1 clock
    CCM_CCGR6 |= 0XC000000; 

//reset TMR1 as it should be @reset state 
    TMR1_COMP10 = 0x0000;
    TMR1_COMP20 = 0x0000;
    TMR1_CAPT0 = 0x0000;
    TMR1_LOAD0 = 0x0000;
    TMR1_HOLD0 = 0x0000;
    TMR1_CNTR0 = 0x0000;
    TMR1_CTRL0 = 0x0000;
    TMR1_SCTRL0 = 0x0000;
    TMR1_CMPLD10 = 0x0000;
    TMR1_CMPLD20 = 0x0000;
    TMR1_CSCTRL0 = 0x0000;
    TMR1_FILT0 = 0x0000;
    TMR1_DMA0 = 0x0000;
    TMR1_ENBL = 0x000f;

// output 10 (GPIO_B0_00) attached to QTIMER1_TIMER0 output
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_00 = 0x00000001; 
}

void setup()
{
    resetTimer();                          // config timer as reset state, enable clock, and setup output pin
    
    TMR1_SCTRL0 = 0x00005; // TMR output enable
    
    TMR1_COMP10 = 0x8000; // compare @half to have 50% duty cycle
    
    TMR1_CTRL0 = 0x3006;   // source 1 prescaler = 1, count on rising edge of source 1, set OFLAG @ COMP, reset @ rollOver
                                               // if =0x3003 output frequency is 1.14KHz whith 0X3006 High level on ooutput pin 
    
    TMR1_CSCTRL0 = 0x0000; //no load at compare

}

void loop()
{
    delay(100);
    // do nothing
}


Is there someone who can take time to explain me what I'm doing wrong ? I

Here below the example from NXP reference manual
Code:
// This example uses TMR0 for Fixed-Frequency PWM mode timing.
//
// The timer will count IP_bus clocks continuously until it rolls over.
// This results in a PWM period of 65536 / 60e6 = 1092.267 usec
//
// Initially, an output pulse width of 25 usec is generated ( 1500 / 60e6 )
//
giving a PWM ratio of 1500 / 65536 = 2.289%
// This pulse width can be changed by changing the COMP1 register value (using CMPLD1).
//
void PWM1_Init(void)
{
[B]setReg(TMR0_CNTR,0); [/B]    /* Reset counter */

/* TMR0_SCTRL: TCF=0,TCFIE=0,TOF=0,TOFIE=0,IEF=0,IEFIE=0,IPS=0,INPUT=0,
Capture_Mode=0,MSTR=0,EEOF=0,VAL=0,FORCE=1,OPS=0,OEN=1 */
[B]setReg(TMR0_SCTRL,0x05); [/B]/* Enable output */

[B]setReg(TMR0_COMP1,1500-1);[/B]/* Store initial value to the duty-compare register */

/* TMR0_CTRL: CM=1,PCS=8,SCS=0,ONCE=0,LENGTH=0,DIR=0,COINIT=0,OUTMODE=6 */
[B]setReg(TMR0_CTRL,0x3006);[/B]/* Run counter */
}
Thank you
Fred
 
If it were me, I might try using the Arduino bult in PWM support and see if it does what you want or what you can learn from it.

That is something like:
Code:
    analogWriteFequency(10, <desired requency);
     analogWrite(128); // 50% duty

From there if it works, you can simply just use.
Else if you want to learn from it.

Look in the <cores>/teensy4/pwm.c file
and look at things like quadtimer_init, quadtimerWrite, quadtimerFrequency.

And/or, dump out all of the registers after the calls to see what it set them to
 
The analogWriteFequency() and analogWrite() write code configures the timer into a mode where both the high and low time can be controlled. Then it keeps the frequency consistent by calculating the high and low time so they always sum to the desired period.

Maybe that's overkill for your needs, but you can just grab known-good code from pwm.c if you use that mode. Probably a lot easier than figuring out NXP's timer documentation.
 
And to address your original question, I don't know why it's not working.

To be honest, all 3 times I've written code for the QuadTimers (analogWrite, OctoWS2811, Audio library ADC input), it's taken multiple days of fiddling to figure out how to get them to work the way I wanted. NXP's documentation leaves a lot to be desired. I know it probably doesn't really help much, but you're not alone. The timers are hard to use. They also have somewhat "weird" features compared to the FlexPWM & GPT timers and the timers I've used in Kinetis, AVR, MSP430, PIC and 8051.

But using analogWriteFrequency() and analogWrite() is extremely easy if you only need basic PWM at a specific fixed frequency.
 
I have a quad timer PWM test sketch qtmrtst.ino from the Teensy 4 beta testing days. Particular tests can be enabled to test interrupts for timer, underflow, and overflow, as well as test functions for Teensy 4 PWM and for NXP SDK's PWM. I've added a test function for fixed frequency.

Here are changes to your setup() to get fixed frequency PWM working. PWM frequency is 150MHz/65536 = 2289Hz
Code:
void setup()
{
  resetTimer();                          // config timer as reset state, enable clock, and setup output pin

  TMR1_COMP10 = 0x8000 - 1; // compare @half to have 50% duty cycle
  TMR1_CMPLD10 = 0x8000 - 1;
  TMR1_LOAD0 = 0x8000;   // start counter here after compare match
  TMR1_CSCTRL0 = 0x0000; //no load at compare
  TMR1_SCTRL0 = 0x00005; // TMR output enable
  TMR1_CTRL0 = 0x3006 | TMR_CTRL_LENGTH ;  // source 1 prescaler = 1, count on rising edge of source 1, set OFLAG @ COMP, reset @ rollOver
  // if =0x3003 output frequency is 1.14KHz whith 0X3006 High level on ooutput pin
}
 
Back
Top