PWM to 50 hz

Hello!

How do i set one of the PWM port to 50hz with a 0.5ms pulse width?

So far i got this:

  pinMode(11, OUTPUT);
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
  TCCR2B = TCCR2B & 0b11111000 | 0x07;
  OCR2A = 180;
  OCR2B = 50;

This code generates a 30.6hz signal with 22.8ms pulse width.

http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM http://playground.arduino.cc/Main/TimerPWMCheatsheet

Thanks in advance

bandwidth ?

You seem to be misusing that term or don't understand what the term means, do you mean duty cycle or pulse width?

Lefty

"Blink without delay" see examples will let you do this.

Mark

Try using the Servo library, using "writeMicroseconds (500);"

Look in the playground - lots of ways to do this kind of thing listed there.

Mark

I edited my post, i ment pulsewidth and not bandwidth. I dont want to use the arduino servo library. I want to write to the registers myself..

You want to pay special attention to this section of the : Varying the timer top limit: fast PWM on this page http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM This is probably the most confusing configuration you'll run into. It's not very intuitive because it looks like you will be toggling OC2A (Pin 11), but you really toggle OC2B (Pin 3). This is because you hijack the OCR2A register to set the TOP limit so it can't possibly set a duty cycle now. The duty cycle is set by OCR2B and consequently it toggles OC2B (Pin 3). This is all necessary because you want to use a "non standard" PWM frequency.

  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS22)| _BV(CS21)| _BV(CS20);           //
  OCR2A = 156;
  OCR2B = 4;

This should give you a 50.1 Hz update rate with a .512mS pulse width on Pin 3. I haven't tested it, but I think it's right.

EDIT: I tested it on the scope and it works. Pin 11 has a 25Hz 50% duty cycle waveform on it.

afremont:
You want to pay special attention to this section of the :
Varying the timer top limit: fast PWM on this page
http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
This is probably the most confusing configuration you’ll run into. It’s not very intuitive because it looks like you will be toggling OC2A (Pin 11), but you really toggle OC2B (Pin 3). This is because you hijack the OCR2A register to set the TOP limit so it can’t possibly set a duty cycle now. The duty cycle is set by OCR2B and consequently it toggles OC2B (Pin 3). This is all necessary because you want to use a “non standard” PWM frequency.

  pinMode(3, OUTPUT);

pinMode(11, OUTPUT);
 TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
 TCCR2B = _BV(WGM22) | _BV(CS22)| _BV(CS21)| _BV(CS20);           //
 OCR2A = 156;
 OCR2B = 4;




This should give you a 50.1 Hz update rate with a .512mS pulse width on Pin 3. I haven't tested it, but I think it's right.

EDIT: I tested it on the scope and it works. Pin 11 has a 25Hz 50% duty cycle waveform on it.

Thanks alot! That works great. To change the pulse width i edit the output compare register b, OCR2B = 4 gives me 510us width. When i change the OCR2B value to 20 i get a 2.560ms width. Between 10 and 11 the value is 1.280ms and 1.410ms, is it possible to get a value inbetween those two? like 1.300?

Thanks in advance!

The problem is that with a 50Hz update rate, the possible pulse length is 20mS. This is spread across 156 possible widths. This only gives .13mS resolution as you already can see. To achieve .02mS resolution as you asked, would require several more bits of resolution in the PWM unit and that's not going to happen, you're stuck at 8 bits maximum; right now you barely have 7 since the TOP count is 156. You are asking for 10 bits of PWM resolution. It's just not possible with the 8-bit timers.

Or you could use the Servo library (or look at the source, and see which bits you want to use, if you don’t want to use the library itself)

I glanced (literally five minutes) at the servo library code and it looks like it uses Timer1 initially. This is a 16 bit counter. It puts it in ordinary count mode and enables overflow interrupts. With the prescaler of 8, it should overflow roughly 30.5 times per second. This would be the update rate to the servo, not 50Hz. This means one cycle is about 33mS long but is divided into 65536 possible parts for 500nS resolution on the duty cycle. It's not using the PWM feature of the chip, it's simulating it with software in the ISR so that it can run a multitude of servos. Given this method, it should work on any output pin.

EDIT: When you hijack Timer1, it disables the ability to use analogWrite() on Pins 9 and 10 for PWM.

I glanced (literally five minutes) at the servo library code and it looks like it uses Timer1 initially. This is a 16 bit counter. It puts it in ordinary count mode and enables overflow interrupts. With the prescaler of 8, it should overflow roughly 30.5 times per second. This would be the update rate to the servo, not 50Hz

I don't have the source to hand to glance at, but I do have a Servo library project and a 'scope. 50Hz, pretty much on the nose.

Yep, you're right. I looked thru the code a little more and the library does go to the trouble to dink the Timer for a 20mS period (50Hz). I commend the author for doing it right.

Thanks alot for all the help. I will look in the servo library and try to use timer1.

thrinker: Thanks alot for all the help. I will look in the servo library and try to use timer1.

Your welcome. Feel free to ask, I spent some time last night getting acquainted with the servo library source code. It's pretty well done. I experimented with TImer1 some more and played around with Timer0 to see what kind of impact its ISR was having on other interrupts. I didn't specifically experiment with PWM modes on Timer1, but I'm pretty sure I could help you get it set up if you want to go that way. The library is probably the best way to sacrifice Timer1 for the most return.

I had the same problem -- needing a 50MHz PWM with short duty cycle for a MicroServo 9g. Varying OCR2B moved the servo to different positions...

pinMode(3, OUTPUT); pinMode(11, OUTPUT); TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22)| _BV(CS21)| _BV(CS20); // OCR2A = 156; OCR2B = 4;

I know this is an old topic, but as I was able to find a way to drive a 50 Hz PWM frequency with higher resolution pulses. My goals were to use a potentiometer to drive a motor ESC using 1-2 ms pulses as well as learn a bit more about timers along the way. To get better resolution, I used the 16 bit timer on the UNO (ATMEGA328P) instead of the 8-bit timer 2 originally discussed. Posting my code here in case it can help anyone else.

Note that I use variables with the suffix "_pre" to set up register values before I write them. This was just for my own sanity as I was looking through the manual. The whole setup() code could probably be shortened to about 5 lines if you have confidence in what you're doing, which I did not.

// ref: https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/
// forum on Arduino PWM: https://forum.arduino.cc/index.php?topic=354160.0
// Schematic: https://content.arduino.cc/assets/UNO-TH_Rev3e_sch.pdf


// Find min and max compare register values to get 1 ms to 2 ms pulse
// Using 16-bit timer with prescale of 8
// Register value should be number of clock cycles before reset - 1 b/c we count up from 0
int OCR1B_min = 1999; //+1=2000 clock cycles to get 1 ms pulse width (off)
int OCR1B_max = 3999; // to get 2 ms pulse width (max speed)

void setup() {
  // Set timer output pin data direction
  // From ATMEGA328/P datasheet pgs 13 and 14 and the UNO schematic
  // OC1B -> PB2 -> MCU Pin 16 -> Board Pin 10
  // pg 167: Actual OC1x value will only be visible if data direction for the port pin is set as output (DDR_OC1x)
  pinMode(10, OUTPUT);

  // Create variables to store the values to be written to the TIM1 registers
  // Control registers are 8 bits (char)
  char TCCR1A_pre = 0x00; 
  char TCCR1B_pre = 0x00;

  // Set OC1B to non-inverting mode
  // pg 167: INCORRECT INFO: Non-inverted  PWM output can be generated by writing the COM1x[1:0] to 0x3
  // pg 174-175: looks like it is actually 0x2 in register description
  // Below code was tested working
  TCCR1A_pre |= _BV(COM1B1);

  // Set waveform generation mode
  // pg 165: counter is incremented until counter value matches value in OCR1A (WGM1[3:0]=0xF)
  // pg 175: WGM1[3:2] bits found in TCCR1B register, WGM1[1:0] found in TCCR1A register
  TCCR1B_pre |= _BV(WGM13) | _BV(WGM12);
  TCCR1A_pre |= _BV(WGM11) | _BV(WGM10);

  // Select the prescaled clock to use.  See Excel worksheet "PWMCalcs" to see justification.
  TCCR1B_pre |= _BV(CS11);

  // Write control registers
  TCCR1A = TCCR1A_pre;
  TCCR1B = TCCR1B_pre;

  // Write output compare registers (2 bytes)
  // pg 167: f_OCnxPWM = f_CLK_IO / (N*(1+TOP))
  OCR1A = 39999; // To get 50 Hz frequency
  OCR1B = OCR1B_min; // start at 1 ms pulse width

}

void loop() {
  // Read analog input
  // Voltages are between 0-5V on an UNO per https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/
  int potVal = analogRead(A0);

  // Map the input to the timer output
  // analogRead() values go from 0 to 1023.  We want to change the register from min to max specified above
  // pg 166: OCR1A is double bufferred, updated when TCNT1 matches TOP
  OCR1B = map(potVal, 0, 1023, OCR1B_min, OCR1B_max);
}

Hi all, no need to strictly stick to 50Hz, because 60HZ works well on most of servo and ESC.

Setting 60HZ pwm signal is easy.
I have a good brand name hobby RC transmitter receiver and I have tested it outputs 60HZ pwm signal to esc, not 50hz, so it looks like most of servo and ESC should handle 60HZ signal pretty well.

Also there are so many cheap $2 ESC/Servo tester out there they work well, many of those just use a NE555 to produce a signal to test servo or ESC, which frequency varies depends the On time, it’s not fixed to one frequency.

Setting 60HZ pwm for arduino is quite easy

TCCR2A = ((1<<WGM20) | (1<<WGM21))
#define TCCRB_INIT_MASK_60HZ ((1<<CS22)|(1<<CS20)|(1<<CS21)) // 1/1024 prescaler for 60HZ
TCCR2B = TCCRB_INIT_MASK_60HZ

OCR2A = 25 //set pwm =25

the only problem is for Pin11 the 8Bit timer 2 resolution is not that good, we only have about 9 levels of PWM output for servo(one dir) or ESC

One pwm period is 1000ms/60hz=16.6ms, there are only about 9 pwm level falls in the 1.5ms to 2ms range (for ESC)

On Time from 1.5ms to 2.0ms is valid, 1.5ms=0 output, 2ms=100% output for ESC

PWM ms on time ms
23 16.66666667 1.50 9.0%
24 16.66666667 1.57 9.4%
25 16.66666667 1.63 9.8%

29 16.66666667 1.90 11.4%
30 16.66666667 1.96 11.8%
31 16.66666667 2.03 12.2%

so we can either use other tricks to make the on time have more values but that complicates things,
or use the 16bit timer time1 when it’s available.
Thanks.

Today I also tested a brushless ESC for Drone, so it gets interesting.

It looks like for RC car ESC, from 1.5ms to 2ms on time is valid for motor moving forward.
but for drone ESC, it starts from 1ms, then it has wide range which is good,

And there is more, the drone ESC (a no brand name cheap one, black, 30A) accepts signal frequency up to 600hz…so we can easy use 244 or even 488Hz with the 8bit timer2 Pin11,
and the pwm output has 60+levels which is very good.

PWM ms period of 244hz, on time ms
62 4.098360656 1.00 24.3%

125 4.098360656 2.01 49.0%

for 244hz TCCR2B = ((1<<CS22)|(1<<CS21))