Pages: [1]   Go Down
Author Topic: Changing PWM frequency  (Read 1207 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Full Member
***
Karma: 0
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

I've been working on a code for a quadcopter, and I've met alot of problems along the road. Seems like when I fix one thing, another problem occurs..
However, the problem I have now is the refresh rate output to the ESC.
Until now I have been using the servo library, but it only generates a frequency of 50hz, which is way to low for a quadcopter (I believe that's why my quad won't stabilize itself good enough), my ESC (turnigy trust 55A) can handle upto 433hz refresh rate.

I've been looking around for a couple of hours, reading at the playground section about changing the PWM frequency to make my own "library" instead.

1.
Now to the fact. There are three timers that I can adjust, timer0 (pin 5,6), timer1 (pin 9,10) and timer2(pin 11,3).
Quote
If you change TCCR0B, it affects millis() and delay(). They will count time faster or slower than normal if you change the TCCR0B settings.
So I will skip the timer0, and only set timer 1 and 2 (4 pins, enough for a quadcopter).

And to set the desired frequency, I've found an example code:
http://playground.arduino.cc/Code/PwmFrequency
Quote
The base frequency for pins 3, 9, 10, and 11 is 31250 Hz.
o The base frequency for pins 5 and 6 is 62500 Hz.

The divisors available on pins 5, 6, 9 and 10 are: 1, 8, 256, and 1024.
The divisors available on pins 3 and 11 are: 1, 8, 32, 128, 256, and 1024.
But these divisors wont give me a frequency of 400hz. Any suggestion on how I can accomplish that?


2.
We assume that the PWM refresh rate 400Hz = 2500micros.
The pulse that we want to send to the engine = 1500micros.

How would this routine look like?


It's soon 6 am, hard to sleep when you have all these kind of questions in your head!
Hope that some1 will be able to help me smiley

Cheers!

Edit:
Sorry for misspelling alot, really tired  smiley-sweat
Logged

Montreal
Offline Offline
Faraday Member
**
Karma: 29
Posts: 2591
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hack a servo library?
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That was also my first thought, but I read on several places that it isn't really good idea because they could only achieve a maximum frequency of ~150hz, and higher than that would bring jitter to the signal (with servo library) ..
Logged

Offline Offline
God Member
*****
Karma: 25
Posts: 526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM

Scroll down to where it says "Varying the timer top limit".

This can absolutely be done, and give you very accurate 400Hz timers, but there is an issue.  If you do it this way, and you need variable duty cycle, you only get one pin per timer.  If you need all 4 pins for your copter, it might be an issue.

If you use the 16 bit timer, and you use a scaler of 64, a top limit of 625 and a PWM compare value of 375, you will get a pulse of width 1500 uSec every 2500 uSec.

The 8 bit timer would not be quite as accurate.  Would you be OK with a 1504 uSec pulse every  2496 uSec?  That's 400.25 Hz.
« Last Edit: April 27, 2013, 12:26:57 am by TanHadron » Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes I actually need all of the 4 pins. I took a look into the aeroquad section and noticed that they have a similar solutoion to this, PWM with timers.
Code can be found here:
https://github.com/AeroQuad/AeroQuad/blob/master/Libraries/AQ_Motors/Motors_PWM_Timer.h

I copied the neccessary parts to give it a try.

Code:
#define PWM_FREQUENCY 50   // in Hz
#define PWM_PRESCALER 64

#define PWM_COUNTER_PERIOD (F_CPU/PWM_PRESCALER/PWM_FREQUENCY)

void initializeMotors(){
    //http://arduino.cc/en/Hacking/PinMapping168 /328
    DDRB = DDRB | B00001110;                                  // Set ports to output PB1-3
    DDRD = DDRD | B00001000;                                  // Set port to output PD3
   
    commandAllMotors(1000);                                     // Initialise motors to 1000us (stopped)
   
    // Init PWM Timer 1  16 bit
    TCCR1A = (1<<WGM11)|(1<<COM1A1)|(1<<COM1B1);
    TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);
    ICR1 = PWM_COUNTER_PERIOD;
    // Init PWM Timer 2   8bit                                 // WGMn1 WGMn2 = Mode ? Fast PWM, TOP = 0xFF ,Update of OCRnx at BOTTOM
    TCCR2A = (1<<WGM20)|(1<<WGM21)|(1<<COM2A1)|(1<<COM2B1);    // Clear OCnA/OCnB on compare match, set OCnA/OCnB at BOTTOM (non-inverting mode)
    TCCR2B = (1<<CS22)|(1<<CS21);                              // Prescaler set to 256, that gives us a resolution of 16us
    // TOP is fixed at 255                                     // Output_PWM_Frequency = 244hz = 16000000/(256*(1+255)) = Clock_Speed / (Prescaler * (1 + TOP))
}

void writeMotors() {
    OCR2B = motorCommand[MOTOR1] / 16 ;                       // 1000-2000 to 128-256
//    OCR1A = motorCommand[MOTOR2] * 2 ;
//    OCR1B = motorCommand[MOTOR3] * 2 ;
//    OCR2A = motorCommand[MOTOR4] / 16 ;
}

void commandAllMotors(int command) {
    OCR2B = command / 16 ;      //MOTOR1
//    OCR1A = command * 2 ;       //MOTOR2
//    OCR1B = command * 2 ;       //MOTOR3
//    OCR2A = command / 16 ;      //MOTOR4
   
}

And main loop:
Code:
void setup(){
  Serial.begin(9600);
  initializeMotors();
}

void loop(){
 
  for(int x = 1000; x < 2000; x++){
      motorCommand[MOTOR1] = x;
      writeMotors();
      delay(200);
  }
 
    for(int y = 2000; y > 1000; y--){
      motorCommand[MOTOR1] = y;
      writeMotors();
      delay(200);
  }
 
}

I've tried to change the
Quote
PWM_FREQUENCY
parameter to even as low as 50hz, but the motors will just beep continously.

To see what the maximum refresh rate is I made a manual sketch where I can change the frequency and just put the digital pin as 1s and 0s.
The result was a maximum frequency of 285hz.
Code:
int freq = 3500; //in micros

void setup(){
  Serial.begin(9600);
  for(int x = 0; x <1000; x++){
  digitalWrite(3, 1);
  delayMicroseconds(1000);
  digitalWrite(3, 0);
  delayMicroseconds(freq);
}

}

void loop(){
 
  for(int x = 1000; x < 2000; x++){
        digitalWrite(3, 1);
        delayMicroseconds(x);
        digitalWrite(3, 0);
        delayMicroseconds(freq);
  }
 
    for(int y = 2000; y > 1000; y--){
        digitalWrite(3, 1);
        delayMicroseconds(y);
        digitalWrite(3, 0);
        delayMicroseconds(freq);
  } 
}

Can any1 see whats wrong with the first code, from aeroquad?

Cheers
Logged

Offline Offline
God Member
*****
Karma: 25
Posts: 526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This line seems to be using external clock.

Code:
    TCCR2B = (1<<CS22)|(1<<CS21);                              // Prescaler set to 256, that gives us a resolution of 16us

To use the main clock, try this:

Code:
    TCCR2B = (1<<CS22);                                        // Prescaler set to 256, that gives us a resolution of 16us


The PWM_FREQUENCY parameter is only used on the 16 bit counter.  Also...

Code:
    TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);

doesn't match what my spec sheet says it should be for prescaler of 64.  Try:

Code:
    TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10);


You are using an Arduino Uno?
« Last Edit: April 27, 2013, 09:07:18 pm by TanHadron » Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I'm using an arduino uno.

I replaced the parts with your code but I'm still getting beeps. Let me see if I've got it correct now, theoretical.
Let us assume I want a refresh rate with 250hz.

Quote
* We are using the 16bit timer (timer1)
* Prescaler set to 64
* To get a refresh rate of 250 (refresh rate each 4000uS) hz we put the top limit as 1000 (16000000(1000*64) = 250).
* Then we can use the OCRnA/OCRnB (I believe these are the compare values) to change the compare match and duty cycle.
- Lets say we want a pulse width of 1000uS: We want 25% of refresh rate => OCRnA = 16 => 16/prescaler => 16/64 = 0.25 = 25%.
--                                               2000uS:             50%                      => OCRnA =  32 => 32/prescaler => 32/64 = 0.5.

We know that the input from receiver is between 1000-2000.
OCRnA = inputReceiver * 0,016.

So this part is for the 8bit timer (timer2), now we want to do something equal to the 16bit timer (timer1).
* We know that the top limit is 256 by default.
* So to get a refresh rate of 250hz => 16000000/(topLimit*prescaler) = 250hz => prescaler = 16000000(250hz*256(toplimit)) =~ 250 = 256 as prescaler.

Please correct me if I'm doing something wrong, it's confusing with different timers and to make them match eachother.
However, how could I implement this? Is the Fast PWM Mode with OCRA top way to go? (http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM). If it is, how would I do it for timer2?

Thanks

Edit:
Okey, seems like the 8bit is giving me continously beeps.
I'm using timer1 now instead with 250hz, with a prescaler of 64 and also changed the lines to what you specified.
Im getting the normal startup beeps, that's really good  but after that it doesn't spin.
Code:
   OCR1B = motorCommand[MOTOR3]*2;
where motorCommand = 1000-2000.
Tried with the theory above with 0.016 but does not work with that at all.
« Last Edit: April 28, 2013, 12:12:19 pm by freak174 » Logged

UK
Offline Offline
Tesla Member
***
Karma: 125
Posts: 7200
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Get the facts "from the horse's mouth" - the Atmel datasheet doc8161.pdf

...R
Logged

Offline Offline
God Member
*****
Karma: 25
Posts: 526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So I read up on how your ESC works, so I would know more about what kind of square wave you're looking for.  Let me see if I understand it correctly.

The ESC expects a pulse with a high duration of somewhere between 1000uS and 2000uS.  1000uS = full stop, and 2000uS = full throttle.  The values between may or may not be linear, but in any case you can tell when your motor is running at 1250uS and running at 1/4 power,  and 1500uS which is half power, and 1750uS which is 3/4 power.  That's what your test program with the "manual sketch" was testing, and you got that working OK.

There is some question about the space between pulses.  Or, to be more accurate, the frequency of the pulses.  It seems like it isn't particularly picky about it.  There's a big range that will still work.  Somewhere between 50Hz and 285Hz, although you mentioned that your ESC is supposed to work with pulses up to 433Hz.  This is probably not a super big deal, except that you want it to work faster than 50Hz.  Probably somewhere around 244Hz would work, if you could get the Arduino to put out those pulses.

You need 4 pins, separately controllable.  You would like to be able to pass a number between 1000 and 2000 inclusive, and the function should start putting out pulses of that many microseconds.


If all that is correct, I think I understand how it should work.  This should be doable.

The 8 bit timer is not as flexible as far as the pulse frequency, and not as fine pulse duration, either.  However, I think it can be done.  Using a prescaler of 256, and the default top of 255, the frequency should be 16MHz/256/256, which is 244.140625Hz.  That should work.  With a prescaler of 256, the timer clock cycles are 16 uS each.  So the actual values for the PWM compare should be 62.5 for 1000uS pulse and 125 for 2000uS pulse.  The code looks like it does that correctly, but you can try to put out the values directly and see if it makes any difference.

But let's check and make sure it's configuring the timer correctly first.

WGM2[2:0] should be set to 011, which is Fast PWM, Top = 0xFF, Update of OCRx at BOTTOM, and TOV Flag set on Max.
COM2A[1:0] should be 10, which is Clear OC0A on Compare Match, set OC0A at BOTTOM, (non-inverting mode).
COM2B[1:0] should be 10, which is Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).
CS2[2:0] should be 100, which is prescaler = 256.

So, TCCR2A should be COM2A1, COM2B1, WGM21, WGM20 = 0xA3
TCCR2B should be CS22 = 0x04

OCR2B should be 63 for off, 125 for full throttle.

That all looks right to me.  But you're saying it's still just beeping.  Could you post the current code?

I'll look at the 16 bit timer as well.  I don't think you need to use the OCRA top.  The 16 bit timer in WGM mode 14 uses the ICR1 as the top, and that should work.
Logged

UK
Offline Offline
Tesla Member
***
Karma: 125
Posts: 7200
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have recently written a sketch in which Timer1 produces a 7-channel PPM signal to drive a R/C transmitter. Any one of the 7 pulses can drive a single standard servo. As far as I know the receiver just separates the pulses and directs them to the appropriate servo or ESC.

The whole process works because the servos expect a pulse every 20millisecs and there is plenty of time in the dead space to accommodate the other 6 pulses interspersed with 0.3ms spacing pulses.

Thus there should be no difficulty generating pulses for 4 ESCs for a quadcopter - the only adaptation of my sketch would be reducing the number of channels and some code to route the pulses to the appropriate pins.

I have already uploaded to sketch on this Thread http://arduino.cc/forum/index.php/topic,163199.msg1220724.html#msg1220724

...R
Logged

Offline Offline
God Member
*****
Karma: 7
Posts: 635
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@ freak174, I have a question about that high refresh rate for your ESC. Do you think it has an utility? I think a quad needs an ESC refresh rate as high as the cycle for the IMU but no higher. Has your IMU a cycle of 400 Hz or 250 Hz?
Logged


Pages: [1]   Go Up
Jump to: