Audio PWM output to operate on a 20kHz frequency?

Status
Not open for further replies.

ceremona

Well-known member
Hi Paul (and other Teensy experts)

Last I checked in the Audio API documentation in the Design tool. The PWM output only works with a carrier frequency of 88.2 kHz. I am using PWM modulation to control motors and, as such, most motor control drivers don't work at anything over 20kHz. Wondering if there is a way to make this PWM function work on 20kHz? Also, would be great if it would work on more than one pin (or if it could be documented more clearly that this only works on the pins in the design tool diagram)??

Thoughts?

Thanks,
Cere
 
RTFM? ... uhm ya thanks... for that is a passive aggressive bit of non-information.

I already am using this PWM library and it works fine up to a point. The problem is that if I wanted to use audio-speed frequency modulation it seems that I need to wrangle with the code that deals with the timing loop and/or use the timing library to get a dynamic changing modulation. I find the Timer library a tad inscrutable when it comes to this kind of thing as I have looked through a number of examples of other people using it, etc. I would just love to be able to use the PWM function from the audio library as it's meant to be used.
 
Please accept my apologies, couldn't imagine that someone wanted to drive a motor with audio signals. And I have still difficulties in seeing what an application of that could be.
 
In that case I'd use analogWriteFrequency(PIN, 20000) in the setup() to configure the PWM frequency for the corresponding pin and an interval timer with a period of 50us which calls an interrupt : IntervalTimer myTimer; myTimer.begin(writeNextSample, 50);

Now you have just to define the interrupt handler :
void writeNextSample() {
uint16_t nextSample = //get the next audio sample from wherever, stream, buffer...
analogWrite(PIN, nextSample)
}
and you are done.
 
Actually, there are lots of uses for a more flexible PWM carrier frequency option. Each high current source that is driven by PWM signals seems to have it's own idiosyncratic PWM carrier frequency. LOTS of LED drivers work off of 8MHz, some 2-20kHz. And I've seen lots of people modulate LEDs to the vocode of a voice, song, etc.
 
Indeed the audio library is probably not what you really want. It works by processing 128 sample blocks, so control from the Arduino sketch is limited to changing settings every 128 samples. It's also designed for 44.1 kHz sample rate, so even if you edit the PWM audio output object, going slower than 44.1 kHz will be quite a challenge!

To get started, I'd also suggest you use analogWriteFrequency() and analogWrite(). If your code is simple, perhaps you can do everything you need by just checking an elapsedMicros variable for timing and updating the PWM with analogWrite? That's a *lot* simpler than messing with interrupts.

Of course you can use interrupts, and the easy way to do this is with IntervalTimer. But interrupts always make everything much more complicated, so you should avoid them unless your program absolutely has to do something for a long time, where "long" is a period of time where you can't accept a lack of updates to the PWM (using whatever criteria you have to do this.... hard to give more specific advice not knowing anything about your PWM control strategy)
 
Ok, well modifying the PWM duty cycle via loop speed is what I am doing already and it works for the most part. Yes, the Timer library just makes things too hard what with not being able to print things out and share variables, etc. I guess I wonder what the use of the PWM object is in the Audio library if not for something like what I had in mind? If it's always an option to just use the analogWrite pwm function then why have the PWM output option at all in the Audio library? Just curious.
 
The idea of the PWM output in the audio library is to have something like an additional DAC output for a digital audio stream created by other objects in the audio library at a constant sample rate. The audio library is something like a more or less closed (if you aren't willing to develop new objects) ecosystem.

But the PWM object in the audio lib is just a very special and restricted application case (I see it rather as a kind of 'demo' app) of what can be done with the Teensy 3.x's FlexTimers. Did you take the time to read the K20 reference manual? It explains the almost infinite variability of the 3 Flextimers in the Teensy 3.2, which are highly configurable, and whose application goes ways beyond simple PWM generation. Writing the modulo, prescaler and compare values in the corresponding registers does not only allow you to set the duty cycle, but also the overall cycle period and you may understand the relationship between the PWM frequency and resolution.

But it's perhaps not needed to dive that low into the hardware. If you would tell EXACTLY what you intend to do, you could get better help here. Apparently there is a misunderstanding when you talk about a Timer library, there are timer objects built in the code, external libraries are not needed for the Teensy 3.x.

Another thing is, you write "modifying the PWM duty cycle via loop speed is what I am doing already" which sounds either confused or confusing since the loop speed has nothing to do with the duty cycle, the latter is set by analogWrite and then remains unchanged (PWM goes automatically on with that duty cycle) until you set a different duty cycle with a new analogWrite command. If you want to modify the duty cycle at regular intervals, you really should use an intervalTimer object because there is no warranty that each loop takes exactly the same time because of the sysTick interrupts and the serial event handling, both in the background.

I still do not see why you'd need such high PWM frequencies for motor control. AFAIR, the efficiency of PWM driven DC motors goes down when the PWM frequency becomes too high because of inductive losses. The only case where I could imagine the need of higher PWM frequencies is driving a tri-phase synchronous motor with 3 PWM outputs, generating 3 sine waves with 120° phase shift. But even then, at 400Hz and 15 samples per cycle (which is already luxurious), we are only at a 6000Hz PWM frequency... But I'll stop guessing around now until you describe your project in a detailed manner.
 
Well I suppose a code post is in order to be concrete about what I have done so far and to share with others some code on how to do a sine modulated PWM signal where a potentiometer sets the sine frequency:

Code:
const int F1 = 3;
const int F2 = 4;
const int F3 = 5;
const int F4 = 6;
const int BREAK=13;  //pin 13 on teensy
const int POT = 14;
int potval=0,val1,val2;

 float clock = 95.99899F;    // Clock Calibration Factor for my teensy 3.1 at 96 MHz <Best>
 float twopi = 6.2831853F;
 float sineFactor;
 //float cosineFactor;
 int steps = 800;            // number of phase angle steps for sinewave generation
                             // a smaller number will here allow to go to higher frequency
                             // but will result in a steppier sine curve.
int i=0;
float clock_steps = (clock * (float)steps * 0.5f);                        
float amplitude;                        
float phase;
float phasestep = twopi/((float)steps);
volatile float period;

float amplitudeValue[1000]; //Initialize array 1.25 * steps. 
                            //This is allows both for 2PI radians of sine,
                            //and 2PI radians of cosine shifted + PI/2
void setup() {
  pinMode(F1, OUTPUT);
  pinMode(F2, OUTPUT);
  pinMode(F3, OUTPUT);
  pinMode(F4, OUTPUT);
  pinMode(BREAK, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(POT, INPUT);
  analogWriteResolution(12);
  Serial.begin(57600);
  fillArray();
  analogWriteFrequency(F1,20000);
  analogWriteFrequency(F2,20000);
}

void fillArray(){
  for (int i=0; i <= 999; i++){
  phase = (float)i*phasestep;
  amplitudeValue[i]=sin(phase);
  }
  
/* // Print array to Serial Monitor
  delay (5000);
  for (int i=0; i <= 799; i++){
  phase = (float)i*phasestep;  
  Serial.print(phase,8);
  Serial.print(",  ");
  Serial.print(amplitudeValue[i],8);
  Serial.print(",  ");
  Serial.println(amplitudeValue[i+200],8);
  }
*/
}

void loop() {
  digitalWrite(BREAK,HIGH);  //enable motor...or not
  sineFactor = (float)analogRead(POT)/1023.0f;      //get analog value from pots scale to between 0 and 1

      i = i + 1;
      if (i >= 799) i = 0;
      analogWrite(F1, sinewave(i));
      analogWrite(F2, sinewave(i));  
      analogWrite(F3, sinewave(i+200)); //cosine
      analogWrite(F4, sinewave(i+200)); //cosine
      delayMicroseconds(4000*sineFactor);
}

float sinewave(int i) {
  amplitude = 2000.0F*(amplitudeValue[i])+2050.0F;
  return amplitude;
}

This code is heavily borrowed from a very informative bit of code the R. Wosniak posted on how to efficiently create a frequency locked loop.

Anyhow. I definitely will not be going into the weeds of a the document you mentioned. I'm just not that level of coder. Conceptually, I was thinking that I would use the Audio libraries FFT output of some voice input to modulate the motion of xyz thingy. So, you're right, that I wouldn't be using normal audio frequencies to drive a motor directly. And yes, that wouldn't make much sense. I think Paul helped straighten me out a bit on what's the best direction for what I want to do via just varying the loop delays.

Thanks.
 
Hate to resurrect an old thread, but my question is very similar. My goal is create a high-voltage singing arc, using the Teensy 3.1 to drive a half-bridge inverter which drives a flyback transfomer.

So far I can create a strong and quiet arc in the frequency range referenced above. This is being done with two PWM pins with complementary 50% duty-cycle output (writing FTM0 directly to do this).

Now I want to modulate this carrier signal using the audio library or maybe the AnalogWrite function. I want modulate the duty-cycle with the audio sample to achieve the desired effect. I thought of hacking the AudioOutputPWM object by adjusting the MODULO register to reduce the 88kHz DMA request, but from the comments above it's probably not that easy.

My project topology might look like this: LineLevelAudio->"ADC->Mixer->AudioOutputPWM"->HalfBridge

I would appreciate any suggestions or tips in to how to do this effectively.

Thanks.
 
Status
Not open for further replies.
Back
Top