Take the 2-minute tour ×
Arduino Stack Exchange is a question and answer site for developers of open-source hardware and software that is compatible with Arduino. It's 100% free, no registration required.

I need to obtain a PWM frequency of at least 125 kHz. I plan to drive a pair of MOSFETs using this PWM as the driver signal. The below code gives a 1 kHz frequency. Can I just change the delay values to obtain a lower time period, and thereby a higher frequency?

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  digitalWrite(13, HIGH);
  delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz
  digitalWrite(13, LOW);  // Can I change the delay to 1 and 9 for a totaL T=10 µsec and hence f = 100 kHz?
  delayMicroseconds(900);
}

Update

On looking up the answers provided, I stumbled across a few tutorials. I used the code below and it resulted in 125 kHz and 1.6 MHz (measured with a CRO, not simulation). But the code was supposed to provide 250 kHz and 8 MHz.

My requirement of 125 kHz is satisfied, but I am just curious to know why the sketch not working as it should.

Second sketch:

// A sketch that creates an 8 MHz, 50% duty cycle PWM and a 250 kHz,
// 6-bit resolution PWM with varying duty cycle (changes every 5µs
// or about every period.

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  pinMode(3, OUTPUT); // Output pin for OCR2B
  pinMode(5, OUTPUT); // Output pin for OCR0B

  // Set up the 250 kHz output (but cro measures only 125 kHz)
   TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
   TCCR2B = _BV(WGM22) | _BV(CS20);
   OCR2A = 63;
   OCR2B = 0;

  // Set up the 8 MHz output
   TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
   TCCR0B = _BV(WGM02) | _BV(CS00);
   OCR0A = 1;
   OCR0B = 0;


  // Make the 250 kHz rolling
  while (1) {
    _delay_us(5);
    if (OCR2B < 63)
      OCR2B += 5;
    else
      OCR2B = 0;
  }
}
share|improve this question
    
The rolling part made it difficult to see on my scope, getting rid of that and setting OCR2B to 30 I seem to get a 250Khz waveform. Strange. –  darwinawardee Mar 26 at 16:22

4 Answers 4

It's a bit beyond the normal Arduino capabilites so you need to delve into setting some of the registers for your ATMEGA chip directly. See the "Using the ATmega PWM registers directly" http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM

share|improve this answer
2  
I fully agree with your advice, but I would consider this kind of timer programming perfectly within the "normal Arduino capabilities". –  microtherion Mar 18 at 5:25
1  
microtherion, what I meant was it's outside the "Arduino language" set of functions that will work across many varients of AVR (such as delay, digitalWrite etc.) and instead delving into registers that will likely be specific to a given AVR chip. –  darwinawardee Mar 26 at 14:30

My requirement of 125Khz is satisfied, but just curious to know why the sketch not working as it should ...

According to my measurements, it is. Using this slightly modified version of your code:

// A sketch that creates an 8MHz, 50% duty cycle PWM and a 250KHz,
// 6bit resolution PWM with varying duty cycle (changes every 5μs
// or about every period.

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
 pinMode(3, OUTPUT); // output pin for OCR2B
 pinMode(5, OUTPUT); // output pin for OCR0B

// Set up the 250KHz output
 TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
 TCCR2B = bit(WGM22) | bit(CS20);
 OCR2A = 63;
 OCR2B = 31;

// Set up the 8MHz output
 TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
 TCCR0B = bit(WGM02) | bit(CS00);
 OCR0A = 1;
 OCR0B = 0;
 }

I get the predicted 8 MHz output on pin 5:

8 MHz scope output

And the 250 kHz output on pin 3:

250 kHz scope output

share|improve this answer

Yes, you can reduce the delay.

From the documentation: "This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times."

Note that, there are a few cycles between the delays, so as you get shorter timeperiods, you will get less and less accuracy.

You could hand-code the delay, I would imagine you could get up to 1mhz, but with little control over the duty cycle (at that frequency).You would need to disable interrupts, and you could do nothing else at all.

If you want still more, you could set a fuse on the AtMega, and get your native clock speed out on of pin 14 CKO (this shows up as digital pin 8 on the arduino board) - I would expect a 50% duty cycle; you will have NO control over - you can't switch it on or off, or affect the duty cycle. It can be used to run multiple chips off the same clock.

share|improve this answer

To your original question: You can reduce the delay, but there's a limit to how small the delay can be.

Two reasons: Firstly, the loop() function does have some overhead, and secondly the code that you write takes time to execute. This is why the hardware PWM Timer is valuable - it generates the pulses without much software intervention.

I used the code below and it resulted in 125Khz and 1.6 Mhz(measured with a CRO, not simulation).

This is really just to add to Nick's answer... (To understand what's going on, you need to refer to the datasheet. Based on your questions, I'm assuming you haven't stared at it long enough.)

In the first case: 125kHz Not sure if you're aware, but you're using a special mode of Fast PWM which is slightly different from the analogWrite() provided in Arduino.

In this mode, your PWM period is determined by the time it takes your timer counter to match the value in OCR2A register. This means that with a 16MHz clock, your output is toggled at a rate of 16e6/63 = 250kHz roughly.

So far so good, but what does the CRO see? The CRO considers a period to be made up of both high and low signal. It takes TWO 250kHz cycles to toggle the signal up and down. Hence, the reading shows 125kHz.

As to why the rolling would make your reading difficult: Think what kind of wave will appear when OCR0B = 0. ;-)

Case 2: 8MHz The same applies in the seccond case, and you should expect a 16MHz cycle that toggles the signal up and down (which gives a 8MHz reading on the CRO.) As to why you are getting 1.6MHz - that's an odd case. Can you post your CRO reading?

share|improve this answer
    
The CRO considers a period to be made up of both high and low signal. - indeed. The original code had such a short pulse that a scope might have interpreted each pulse as half a period, and thus halved the reported frequency. It might have missed the very short on cycle, which is why I adjusted the duty cycle up to 50%. –  Nick Gammon Aug 5 at 10:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.