Servo Motor control with ATmega328p

 A servo motor is a type of motor commonly used in control systems that require precise position control. A servo motor rotation is controlled using PWM(Pulse Width Modulation) Duty Cycle. The ATmega328p microcontroller is a popular microcontroller which is easy to use and widely available. It can be used for servo motor control. In this blog post, we will discuss how to control a servo motor using the ATmega328p microcontroller and provide an example code.

To control a servo motor using the ATmega328p microcontroller, we need to generate a PWM (Pulse Width Modulation) signal with a frequency of 50Hz(time period of 20ms) and a pulse width between 0.5ms to 2.4ms which corresponds to minimum and maximum angle of the servo rotation. That is, the pulse width corresponds to the angle of the servo motor. There are 3 timers in ATmega328P called Timer0, Timer1 and Timer2. The Timer1 is 16-bit timer and thus offers higher resolution than the other two timers. So here we will use the timer1.

Servo Motor PWM & Duty Cycle and Rotation Angle

A general hobby servo motor can rotate from -90 degree to +90 degree, or from 0 to 180 degree although there are continuous hobby motor. Here we will consider a hobby servo motor like SG90 servo motor which can rotate from 0 to 180 degree(from-90 degree to +90 degree which is essentially the same). 

 servo motor

Usually such hobby servo motor requires 1ms(or less) pulse to rotate the servo to its minimum value which is 0 degree(-90 degree) and requires 2ms(or more like 2.4ms) pulse to rotate the servo motor to its minimum value. Pulse with pulse between 1ms and 2ms will rotate the servo arm between its minimum and maximum degree of rotation. This is illustrated by the following diagram.

servo control diagram

This means that hobby servo motor is controlled by PWM pulses that have 20ms(50Hz) time period and duty cycle depending upon where we want to rotate the servo. 

For example to rotate the servo arm to its minimum assuming it requires 1ms pulse the required duty cycle for the PWM signal is 5%. This is because pulse width/time period*100%=(1ms/20ms)*100%=5%. The following shows the PWM signal required to position the servo in its minimum angle position.

PWM signal waveform for Servo motor minimum position

 Similarly the duty cycle for PWM signal that rotates the servo to its maximum angle position(180 degree) is,

Duty Cycle = (pulse width/time period)*100%= (2ms/20ms)*100=10%.

The PWM signal waveform for rotating servo arm to its maximum angle position is shown below.

PWM signal waveform for Servo motor minimum position

The result is similar for any other servo motor position. 

ATmega328P Timer's PWM and Duty Cycle

Once we know how the PWM signal and concept of duty cycle is applied to control a servo motor the next step is to understand how to configure the microcontroller timer to generate such PWM signal. 

The PWM signal frequency of ATmega328P Timer 1 in Fast PWM mode is given by the following equation.

 \(f_{OC1x} = \frac{f_{clk(I/O)}}{N(1+TOP)} \)    ----->(1)
where,x=A or B, and, TOP=OCR1A or ICR1

We can rearrange the above equation for TOP=ICR1,

\(TOP = \frac{f_{clk(I/O)}}{N f_{OC1x}}-1\)    ----->(2)

 Now to set the PWM  period of 20ms we need to calculate the count value to be loaded into ICR1(Input Capture Register 1). Since 20ms corresponds to 50Hz(f=1/T=1/20ms=50Hz), using equation(2), we have,

\(TOP = ICR1 =\frac{16MHz}{8\times 50Hz}-1 = 39,999\)

So when the ICR1 resistor is loaded with 39,999 the counter starts from 0 to 39,999 giving a period of 20ms.

Next, to set the duty cycle of the PWM corresponding to minimum, maximum or any other rotation angle we need to calculate the value to be loaded into the OCR1A register. We can use two methods to calculate the value required. 

For example, for minimum angle of rotation, we can calculate the duty cycle for minimum rotation and multiple with the total period count. For 1ms pulse in 20ms time period, the duty cycle calculated was 5%. Therefore it means we need count value of 5%*40,000-1=1999 that needs to be loaded into the OCR1A register for minimum angle rotation. Note that it takes 39,999 + 1 clock cycle giving total of 40,000 clock cycle because the count starts at 0. Similarly, it takes 2000-1=1999 clock cycle to reach 5% duty cycle. The other method is to calculate the count value to be loaded into the OCR1A register is to use the equation (2) above. For minimum rotation, the pulse width is 1ms. The frequency is then f=1/1ms = 1KHz which when used in equation(2) we will get, \(OCR1A =\frac{16MHz}{8\times 1kHz}-1 = 1999\) which is same as before.

Like, we can calculate the OCR1A count value required for maximum rotation in similar manner. In this case, a pulse width of 2ms is required. This means the duty cycle is 10%, as explained above. This then means the count value is OCR1A=10%*40,000-1=3,999. Using equation(2) we get the same result, as 2ms means frequency,f=1/2ms=0.5kHz and therefore from(2), \(OCR1A =\frac{16MHz}{8\times 0.5kHz}-1 = 3999\).

Overall, to set the time period of the PWM we need to load ICR1 register with count value of 39,999.  For minimum angle rotation and maximum angle rotation we need to load the OCR1A register with values 1,999 and 3,999 respectively and to set angle between min and max angle we need to load OCR1A value between 1,999 and 3,999.

Programming ATmega328P for Servo Control

Once we have understood the concept of PWM signal, duty cycle which controls the rotation angle of servo motor and once we know how to set these values in the ATmega328P Timer1 registers(ICR1 and OCR1A), we can then start writing the control code for the servo motor. Here different servo program are provided starting with a most simple and basic to more complex ones.

Example 1: Simple Servo Control Program

Here we will write code to rotate the servo motor from minimum angle to maximum angle(0 degree to 180 degree or -90 degree to +90 degree) and back to minimum. To write the program we need to configure the Timer 1 in Fast PWM mode. Within this mode we have configure the output as non-inverted PWM or inverted PWM. Here we will use the non-inverted PWM. 

Following is example code:

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

    int main(void)
    {
        DDRB |= (1<<PB1); // Set PB1 as output

        TCCR1A |= (1<<COM1A1) | (1<<WGM11); // Fast PWM, non-inverting mode
        TCCR1B |= (1<<WGM13) | (1<<WGM12) | (1<<CS11); // Fast PWM, prescaler = 8
        ICR1=39999;   //20ms PWM period
        //OCR1A = 1999; // Set initial position to 90 degrees

        while (1){
            OCR1A = 1999; // Set position to 0 degrees
            _delay_ms(1000);
            OCR1A = 2999; // Set position to 90 degrees
            _delay_ms(1000);
            OCR1A = 3999; // Set position to 180 degrees
            _delay_ms(1000);
        }
    }
 

Explanation

In the above code, we first set PB1 as an output pin using the DDRB register. We then configure the Timer/Counter1 module of the ATmega328p to generate a PWM signal with a frequency of 50Hz(2ms time period).

The TCCR1A and TCCR1B registers are used to configure the Timer/Counter1 module. In TCCR1A, we set the COM1A1 and WGM11 bits to configure the PWM signal in non-inverting mode and fast PWM mode, respectively. In TCCR1B, we set the WGM13, WGM12, and CS11 bits to configure the PWM signal in fast PWM mode with a prescaler of 8(N=8). The ICR1 register is loaded with 39,999 for 2ms time period(50Hz).

In the while loop, , the OCR1A register is used to set the pulse width of the PWM signal. In the example code, we have first set the position of the servo motor to 0 degrees by setting OCR1A to 1999. The we wait for 1 second and then load OCR1A register with count value of 2999 to rotate the servo by 90 degree. We again wait for 1 second and then load OCR1A register with count value 3,999, which corresponds to the angle of the servo motor of 180 degrees.

Below is ATmega328P Servo Motor Circuit Diagram.

ATmega328P Servo Motor Circuit Diagram
In the next servo motor control tutorial with ATmega328P microcontroller we will show how to control a servo with a potentiometer.


Conclusion

In conclusion, controlling a servo motor using the ATmega328p microcontroller is a simple and straightforward process. By generating a PWM signal with a frequency of 50Hz and a pulse width between 1ms to 2ms, we can control the position of the servo motor. The example code provided in this servo control tutorial can be used as a starting point for your servo motor control project. See references for servo motor projects.

References and Further Readings

[1]  Servo Motor control using Simulink and Arduino

[2]  Password based Door Locking System using Arduino

[3] Intruder Sensor with Ultrasonic Sensor 

[4] Servo Control with Johnny-Five and Arduino

1 Comments

Previous Post Next Post