If you want to generate a pulswidth of 0 - period (0-100%) I generally used Timer 1 because it has to Output Compare Units (A and B).
The code that I use to set up is in asm, because I’m an assembler junkie.
;Init Timers
;Timer 1 16 bit 2 output compare to drive motors
ldi temp,0b10110001 ;Clear OC1A/OC1B on compare match, set at top
out TCCR1A,temp ;PWM Phase Correct 8bit
ldi temp,0b00000001 ;prescaler is 1
out TCCR1B,temp
The way I have it, it does not generate interupts, the Atmega hardware has two pins, OC1A and OC1B, this allows you to do 16 bit pwm on two channels.
If you want to control RC servos, which are a PWM wave with the PWM from anywhere from .5 ms to 2.5 ms every ~50 hz (20 ms) then I have a pretty inefficient code, but it gets the job done. BTW this is all using a 16mHz crystal.
;Timer 0 (to call servo routine every 10 ms
ldi temp,0b00001101
out TCCR0,temp ;set prescaler to 1024 and clear timer on compare match
ldi temp,0b00000010
out TIMSK, temp ;generate an interupt on compare match 16mhz/1024/156=100 hz
ldi temp,156
out OCR0, temp
;we want to execute the actual servo subroutine 50 hz, but the timer
;doesnt go that slow, so we call the servo every 100 hz, and only execute
;the code every other call
I first have a timer that calls an adress every 10 ms, but in the actual subroutine it only executes the code every even call.
Servo: ;update servo positions, should call every 20ish ms called by interupt
push temp
push temp2
in temp, SREG
push temp
dec ServoSt ;this is to activatre the routine only every other interupt
tst ServoSt ;since it is called every 10ms
brne Servexit
ldi ServoSt, 2
clr ServCoun
sbi Servport,Serv1 ; Servport is the port and Serv1 is the pin
sbi Servport,Serv2
sbi Servport,Serv3
sbi Servport,Serv4
; rcall Delay200 ;this is to optomize the range of the servo
rcall Delay200 ;to 255, because no servo signal should ever be
rcall Delay200 ;less than ~400-600 us
ser temp ;this is to execute the below code for 2 ms then leave
Serloop:
dec ServCoun ;servo counter, is decremented ~ every 7.5 us
Ser1:
cp ServCoun,Servo1 ;sees if the servo counter is more or = to the
brsh Ser2 ;desired servo value
cbi Servport,Serv1
nop ;an attempt to make it equal if servo is met or not
Ser2:
cp ServCoun,Servo2
brsh Ser3
cbi Servport,Serv2
nop
Ser3:
cp ServCoun,Servo3
brsh Ser4
cbi Servport,Serv3
nop
Ser4:
cp ServCoun,Servo4
brsh Ser5
cbi Servport,Serv4
nop
Ser5:
;continue if more servos
rcall Delay7
dec temp
brne Serloop
ServExit:
pop temp
out SREG,temp
pop temp2
pop temp
reti
I swear it looks prettier in the text editor.
all of the names temp, temp2, Serloop, ServSt, Servo1- 4 are all registers. To change the servo position change the value of Servo1-4.
AND one more thing, Delay200, Delay7 are two delay loops. Their code:
Delay7:
; each cycle = 62.5 ns (16 MHz)
; number of processor cycle = 112 = 40 uS
; total delay ~7 useconds
;clr DelayCounter1 ;1 (A)
ldi DelayCounter1, 35 ;
DelayLoop2: ;
dec DelayCounter1 ;60 x 1 (B)
brne DelayLoop2 ;1 x 1 if no branch (C), 255 x 2 if branch (D)
; (A) (B) (D) (C)
; so far: 1 + 60 + 118 + 1 = 180 cycles * 62.5ns
;
; 1 + N + (N-1)*2 + 1 = 1 + 3N -2 + 1 = 3N
; N=213 --> 40 uS
ret
Delay200:
ldi DelayCounterMS, 5 ;40*5=200 useconds
Delay200msloop:
call Delay40
dec DelayCounterMS
brne Delay200msloop
ret
(note Delay200 depends on Delay40!!)
Delay40:
; each cycle = 62.5 ns (16 MHz)
; number of cycle = 213 = 40 uS (not processor cycles)
; total delay 40 useconds (slightly less)
;clr DelayCounter1 ;1 (A)
ldi DelayCounter1, 210 ;1 (was 213, 210 tuned with Locic Analy)
DelayLoop1: ;
dec DelayCounter1 ;256 x 1 (B)
brne DelayLoop1 ;1 x 1 if no branch (C), 255 x 2 if branch (D)
; (A) (B) (D) (C)
; so far: 1 + 256 + 510 + 1 = 768 cycles * 270 ns --> 207 useconds
; 768 * 62.5 --> 48 useconds
; 1 + N + (N-1)*2 + 1 = 1 + 3N -2 + 1 = 3N
; N=213 --> 40 uS
ret
:sigh: it’s ugly, and inificient, but it did the job for me. If anything IM me or PM me.
[Edit for code tags, thanks Matt Krass]