2Hz analogWriteFrequency on Teensy 3.1

This is an old T_3.1 thread - if an example problem on T_4.0 shows up it would be good to start a new thread perhaps.

By any chance has anyone got the analogWriteFrequencySlow function running on the Teensy 4.0?

More context please? Not seeing a analogWriteFrequencySlow() so it wasn't added to the Teensy CORES.

Does the analogWriteFrequency() not work on T_4.0 at 2Khz?
 
Quite some time ago I merged a contribution which I believe was originally named analogWriteFrequencySlow. But it was merged into normal analogWriteFrequency, rather than adding a new function name. If you request a frequency on Teensy 3.x which is too low to be generated by the timer+prescalers from F_BUS, then it switches the timer to using a much slower clock. This is only possible because the FTM timers have an option to use a slower clock.

So far, I am not aware of anyone managing to use an alternate clock for PWM on Teensy 4. It may be possible. Here's the info from the reference manual.

pwmclock.png

Normally we run with mux input 0, which is 150 MHz. With only an 8 bit prescaler and 16 bit counters, we just can't reach very slow PWM frequency because we can only divide 150 MHz by 2^24.

The AUX_CLK (input 2) might be possible, but it would require dedicating submodule0 to act as a prescaler. My understanding is it could only apply to the pins controlled by the other 3 submodules of each FlexPWM, and doing so would make the PWM unusable on the pins controlled by that first submodule.

EXT_CLK (input 1) looks much more promising. It appears to be connected to XBAR output 34 for FlexPWM1 and XBAR output 48 for the other 3 FlexPWM timers. So perhaps we could dedicate one of the unused QuadTimers to generate a relatively slow clock, like 100 kHz and route its through the XBAR to the EXT_CLK inputs.

Of course this would only work on the PWM pins controlled by FlexPWM timers. The QuadTimer controlled pins would need some other approach. But the QuadTimer prescaler has more bits, so maybe not such an issue with those pins.
 
By any chance has anyone got the analogWriteFrequencySlow function running on the Teensy 4.0?

Re: slow PWM with GPT

I think you can run the T4 GPT timer at low PWM frequency. The compare register is 32 bits so you should be able to get below 1 hz. I think you can only do 50% duty and the output pin is different for T4.0 (GPT2 compare3, pin 16) vs T4.1 (GPT2 compare1, pin 41). here is a T4.1 GPT PWM example
Code:
//teensy 4.1 GPT PWM  pin 41 AD_B1_05 PWM GPT2 compare1 ALT8

#define TICKS  24000

void setup() {
  //initialise general hardware
  Serial.begin(115200);             //setup serial port

  //  CCM_CSCMR1 &= ~CCM_CSCMR1_PERCLK_CLK_SEL; // turn off 24mhz mode
  CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON) ;  // enable GPT2 module

  //configure GPT2 for test
  GPT2_CR = 0;                                //clear the control register, FRR = 0 means restart after Compare
  GPT2_SR = 0x3F;                             //clear all prior status
  GPT2_PR = 0;                                //prescale register set divide by 1
  GPT2_CR |= GPT_CR_CLKSRC(1);                //clock selection #1 24 mhz crystal

  GPT2_CR |= GPT_CR_ENMOD;                    //reset count to zero before enabling
  GPT2_CR |= GPT_CR_OM1(1);           // toggle mode compare1
  GPT2_OCR1 = TICKS - 1;                   //Compare1 value  1khz

  GPT2_CR |= GPT_CR_EN;                   //enable GPT2

  //configure T41 pin 41 as Compare1 output
  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_05 = 8;
}


void loop() {

}
if you want to go real slow, you can select the 32khz clock for the GPT timer.

Restart mode (FRR = 0) only works on compare channel 1.
 
Last edited:
Re: slow PWM

As Paul noted, one can use EXT_CLK via XBAR to flexpwm timers to get a slower clock. Here is a proof-of-concept sketch using quad-timer to provide a 10khz clock via XBAR to flexpwm timer. Hook scope to pin 8 (PWM output pin).
Code:
// QTIMER4 timer 0  xbar 36 to EXT_CLK flexpwm via XBAR  34
// pin 8 FLEXPWM1_A3   XBAR1_OUT34

#define MMASK (1<<3)   // module mask 3 for PWM1_A3

#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)

void flexpwm_init(int scale) {
  // flexpwm pin 8  external clock

  FLEXPWM1_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(MMASK);  //A3
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 6 ;  // ALT6
  FLEXPWM1_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(MMASK);   // clear
  FLEXPWM1_FSTS0 = FLEXPWM_FSTS0_FFLAG(MMASK);    // clear
  FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(MMASK);
  FLEXPWM1_SM3CTRL2 = FLEXPWM_SMCTRL2_INDEP | FLEXPWM_SMCTRL2_CLK_SEL(1); // ext clk
  FLEXPWM1_SM3CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(0);
  FLEXPWM1_SM3INIT = 0;
  FLEXPWM1_SM3VAL0 = 0;
  FLEXPWM1_SM3VAL1 = scale - 1;  // period ticks
  FLEXPWM1_SM3VAL2 = 0;
  FLEXPWM1_SM3VAL3 = scale / 2;
  FLEXPWM1_SM3VAL4 = 0;
  FLEXPWM1_SM3VAL5 = 0;
  FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(MMASK) | FLEXPWM_MCTRL_RUN(MMASK);
}

void xbar_connect(unsigned int input, unsigned int output)
{
  if (input >= 88) return;
  if (output >= 132) return;
  volatile uint16_t *xbar = &XBARA1_SEL0 + (output / 2);
  uint16_t val = *xbar;
  if (!(output & 1)) {
    val = (val & 0xFF00) | input;
  } else {
    val = (val & 0x00FF) | (input << 8);
  }
  *xbar = val;
}

void xbar_init() {
  CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);   //turn clock on for xbara1
  xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_FLEXPWM1_EXT_CLK);   // 36,34
}

void qtmr40_init(int hz) {
  int cnt, pcs = 0;
  cnt = 150000000 / hz;
  while (cnt > 65536) {
    pcs++;
    hz *= 2;
    cnt = 150000000 / hz;
  }
  CCM_CCGR6 |= CCM_CCGR6_QTIMER4(CCM_CCGR_ON);  // enable qtmr4
  TMR4_CTRL0 = 0; // stop
  TMR4_SCTRL0 =  TMR_SCTRL_OPS | TMR_SCTRL_OEN ;
  TMR4_LOAD0 = 0;  // start val after compare
  TMR4_COMP10 = cnt - 1;  // count up to this val, interrupt,  and start again
  TMR4_CMPLD10 = cnt - 1;
  TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8 + pcs) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(4) ; // prescale
}


void setup() {
  Serial.begin(9600);

  delay(1000);
  xbar_init();
  flexpwm_init(10);   //divisor
  qtmr40_init(10000);  // 10 khz
}

void loop() {

}

EDIT: use QTIMER4/0 since not used by T4*
https://github.com/manitou48/teensy4/blob/master/qtmrxpwm.ino
 
Last edited:
Here is a proof-of-concept sketch

Awesome!

I had already put this thread on my low priority issue list, where "low priority" means I'll probably never actually get around to doing it. But with this proof of concept paving the way, I'll integrate it into analogWriteFrequency in Teensyduino 1.54 or 1.55.
 
Has anyone found a way to make this work for teensy 4.x? Seems like it will be a while before we can order teensy 3's...
 
Could you not use a frequency divider chip?
That way the 4.x can generate a higher frequency which is divided down by the external chip.
 
Has anyone found a way to make this work for teensy 4.x? Seems like it will be a while before we can order teensy 3's...

see example in post #55, flexpwm_init(5000); will give 2hz on pin 8
 
Last edited:
Back
Top