[SOLVED] Getting 50hz PWM from timer0

I have servo motor working with 2x555 timer setup....decide to switch timers with attiny. Problem I`m running into with math is I cant generate 50hz with timer0 in FastPWM or PhaseCorrect PWM mode.

Tried with cpu clock prescalers too from 62.5khz to 16mhz(I`m testing on Arduino uno).

from fastPWM mode 3 equation: Fclk/((TOP+1)*N) where N is prescaler.

Solving for N with 50hz is:
50hz=16M/(256*N)
N=16M/(50x256)=1250prescaler

Manipulating with all standard clocks and prescaler the closest I can get is 61hz.

I need 2 positions for servo to go central and +90.

duty cycle 7.5% and 10% for 50hz.

solving duty cycle for 61hz I`m getting

duty cycle= ((OCR+1)/(TOP+1))*100 for non inverted mode

duty cycle= ((TOP-OCR0)/(TOP+1))*100 for inverted mode

converting 7.5% of 50hz to 61hz I need 2.5% duty cycle in non inverted mode OCR0A value of 5.4
10% of 50hz to 61hz 3.3% OCR0A value of 8.44

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <avr/power.h>

void setup() {
TCCR0A=_BV(COM0A1) | _BV(WGM01) | _BV(WGM00);//compare match non inverting mode fast pwm mode 3
TCCR0B=_BV(CS02) | _BV(CS00);// 1024 prescaler
TIMSK0=_BV(OCIE0A) | _BV(TOIE0);//enable ocr interrupt and overflow interrupt
sei();//global I on

}

void loop() {
OCR0A=5;//~1.5MS on time;
//OCR0A=8;//~2MS on time
}

in code Im just trying to get servo shaft to rotate no logic needed. but nothing is happening. Currently im without oscilloscope. Also Im not sure if I need to explicitly declare ISR for overflow and ocr interrupts thought that in fastpwm mode it must be done totally in hardware.

don't want to make this post boring by adding code and equations for phase correct mode cos that one is not working too.

would like to know where I`m wrong in math or in code or its just impossible to get 50hz in this modes with timer0;

Thanks.

Uh - why not just use the Servo library (which uses timer1, not timer0 - since using timer0 would break millis() and other timekeeping functions)?

My ATTinyCore includes a version of Servo library that works with all the supported ATTiny chips except the attiny43, and provides the same interface as the normal Servo library - so you don't need to reinvent the wheel yourself.

Depending on which ATTiny you plan to use, if you need to do it manually for some reason, you're probably better off using timer1 - on the tiny85/861, that's a high-speed timer with a very flexible set of prescale options, while on other tinies that's a 16-bit timer (like the one on the Uno) which would also give you more flexibility with setting the frequency.

@OP

1. It is very very difficult to generate low frequency 50 Hz PWM signal using 8-bit TCs like TC0 and TC2.

2. To generate accurate low/high frequency PWM signal, one has to take the help of TC1 Module which supports frequency change by N (TC1 clock prescaler) and ICR1 Register; PW (pulse width) change by OCR1 Register. Before you ask for a short tutorial, please go through the data sheets to get the meanings of the BOTTOM and TOP parameters.

3. In the meantime, you can create 50 Hz PWM signal at any valid DPin of the UNO using Servo.h Library as suggested by @DrAzzy in Post#1.

is 50Hz PWM? i think it doesn't have the properties of PWM

Juraj:
is 50Hz PWM? i think it doesn't have the properties of PWM

You may execute the following short sketch and check on an oscilloscope if the wave at DPin-2 does posses some properties of 50Hz PWM wave.

#include<Servo.h>
Servo myServo;
int pw=0;

void setup()
{
   Serial.begin(9600);
   myServo.attach(2);
}

void loop()
{
   myServo.write(pw);
   pw = pw + 0x20;
   delay(1000);
}

GolamMostafa:
You may execute the following short sketch and check on an oscilloscope if the wave at DPin-2 does posses some properties of 50Hz PWM wave.

#include<Servo.h>

Servo myServo;
int pw=0;

void setup()
{
  Serial.begin(9600);
  myServo.attach(2);
}

void loop()
{
  myServo.write(pw);
  pw = pw + 0x20;
  delay(1000);
}

the Servo libro doesn't use PWM support of the MCU. it uses a timer to create pulses

Juraj:
the Servo libro doesn't use PWM support of the MCU. it uses a timer to create pulses

Can we continuously change the ON-period of this pulses based on some kind of feedback? If yes, it is a PWM signal by definition.

Sorry i missed the mcu model in my post. Its attiny13v.

So 16bit timer is not available or it would be very easy to generate 50hz with varible duty cycle for servo.

@Drazzy can please send a link to your library? I really dont want to spend time on reinventing anything:-).

If we are talking about softwareservo.h library which is indeed working on some attinys it wont fit into memory of attiny13. Besides i hardcoded pwm via delays in my code same as softwareservo is doing but thats not the one i would like to have.

For ex.
For 1.5ms pulse i did

Pin-high
_delay_us(1500)
Pin-low
_delay_us(18000)

Wrapped in a function with high pulse attribute. Its very short code and doing its job.

But i want to do it in hardware without mcu going line by line in the code in function.

And i cant make fastpwm work so wanted to know if im off in my math or in my code. I dont think that 61hz vs 50hz will prevent servo recognizing the pulse if im feeding it with right duty cycle.

With a prescaler of 1024 and an 8 bit timer, you need 312.5 timer ticks for a 50 hz period. You can use the the phase mode 5 PWM to OCR0A with OCR0A set to 312/2 = 156. You will have to use the pwm on output B.

Obviously you will loose some resolution on the PWM duty cycle setting.

I didnt quite get idea of using mode 5 if top value is ocr value. I never used mode 5 before.

@cattledog if i got you right in mode 1 then i have 510 ticks total. With prescaler of 1024 and 16mhz clock i have 64us per tick.

20ms/64us is 312.5ticks needed.

312/2=156 for ocr

In mode 1 upcounting 0-156 ocr is set. From 156 to down counting to 156 is giving (255-156)*2=198ticks =12672us.

From 156 to bottom and up to 156 is giving 312ticks i.e. ~20ms. Meanwhile signal is low.

If i fit second ocr with value of 11 i will set high for duriation of 11+11=22tics*64=1.4ms .

And then again long pause for clock to reach to 156 at downcounting.

Will attach drawing.

I didnt quite get idea of using mode 5 if top value is ocr value. I never used mode 5 before.

You need to use this mode if you want to get a 20ms period. Mode 5 uses the OCR0A value as top, so top will be OCR0A = 156 not 255. Your image still indicate you are using a mode with 255 top.

Then the PWM output will be on B. Duty cycle will be set with OCR0B.

Set COM0B1 and COM0B0 to 11. I think that is set rising, clear falling. Otherwise the setting will be 10. Duty cycle values may be reversed, with small numbers higher duty cycle values. If you want to have it as normal, then you will need to use an output setting which clears rising and sets falling.

Thanks will update the topic if any success on mode 5.

UPDATE:
didn't work on mode 5;

20ms/64us=23us;

156-23/2=144; for central position

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <avr/power.h>

void setup() {
DDRB|=_BV(DDB2) | _BV(DDB1); // pins 9 and 10 out
TCCR0A=_BV(WGM02) | _BV(WGM00) | _BV(COM0B1) | _BV(COM0B0);// mode5 , set on rising clear on falling
TCCR0B=_BV(CS02) | _BV(CS00);//1024 prescaler
OCR0A=156; // 156*2=312=20ms period
//TIMSK0=_BV(OCIE0B) | _BV(OCIE0A);


}

void loop() {
  OCR0B=144; //1.5ms
 // OCR0B=140;//for 2ms
}

UPDATE:
didn't work on mode 5;

What did your scope show?

WGM02 is on TCCR0B in the AT328.

Are you using an ATtiny? What processor? Do you have a data sheet in front of you?

I'm not certain about your timer default presets, so it's always good to have TCCRxA and TCCRxB initialized to 0 before adding bits to where you want them. The timers are usually have presets for the millis()/micros() and the default pwm settings.

You are right about wgm02 bit ... went many times through code and didnt even think of checking where it belongs. No im testing on 328p(uno) . Currently no oscilloscope so its realy hard to see what the signal is thats why got back to pen and paper method.

Im initializing timer registers with 0.
Im not OR ing tccrb.

Tccr= (1<<bit) will set all zeros except the mentioned bit.

Same as
tccrb=0
Tccrb|= set bit.

Will test wgm02 in correct register and will update.

UPDATE: same thing servo is not recognizing the pulse.

servo is attached to pin 10 which is pb2.
code edited with wgm02 in tccr0b register.

rechecked servo with _delay_us routines its working.

servo is attached to pin 10 which is pb2.

Pin10 is not an output for Timer0. Timer0 outputB is pin D5.

cattledog:
Pin10 is not an output for Timer0. Timer0 outputB is pin D5.

Nailed it! +1

Thanks a lot servo is moving at angles determined by OCR0B value now.

and code for others not to melt their brains while making timer0 to work with servo in pwm mode.

#define F_CPU 16000000UL
#include <avr/io.h>


/*ticks in micros
  1/fcpu*prescaler*1000000
*/

void setup() {
  DDRD |= _BV(DDD5); // pin 5 out
  
  TCCR0A = _BV(WGM00) | _BV(COM0B1) | _BV(COM0B0) ; // mode5 , set on rising clear on falling
  
  TCCR0B = _BV(WGM02) | _BV(CS02) | _BV(CS00); //1024 prescaler
  
  OCR0A = 156; // 156*2=312=20ms period

 }

void loop() {

  for (OCR0B = 140; OCR0B < 148; OCR0B++) _delay_ms(1000); //1ms to 2ms duty
 
  for (OCR0B = 148; OCR0B > 140; OCR0B--) _delay_ms(1000); //2ms to 1ms duty

}
1 Like