Sei sulla pagina 1di 7

[TUT] ATMEGA328PB, 16-bit timer, servo, FastPWM, HXT900

This about using an ATMEGA328PB Xplained mini to drive an HXT900 servo with a 16-bit timer
using FastPWM Mode. (see link 1 at the end of this document)

The plan is to use the FastPWM mode with ICR1 used as the TOP of the counter range and OC1A &
OC1B pins used for the output. Using ICR1 as the TOP allows the adjusting of the frequency.
OCR1A & OCR1B adjust the pulse width for their pin. Thus 2 servos can be driven by the 1 timer.

The ATMega328PB has 3 16-bit timers,TIMER1, TIMER3 and TIMER4, They are identical except that
TIMER3 and TIMER4 share 1 output pin. Both OC3B and OC4B share PD2. See Section 22 in the
datasheet. Also OC3A is on PD0 which is RXD0 on the 328PB Xplained mini and OC4A is connected
to PD1 which is TX0.

HXT900 servo specs from ServoDatabase.com:

Pulse cycle: 20ms


Rotational Range: 60
Pulse width: 450 – 2450 us. For 180 degrees.

To configure the 328PB, start with the frequency needed for the servo. Then select the prescaled clock
for the timer.

The frequency for the servo is 1000ms/20ms = 50Hz.

To find the prescale use: (1) Freq = fclk/(N * cnt). cnt = number of timer ticks for 1
cycle.
FastPWM counts up then restarts
at the bottom, 65536 ticks.

Rearranging: (2) N = fclk/(Freq * cnt)

cnt = 65536
Freq = 50
fclk = 16000000
N = 4.9

We need to choose a prescaler >= N. The choices on the 16-bit timer, 8, 64, 256, 1024.
Using the lowest value for the prescaler, N = 8:

From eq. (1):

Freq = 16000000/(8 * 65536) = 30.5, which is too slow.

Now we need to solve for cnt to give a frequency of 50:


(3) cnt = fclk/(N * Freq)
cnt = 16000000/(8 * 50) = 40000

This is the information we need: Prescaler = 8, cnt = 40000.


Now we can configure TCCR1A, TCCR1B, ICR1, OCR1A, OCR1B.

TCCR1A:

COM1A & COM1B refer to the output pins. Output on the COM1A & COM1B pins.

Set COM1A & COM1B = 0b10 = 2. Set at bottom, clear on compare match.
TCCR1A = 0b1010---- =0xA-.

The 16-bit timers have more Modes than the 8-bit timer. There are 8, 9 & 10 bit Modes
that set the TOP to 0xFF, 0x1FF & 0x3FF. Also there are Phase and Frequency Correct
Modes.

This example uses Mode 14; FastPWM, with IRC1 as the TOP and update at Bottom.
Select Mode 14, FastPWM, with ICR1 to be the TOP.
WGM1[0] = 0 and WGM1[1] =1 are set here. And 2 bits WGM1[2] =1 & WGM1[3] = 1 are
set in TCCR1B.

The setting for TCCR1A = 0b10100010 = 0xA2

TCCR1B

ICNC1 & ICES1 are not used when ICR1 is used for TOP, like this example.

WGM![2] & WGM1[3] need to be set. ( see above discussion of TCCR1A & WGM1[1:0].
CS1 will be set to 0b010 for fclk/8, but setting the clock, in TCCR1B starts the timer, so
during setup we will just set the WGM1[2] & WGM1[3] bits.

TCCR1B = 0b00011000 or 0x18.

ICR1

Set to the count. The total count for the period was computed above, only counting up,
was 40000, so:

ICR1 = 40000.

OCR1A
OCR1B

Controls PINs OC1A & OC1B, which controls the duty cycle, or the pulse width. Note in the
specifications of the HXT900 pulse width = 450 – 2450us. .000450 - .002450secs.

20ms = .020000s is the pulse cycle or 1 period. In that 20ms, the timer will count 40000
so 1 count = 0.0000005s = 0.0005ms = 0.5us = 500ns.

The pulse width 450 = 900 ticks and 2450 = 4900 ticks.
Thus the setting will go from a low of 900 to a high of 4900. 4000 ticks.
This means that with the range of 180 degrees, each count will move 0.045 degrees.
This is the advantage of using the 16-bit timer, much better resolution compared to the 8-bit
timer.
The advantage over the Phase Correct PWM. Is 0.045 degrees per count v 0.09 degrees/count.
If you have a Logic Analyzer you can try each value, 900 – 4900, and see the pulse width.

I got 900 = 451 and 4900 = 2452.


So for the 1st program set:

OCR1A = 30000. duty cycle = 75%,


OCR1B = 20000. duty cycle = 50%.
or any value between 0 and 40000 (depends where you want to begin).

The OCR1A & OCR1B registers are double buffered. When you write to OCR1A or OCR1B,
it will be updated at the next Bottom. This could be an almost 20ms delay.

OC1A
OC1B

The pins OC1A & OC1B need to be configured as an output. OC1A = PB1, OC1B = PB2.
See Table 6-1 PORT Function Multiplexing.

DDRB = 6 or 0x6 or 0b110. (if you set other PORTB pins, use |= )

Start:

TCCR1B |= 2 or 0b00000010. For clk2. Main clock/2.


Need to |= here since during setup we set the WGM1[2] & WGM1[3] bit.

Stop:

TCCR0B &= ~(7) or ~(0b00000111). Need to zero the clock field of 3 bits.

To begin testing, connect 2 LEDs to Pin PB1 & PB2.

I would suggest you obtain a Logic Analyzer See link 2 at end of the document.

Here is a program that sets up the timer for PWM Phase Correct and starts the timer with a 75%
duty cycle on PB1 and a 50% duty cycle on PB2.
Do not connect your servo yet, just the LEDs. This program is attached as M328PBfpservo16a.

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

int main ( void )


{
DDRB |= 6; //Set pin PB1 & PB2 as output for OC1A & OC1B
// Timer 1 setup pwm
TCCR1A = 0xA2; // Set OC1A & OC1B; WMG1[1]. Table 18-8 in Datasheet. & Table 18-9.
TCCR1B = 0x18; // Set WMG1[2] & WMG1[3]; tABLE 18-9
TCNT1 = 0; // clear counter
ICR1 = 40000; // count of 40000 for a 20ms period or 50 Hz cycle
OCR1A = 30000; // 75% duty cycle
OCR1B = 20000; // 50% duty cycle
TCCR1B |= 2; //set clock to divide by 8 and start TABLE 18-10
while (1)
{

}
}
Result should be a somewhat dim LEDs on Pins PB1 & PB2. OCR1A = 30000 gives 75% duty cycle
and OCR1B = 20000 gives 50% duty cycle. If you have a Logic Analyzer this is the output:

CONNECT THE SERVO

3 wires, VCC & GND to your power source and the control (yellow) wire to PB1 & PB2.
It is not a good idea to connect VCC & GND to the MCU board in actual use.
The HXT900 can pull almost 1 amp under certain high load conditions. However with no
load it is safe to draw power from the MCU board for 1 servo.
With 2 servos I don't recommend connecting both servos to the MCU board even in the lab.
May work, may cause a problem.

This next program cycles the servos low – middle – high – middle – low ….
The servo can be attached now. This program is M328PBfpservo16c.

//M328PBfpserver16C
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
static uint16_t posit[4]={900,2900,4900,2900}; //position of servo

int main ( void )


{
DDRB |= 6; //Set pin PB1 & PB2 as output for OC1A & OC1B
// Timer 1 setup pwm
TCCR1A = 0xA2; // Set OC1A & OC1B; WMG1[1]. Table 18-8 in Datasheet. & Table 18-9.
TCCR1B = 0x18; // Set WMG1[2] & WMG1[3]; tABLE 18-9
TCNT1 = 0; // clear counter
ICR1 = 40000; // count of 40000 for a 20ms period or 50 Hz cycle
OCR1A = 2900; // start in the middle between 900 & 4900
OCR1B = 2900; //
TCCR1B |= 2; //set clock to divide by 8 and start TABLE
while (1)
{

for (uint16_t ii = 0;ii < 4;ii++)


{
OCR1A = posit[ii];
OCR1B = posit[3 - ii];
_delay_ms(1000);
}

}
}
The output the LED dimly flashes.
Logic Analyzer:

This configuration results in a period of 20ms, frequency = 50 Hz. Each count = .045 degrees. Or 22
counts per degree. With the 16-bit timers other resolutions are possible.

1. https://hobbyking.com/en_us/hxt900-micro-servo-1-6kg-0-12sec-9g.html

2. https://www.ebay.com/itm/USB-Logic-Analyzer-Device-Set-USB-Cable-24MHz-8CH-24MHz-
ARM-FPGA-M100-/201541710029?hash=item2eecd270cd

Potrebbero piacerti anche