2Hz analogWriteFrequency on Teensy 3.1

Hi guys,
I'm playing around with analogWriteFrequency and seem to be having some issues...
I've got Teensy 3.2
Got pin 3 as PWM output to LED (actually sinking LED from Teensy's 3.3 Vout, so i use 1024-LEDR_duty to get inverse)
Have:
setup:
analogWriteResolution(10);

loop:
analogWriteFrequency(PIN_LEDR_out, LEDR_freq_set);
analogWrite(PIN_LEDR_out, 1024-LEDR_duty);


It seems that decreasing the WriteFrequency reduces the number of discrete duty cycle steps.
When LEDR_freq_set is around 15000 or above, brightness control with LEDR_duty appears to be smooth as normal
When LEDR_freq_set is around 11000, there appears to be about 5 discrete steps of LED brightness when varying LEDR_duty from 0 - 1024.
When LEDR_freq_set is 8000 or below, it seems to stay stuck on the brightness it had before, and adjusting LEDR_duty has no effect.
Anything below a few hundred LEDR_freq_set turns lights off completely. Basically there is very weird and erratic behaviour for LEDR_freq_set below 15000

ANy ideas?

Thanks
 
First, it's most probably not a good idea to set analogWriteFrequency again and again each time the loop runs because it will reconfigure the corresponding FlexTimer each time from A to Z, doing first some calculus for the needed parameters and then writing these to the FTM registers which will each time interrupt the PWM output cycle for a little moment which will lead to weird "phenomenons".
Second, you should understand how analogWriteFrequency() and analogWrite() work together : The Flextimer module is normally clocked by F_BUS which is = F_CPU for clock frequencies below 50MHz, and F_CPU/2 for clock frequencies between 50 and 100 MHz. If you operate your Teensy for example at 72MHz, F_BUS will thus be 36MHz. analogWriteFrequency() takes the desired frequency and looks which of the prescaler values will be needed to reach the goal with the highest possible resolution. Let's imagine that you want a frequency of 8000Hz: Since 8000Hz > (36MHz / 65536), no prescaler is needed. To come closest to your desired 8000Hz, the modulo value will be calculated to 4500. Thus a timer cycle will take 4500 of the 36MHz ticks which is exactly 8000Hz. (Calculating and setting that takes time, that's why this shouldn't be repeated too often).
That makes that the "real" PWM cycle value may vary between 0 and 4499. By choosing an analogWriteResolution of 10 bits (0 to 1023), you instruct the Teensy to scale your (0 to 1023) up towards (0 to 4499). Thus there is no reason for seeing a reduced number of discrete steps. It's rather the opposite: The more you lower the analogWriteFrequency, the more discrete steps you have.
 
Here is an example which has an outer and an inner loop. The outer loop starts with an analogWriteFrequency of 16000 and decreases it consecutively by 1000 down to 1000 before restarting again. The inner loop does then dim up your LED just using analogWrite without setting the (same) frequency again and again before the analogWriteFrequency is modified by the outer loop.
Code:
#define PIN_LEDR_out 3
#define MyResolution 10

void setup() {
  // put your setup code here, to run once:
  analogWriteResolution(MyResolution);
}

void loop() {
  // put your main code here, to run repeatedly:
  uint32_t myFreq, myDelay;
  for(int i = 0; i < 16000; i += 1000) {
    myFreq = 16000 - i;
    myDelay = 1000000 / myFreq;
    analogWriteFrequency(PIN_LEDR_out, myFreq);
    for(int j = 0; i < (1 << MyResolution); i++) {
      analogWrite(PIN_LEDR_out, 1023 - j);
      delayMicroseconds(myDelay); //give it the time to accomplish at least 1 PWM cycle, the Teensy risks to be too quick.
    }  
  }
}
 
First, it's most probably not a good idea to set analogWriteFrequency again and again each time the loop runs because it will reconfigure the corresponding FlexTimer each time from A to Z, doing first some calculus for the needed parameters and then writing these to the FTM registers which will each time interrupt the PWM output cycle for a little moment which will lead to weird "phenomenons".
Second, you should understand how analogWriteFrequency() and analogWrite() work together : The Flextimer module is normally clocked by F_BUS which is = F_CPU for clock frequencies below 50MHz, and F_CPU/2 for clock frequencies between 50 and 100 MHz. If you operate your Teensy for example at 72MHz, F_BUS will thus be 36MHz. analogWriteFrequency() takes the desired frequency and looks which of the prescaler values will be needed to reach the goal with the highest possible resolution. Let's imagine that you want a frequency of 8000Hz: Since 8000Hz > (36MHz / 65536), no prescaler is needed. To come closest to your desired 8000Hz, the modulo value will be calculated to 4500. Thus a timer cycle will take 4500 of the 36MHz ticks which is exactly 8000Hz. (Calculating and setting that takes time, that's why this shouldn't be repeated too often).
That makes that the "real" PWM cycle value may vary between 0 and 4499. By choosing an analogWriteResolution of 10 bits (0 to 1023), you instruct the Teensy to scale your (0 to 1023) up towards (0 to 4499). Thus there is no reason for seeing a reduced number of discrete steps. It's rather the opposite: The more you lower the analogWriteFrequency, the more discrete steps you have.

Hi Theremingenieur,

Yeah I should have figured that setting analogfrequency continuously would cause issues.
I made the loop run once a second instead and it now works fine, thanks!
I'll probably have it running more frequently in practice, but will put in an exception to not set the frequency if it is already currently at the desired value.
This means it will only change as often as the frequency variable changes

Cheers
 
I just sent the pull request on github.

Hey Theremingenieur,

I wonder if you might be able to add a similar patch for the Tone call/library as well? Finding that more useful as the positive and negative pulses are staggered relative to one another, whereas the writefrequency call just sends a quick plus/minus pulse with no way to stagger the two.

-Cere
 
Hey Theremingenieur,

I wonder if you might be able to add a similar patch for the Tone call/library as well? Finding that more useful as the positive and negative pulses are staggered relative to one another, whereas the writefrequency call just sends a quick plus/minus pulse with no way to stagger the two.

-Cere

And I can't go below 1Hz with the Tone call as it's written currently...
 
Nevermind. I figured it out using the analogWriteFrequency stuff. Duh.

:) I'd prefer not touching at the tone() function. That square wave sound is already ugly enough for my musical ears and I see absolutely no need to extend it to frequencies which are below the limits what a human ear can perceive as sound.
 
Well hrm. I'm still in a bit of a pickle here. I am looking to drive a current amplified (sloooow) sine wave audio signal coming from the teensy on the order of .1hz. But the teensy audio library doesn't really support this frequency in a sine tone from what I can tell. I <could> use the analogwrite(pin,PWM) values that Theremingenieur's additions now support, but to get a nice smooth sine shape, I essentially need a low pass filter that is pretty unachievable via capacitor values. Further, if I want to send this signal into voltage->current source converter I need to not have any negative signal. And the usual capacitive coupling trick isn't going to work for low frequency signal like .1Hz. So hrm. Plan B?

Could I achieve the goal of creating a low frequency (.1hz) sine wave via the servo library? From the examples I see of the servo library, I see that I can (at least) control the speed that the Servo object moves to the new position (hence sort of simulating a sine-like motion) but it's not clear what signal amplification scenario/board/circuit would be appropriate of the ones that are commercially available. Would the H-bridge board from Pesky products work? https://www.tindie.com/products/onehorse/drv8837-h-bridge-dc-motor-driver/

Thanks,
Hopefully it's clear what I am wanting to do.
 
I am looking to drive a current amplified (sloooow) sine wave audio signal coming from the teensy on the order of .1hz. But the teensy audio library doesn't really support this frequency in a sine tone from what I can tell.

How did you arrive at that conclusion?! The audio library does indeed support low frequency synthesis.

First, watch the tutorial video. The part about oscillators begins at 25:04. You can see the use of an oscillator as a LFO in this tutorial. There's no dedicated LFO object, because all the oscillators support low frequency.

As a quick check, I just tried this:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine   sine1;          //xy=170,106
AudioOutputAnalog        dac1;           //xy=341,120
AudioConnection          patchCord1(sine1, dac1);
// GUItool: end automatically generated code

void setup() {
  AudioMemory(8);
  sine1.amplitude(0.9);
  sine1.frequency(0.1);
}

void loop() {
}

It does indeed work. Here's the waveform at the DAC pin. Notice the time scale is 2 seconds per division...

file.png

The one important caveat is you must use the DAC without a capacitor. The audio shield has 2.2uF capacitors. If you try this with the audio shield, you'll need to modify the hardware. The library will produce the 0.1 Hz signal, but you need to connect without the capacitor to actually get access to it.
 
Uh...errr, in retrospect, I can't exactly remember how I came to that conclusion but I thought I had tested it and seen bad results. I also did manage to blow up my teensy right around that time, so perhaps the two incidents got cross wired in my brain. Thanks for straightening me out on that, Paul.

I did notice something that didn't seem right wrt PWM output on a simple sine --> pwm piece of code on the Teensy 3.2 (didn't test it on 3.1).

<code>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine sine1; //xy=505.8888854980469,119.88888549804688
AudioOutputPWM pwm1; //xy=874.8888854980469,319.8888854980469
AudioOutputAnalog dac1; //xy=877.8889465332031,212.8888816833496
AudioConnection patchCord1(sine1, dac1);
AudioConnection patchCord2(sine1, pwm1);
// GUItool: end automatically generated code

void setup() {
AudioMemory(4);
delay(50);
// put your setup code here, to run once:
Serial.begin(38400);
//dc1.amplitude(1.0);
sine1.frequency(50);
sine1.amplitude(1.0);
//mixer1.gain(0,.5);
//mixer1.gain(1,.5);
}

void loop() {
// put your main code here, to run repeatedly:

}
</code>

The only PWM output I could see on my oscilloscope was on pins 4 and 5, but nothing on the other PWM pins, well not on the adjacent ones anyway. Nothing on pin 3 and nothing on pin 6. As I read it this signal is supposed to go out all PWM pins. Even if it was just out of the two that the pwm object instructions show in the Audio GUI creator, It's not going out of pin 3 and 4 as the picture shows. Puzzler.
 
I am using the on-board DAC btw, not the DAC from the audio card. Another weird problem I notice is that, if I use the mixer on setup like so:

<code>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformDc dc1; //xy=400.8888854980469,340.8888854980469
AudioSynthWaveformSine sine1; //xy=455.8888854980469,199.88888549804688
AudioMixer4 mixer1; //xy=675.8888854980469,296.8888854980469
AudioOutputPWM pwm1; //xy=874.8888854980469,319.8888854980469
AudioOutputAnalog dac1; //xy=877.8889465332031,212.8888816833496
AudioConnection patchCord1(dc1, 0, mixer1, 1);
AudioConnection patchCord2(sine1, 0, mixer1, 0);
AudioConnection patchCord3(mixer1, dac1);
//AudioConnection patchCord4(mixer1, pwm1);
// GUItool: end automatically generated code


void setup() {
AudioMemory(8);
delay(50);
// put your setup code here, to run once:
Serial.begin(38400);
//dc1.amplitude(1.0);
sine1.frequency(15);
sine1.amplitude(1.0);
mixer1.gain(0,1.0);
mixer1.gain(1,1.0);
}
</code>

I get a normal (expected) signal from the DAC, but if I start increasing the gain of the mixer to something like:
mixer.gain(0,255);
mixer.gain(1,255);

The signal becomes pretty not sineish and certainly does not increase in magnitude.

Am I doing something obviously wrong here? Is the mixer only meant for the audio card use?
 
As I read it this signal is supposed to go out all PWM pins.

Use the design tool, click on the "pwm" object, and then read the docs in the right-side panel. Look for the Hardware section with schematic! Only those pins get output.


I get a normal (expected) signal from the DAC, but if I start increasing the gain of the mixer to something like:
mixer.gain(0,255);
mixer.gain(1,255);
The signal becomes pretty not sineish and certainly does not increase in magnitude.

Well, what do you expect to happen when you amplify the signal 255 times? Signals can never be more than 1.0 amplitude! Unless the signal started out very tiny, you're certainly going to get clipping. Same thing happens on real analog mixers, and even regular stereo systems. Turn the gain/volume up too much, and unless the signal was tiny, it's going to clip/distort when you try to go over the maximum level.

Please watch parts 2-2 and 2-3 of the tutorial video. Skip forward to 9:06. Pay close attention to the talk about mixer gain settings.
 
Paul,

My mixer documentation states: "Adjust the amplification or attenuation. "channel" must be 0 to 3. "level" may be any floating point number from 0 to 32767. 1.0 passes the signal through directly. Level of 0 shuts the channel off completely. Between 0 to 1.0 attenuates the signal, and above 1.0 amplifies it. All 4 channels have separate settings."

It might be a tad clearer to better explain how the gain level can be a floating point number from 0 to 32767 while simultaneously not going above 1.0?? I am quite possibly not the only english speaker who finds this sentence confusing.
 
? Its a mulitplication. I'm not a english speaker :)
x * 0 = 0 -> silence
x * 1 = x -> no change
x * 2 = 2x -> double value
..
the range for the value is signed 16 Bit.
So..if your current signal is, lets say near 32000, and you multiply it by 255, what do you expect? :) Does the result fit in 16 Bits (signed)?
 
Last edited:
Ok, currently we have this:

1.0 passes the signal through directly. Level of 0 shuts the channel off completely. Between 0 to 1.0 attenuates the signal, and above 1.0 amplifies it.

I'm open to changing this to make it clearer. Can anyone suggest specific wording?

explain how the gain level can be a floating point number from 0 to 32767 while simultaneously not going above 1.0?? I am quite possibly not the only english speaker who finds this sentence confusing.

Setting above 1.0 cause the mixer to amplify your signal. You can amplify as much as 32767 (which is a pretty insane amount to amplify).

I should point out, we do also have this text in the mixer documentation:

Signal clipping can occur when any channel has gain greater than 1.0, or when multiple signals add together to greater than 1.0.

I'm open to also editing or expanding this to make it clearer. Again, I really need some suggestions about what wording would be easier to understand. I'm much too close to the inner working of the library to be able to see what words could communicate these concepts clearly to people who are absolutely new to using the library and to digital signal processing in general.
 
If I now understand what is trying to be said, I would say something like:

Mixer gain can be increased in a range from 0 to 32767, however the total gain of a given channel should (generally) not go above 1.0 when considering the combined (multiplied) gain of the input source and the gain setting of the mixer channel.
 
Last edited:
I think the wording as it was is pretty clear. If anything I think the addition of some diagrams may help people understand some of these things, such as a picture of a sine wave > gain of 0.25 / gain of 1.0/ gain of 2.0 with clipping and maybe even one with two signals causing clipping when added together. Overall I think diagrams help language barrier.
 
A more elegant way would be to convince Paul to modify the original analogWriteFrequency as follows, so that the alternative clock would be used automatically when setting very low frequencies: ...

Ought this still work on TeensyLC in recent Teensyduino? When setting 1.0 Hz i get reasonable debug output ftmClockSource = 2, F_TIMER/ftm_Clock = 31250, prescale = 0, mod = 31249. Except that there is no signal output!

Already tried MCG_C1 |= MCG_C1_IRCLKEN which should enable the internal reference clock and MCG_C2 &= ~MCG_C2_IRCS which should select the slow internal reference clock (31250 Hz) instead of the fast internal reference clock (4 MHz). But how this can be linked to the TPM i couldn't figure out.

Suspicious in analogWriteFrequency() is also FTM_SC_CLKS(ftmClockSource) because that sets TPMx_SC[CMOD] where 0b001=TPM counter increments on every TPM counter clock, 0b010=TPM counter increments on rising edge of TPM_EXTCLK, according to the manual. So this **_CLKS() does not set clock source, just mode.

What works is 10.0 Hz to analogWriteFrequency, but that switches back to the first timer so ftmClockSource = 1.

Maybe I misread something and <10 Hz support was intended for other Teensy than the LC version...?

My test code is below:

Code:
#define LOW_FREQ 1.0f  // 1.0f Hz does not produce output, 10.0f Hz produces blinking LED

int led_onboard_pin = 13; // teensy-lc LED0
int ctrl_pin_lofreq = 6;  // FTM0, teensy-lc 6,9,10,20,22 or 23

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

void reinit()
{
  pinMode(led_onboard_pin , OUTPUT);
  pinMode(ctrl_pin_lofreq, OUTPUT);
  analogWriteResolution(16);
  analogWriteFrequency(ctrl_pin_lofreq, LOW_FREQ);
  analogWrite(ctrl_pin_lofreq, 32768);
  Serial.println("Initialized");
}

void loop() {
  char b;

  // Check "frequency" output and reproduce it on LED
  if (digitalRead(ctrl_pin_lofreq)) {
    digitalWrite(led_onboard_pin, HIGH);
  } else {
    digitalWrite(led_onboard_pin, LOW);
  }

  // Handle USB Serial
  if (Serial.available()) {
    b = Serial.read();
    if (b == 'r') {
      reinit();
    }
    if (b == 's') {
      Serial.print("IRC is ");
      if (MCG_S & 1) {
        Serial.println("4 MHz");
      } else {
        Serial.println("32 kHz");
      }
      Serial.print("MCG_C1 = ");
      Serial.println((int)(MCG_C1 & 0xFF)); // 32 = 0x20 = 0b0010.0000 : 00b ClkSrc FLL/PLL, 100b div16, extref, MCGIRCLK inactive,
    }
  }
}
 
Oh great yeah the topic says Teensy 3.1! For some reason I didn't catch that earlier :p Then no wonder if <10 Hz does not work just via analogWriteFrequency() on Teensy LC. Maybe need to generate 31250 Hz output on another TPM and feed that back as an "external clock", using TPMnCLKSEL to choose pin TPM_CLKIN0 or TPM_CLKIN1 as the input of that previously output clock.
 
I must admit that when committing my few modest contributions to the Teensy world, I've always targeted the 3.x series and I never cared about the LC. Seen that an original Teensy 3.2 is ways more powerful and cheaper than the original Arduino UNO, there is (in my eyes) no reason to look at the LC or at the 8bit Teensy 2.x series.
I admire Paul for his patience supporting that legacy stuff, but that wouldn't be my thing.
The Teensy 3.x ecosystem has the advantage of a more or less common H/W infrastructure which allows often a "one fits all" development, while the Cortex M0 architecture of the LC seems less close to that and requires often alternative development paths which (still in my personal opinion) aren't worth it when you can get 4 to 8 times its power in a T3.2 for just $8 more.
 
The slowest working PWM frequency on Teensy LC is 48e6 / 128 / 65536.

Code:
void setup() {
  analogWriteResolution(16);
  analogWriteFrequency(6, 5.7220459);
  analogWrite(6, 32768);
}

void loop() {
}

The code is trying to use a slower clock source, but it seems that only works on Teensy 3.x. :(
 
Back
Top