Modify analogWriteFrequency function

Status
Not open for further replies.
Hi,

Was wondering if it is possible to modify the analogWriteFrequency function so that it becomes double buffered similiar to the analogWrite function?
Namely I just want the new frequency I specify using analogWriteFrequency to take effect on the next PWM cycle so that there is a smooth transition.

I am using a teensy 3.6 and need a way to transition from one PWM frequency and duty cycle to another smoothly.

Thanks so much.

-Sahil
 
Or you could wire your PWM output pin to an input pin, set the input pin to pin change interrupt, and write the new frequency in your interrupt function. And you would not need to mess about in changing the core files.
 
thanks,
does setting just FTMx_mod automatically propogate in at the next cycle or is that only for teensy LC?

You should test with scope or analyzer. Hack should apply to all Teensy 3* creatures. My experience is that duty/frequency change takes place on next PWM cycle, see analyzer image in
https://forum.pjrc.com/threads/4382...cies-Teensy-LC?p=142593&viewfull=1#post142593

you'll need to study the MCU reference manual. If your frequency change requires altering the prescale value, i think you have to STOP the PWM first, which is what analogrWriteFrequency() does.
 
Last edited:
Can I do this by messing with the registers directly? There are cases where I need to jump from 2.5 kHz back to down to 1.3 Hz and then back up again. the prescale value would need to be changed in those cases, but if that means I need to stop the clock then I need an alternative.
 
Already doing this but the main issue is that I need to make a duty cycle and frequency change together and so even if I do it on a rising edge interrupt the first pulse after I make the change still comes in with the wrong pulse width period.
 
I have the same issue doing it on the falling edge. Do I need to stop the clock using FTMx_SC if I want to change the prescaler? If I do not would it stop working altogether?
 
RISING EDGE.jpg
Here is my scope trace right now, you can see that the transition point gets screwed up going from 2.5 kHz to 1.3 Hz.-
 
Can't you find a prescaler and a clock source value which works for both frequencies, 2.5kHz and 1.3Hz? That's basically a 1:1923 ratio, so that with a good prescaler value, you should find appropriate MOD values between 1 and 16383 for both frequencies.
 
Right now I have the analogWriteResoluation set to 16, are you saying I should lower it? Also could you explain in a bit more detail what you mean in your last post. As in where did these numbers you just mentioned come from? I apologize I am still new to much of this information.
 
The PWM frequency depends on 3 factors: The clock source, the prescaler value and the mod value. All three can be set in the corresponding configuration registers of the FlexTimer which is associated to the pin. That‘s what the analogWriteFrequency() function does, depending on the frequency you choose. You are invited to look at the well commented source code of the analogWriteFrequency() function in the Teensyduino core files to learn how it is done. There you will also discover the analogWriteResolution() function which is there only for Arduino convenience. If you will have already calculated and written the clock source, the prescaler, and the mod values into their registers, it‘s an easy step to multiply the mod value with your desired duty cycle and write the result into the appropriate timer channel compare registerwhich is much more precise.
All these Arduino functions like analogWrite, analogWriteFrequency, and analogWriteResolution are convenient for beginners executing very simple and basic tasks. But when it comes to precise timing, the bare metal approach by writing directly to the timer registers gives much better and more precise results.
For a comprehensive documentation of the KINETIS FlexTimers, you should study the corresponding reference manual of either the Teensy 3.2, 3.5, or 3.6 (free download on the PJRC website), they are technically all equal.
 
Thanks for the tips.
However I am still confused why dropping the frequency from 2.5 kHz to 1 Hz caused the next pulse to remain high as shown in that image.

First I call analogWriteFrequency to change the prescaler, mod and clock source and then I call the analogWrite function to change the duty cycle as follows in my rising edge ISR:

void isr_FrL(){

if(speedChange_FrL == true){

analogWriteFrequency(FrL_pin,freq_FrL);
analogWrite(FrL_pin,PWM_value_FrL);
speedChange_FrL = false;
digitalWriteFast(33,HIGH);
}
}

I guess what I am asking is that is the change I am trying to make happening in the wrong order, where I am applying a frequency change to the previous cycles PWM but then the new duty cycle gets implemented on the next clock cycle? I thought since I am making the change on a rising edge ISR this would not happen.

Looking at the analogWriteFrequency function yields the following conclusion:
FTM2_SC = 0; //this line disables the timer
FTM2_CNT = 0; //this resets the timer counter to its initial value
FTM2_MOD = mod; //this reassigns the mod value which should get written right away I believe?
FTM2_SC = FTM_SC_CLKS(ftmClockSource) | FTM_SC_PS(prescale); // Since I am dropping to a very low frequency I use the low frequency clock that is selected earlier in the function


I am asking this more to help my understanding, just want to understand the strange scope result I got when dropping from 2.5 kHz to 1.3 Hz.

Thanks again,
Sahil
 
you might change duty first (analogWrite) then analgWriteFrequency(), since analogWriteFrequency stops AND starts the timer... on second thought that probably won't work, the analogWrite will set the duty based on current frequency.... so, you may to have manually manipulate the registers: stop the timer, set new frequency, calculate new duty, and set prescaler, mod, and duty (C0V) registers, then start the timer. There might be a tiny glitch for the number of cycles the timer is stopped ?

For 2.5khz to 1.3 hz, you'll configure FTM to use fixed frequency clock (31250 hz), no prescale, and MOD should be able to select close to the desired frequencies.
 
Last edited:
You're asking for help with a specific problem, but you haven't show the code which reproduces the problem.

I am asking this more to help my understanding, just want to understand the strange scope result I got when dropping from 2.5 kHz to 1.3 Hz.

This isn't how things work on this forum. When you want help with a specific problem related to how your code is implemented, we expect you to show the complete code. The idea is anyone here could copy the code into Arduino and run it on their board, to actually see the waveform problem you're experiencing. A partial code fragment isn't enough. Post a complete program anyone can copy into Arduino and upload to real hardware to recreate the problem.

We have the "Forum Rule" because it lets us help you much better, and it saves everyone a lot of time. Please, follow this rule and show the complete code to reproduce the problem.
 
Hi Paul,

I am aware of the rule, Unfortunately I cannot post the entire code because I am working on an application for my work and the code is proprietary, my boss will not let me share the whole thing. I apologize for the inconvenience.
I phrased my question earlier poorly I was asking a more general question:

If you call analogWriteFrequency and then analogWrite to change both frequency and duty cycle on a rising edge interrupt FTMx_CnV register gets updated on the next clock cycle but the mod register gets updated right away?
 
my boss will not let me share the whole thing.

We still expect you to trim your code to only a small but complete program which shows the problem. No need to post your entire proprietary program. Just put in a little effort to copy the specific part to a small program without any proprietary stuff. And please, double check that the small program really does reproduce the problem before you post it here.

I'm sure you can do this.
 
In the spirit of the LC hack in post #5, here is a Teensy 3.6 sketch using PWM pin 3 (FTM1 CH0) jumpered to pin 11 that changes the frequency and duty cycle every 4 periods. Start PWM with 1.3hz so core configures fixed frequency FTM clock (31250 hz). Flip back and forth between 2.5khz and 1.3 hz on the rising edge of the PWM cycle. Observe with scope or analyzer
Code:
// pin 3 PWM is FTM1 CH0  jumper to pin 11
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)

volatile int lineState = 0;
int lineCounter = 0;
volatile bool flagChange = false;

void isr() {
  lineCounter++;
  if (lineCounter == 4) {
    lineCounter = 0;
    lineState = (lineState + 1) % 2;
    flagChange = true;
  }
}

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(13, OUTPUT);
  analogWriteFrequency(3, 1.3);
  analogWrite(3, 128); // 50% duty
  delay(1000);
  PRREG(FTM1_SC);
  PRREG(FTM1_MOD);
  PRREG(FTM1_C0V);

  FTM1_C0V = 6;
  FTM1_MOD = 12;

  delay(1000);
  PRREG(FTM1_SC);
  PRREG(FTM1_MOD);
  PRREG(FTM1_C0V);

  attachInterrupt(11, isr, RISING);
}

void loop() {
  static uint32_t prev;
  uint32_t t = millis();


  if (flagChange == true) {
    if (lineState == 1) {
      digitalWriteFast(13, HIGH);
      FTM1_MOD = 12;
      FTM1_C0V = 12 >> 1 ;  // 50%   
    }
    else {
      digitalWriteFast(13, LOW);
      FTM1_MOD = 24038 - 1;
      FTM1_C0V = 24038 >> 1;  // 50%
    }
    flagChange = false;
  }

  if (t - prev  > 2000) {
    Serial.println(millis());
    PRREG(FTM1_SC);
    PRREG(FTM1_MOD);
    PRREG(FTM1_C0V);
    prev = t;
  }
}

Beware in the sketch, you cannot use analogWrite() to set the duty cycle immediately after setting FTM1_MOD. The FTM timer is still running and FTM1_MOD is buffered. analogWrite() reads FTM1_MOD in calculating the duty value, but that will end up using the old frequency since the read-back value of FTM1_MOD won't be updated til the end of the PWM cycle. In setting FTM1_C0V you need to calculate the duty value without referring to FTM1_MOD. (For the LC FTM/TPM sketch in post #5, there was no such buffering problem.?)

Below is snapshot from analyzer hooked to pin 13 (top channel) and PWM pin 3 showing the FTM registers are buffered.
pwmhi.png
pin 13 has been high since the setting of the FTM regs for high frequency, then you see the 4 2.5 khz pwm cycles, and notice that pin 13 drops low at rise for the last cycle and the regs are set for low frequency, but the PWM doesn't change til that final cycle is complete.
 
Last edited:
manitou, that's exactly what I meant above - finding a suitable clock source, so that only the mod and cov values have to be changed on the fly. But for pedagogic reasons, I decided against writing code for salisharma09 - he would have learned more by studying the reference manual and finding that by himself...
 
Thanks for the sample manitou - with Theremingenieur's post that is what I expected to see - but would never have bothered to look.

Wish I had a scope to see the output signal … suppose I need to write a sketch for a second Teensy … okay did that - and used Arduino Plotter for first time … there's some pedagogy for you Theremingenieur :)

Question: why not have the isr() set the updated reg values?

This plot is the (blue) measured analog output and scaled (red) duration of the time between transitions.
PWMtoggle.jpg
 
Status
Not open for further replies.
Back
Top