# How to control a servo with a potentiometer?

A servo motor is a commonly used motor in robotics and automation. It is used to control the position of an object in a precise manner. The servo motor has a built-in feedback mechanism that allows it to be accurately positioned. In this servo motor control tutorial, we will explore how to control a servo motor with a potentiometer using the ATmega328P microcontroller.

Prerequisites

Before you begin this tutorial see the first part of this tutorial Servo Motor control with ATmega328p. In that tutorial it is explained how to calculate the count value to be loaded into the ICR1 register for time period or frequency of the PWM(Pulse Code Modulation) signal and how to calculate the count value to be loaded into the OCR1A register to control the duty cycle of the PWM signal.

Materials Required

• ATmega328P microcontroller
• 10KOhm Potentiometer
• Hobby Servo motor
• Jumper wires

### How servo motor works

Servo motor control works by sending PWM signal to the servo motor using microcontroller like ATmega328P or microcontroller board like Arduino. The PWM signal has a certain time period which is usually 20ms. The servo motor rotation angle is controlled using the duty cycle of the PWM signal. Usually, 1ms pulse with move the servo arm to the minimum angle position and 2ms will move the servo arm to maximum angle position, while pulse duration between 1ms and 2ms will move the servo motor arm between the minimum and maximum angle positions.

For example, if a -90 degree rotation is required a PWM signal with 1ms pulse duration is sent. Since the total period is 20ms then the duty cycle of the PWM signal is 1ms/20ms=5%. This is illustrated by the PWM signal below.

Similarly, if the PWM signal is sent to control the servo motor which has 2ms high width, then the servo motor will move to the maximum angle position. The duty cycle in this case is 2ms/20ms=10%. So PWM signal of 50Hz frequency(f=1/20ms) with 10% duty cycle is required to move the servo arm to maximum position.

### Servo, ATmega328P, Potentiometer Interfacing

To control a servo motor with a potentiometer using the ATmega328P, we need to connect the potentiometer and the servo motor to the microcontroller. The potentiometer is used to control the position of the servo motor, and the servo motor is used to move the object to a precise position.

The connections is shown below.

- Connect the center pin of the potentiometer to one of the analog input pins of the ATmega328P.
- Connect one of the outer pins of the potentiometer to the ground pin of the ATmega328P.
- Connect the other outer pin of the potentiometer to the 5V pin of the ATmega328P.
- Connect the signal pin of the servo motor to one of the PWM output pins of the ATmega328P.

### Programming the ATmega328P Servo Motor Control with Potentiometer

Now that we have set up the hardware, we need to program the ATmega328p microcontroller to read the potentiometer and control the position of the servo motor accordingly.

The program should do the following:

• Configure the PWM output pin for the servo motor.
• Configure the analog input pin for the potentiometer.
• Read the value of the potentiometer using the analog input pin.
• Map the value of the potentiometer to the range of positions for the servo motor.
• Send the mapped position value to the servo motor using the PWM output pin.

The program code for controlling servo motor with potentiometer and ATmega328P is below.

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

#define SERVO_MIN_POS 1999 // 0 degrees
#define SERVO_MAX_POS 3999 // 180 degrees

uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max);

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

ADMUX |= (1<<REFS0) | (1<<MUX0); // Set reference voltage to AVCC and select ADC1 as input
ADCSRA |= (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); // Enable ADC and set prescaler to 64

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

while (1){
ADCSRA |= (1<<ADSC); // Start ADC conversion
while (ADCSRA & (1<<ADSC)); // Wait for conversion to complete
uint16_t pot_val = ADC; // Read ADC result
uint16_t servo_pos = map(pot_val, 0, 1023, SERVO_MIN_POS, SERVO_MAX_POS); // Map pot_val to servo position
OCR1A = servo_pos;
_delay_ms(100); // Delay for servo to reach position
}
}

uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

This code is an example of how to control a servo motor using an AVR microcontroller's ADC module. The servo motor's position is set based on the value read from a potentiometer connected to the ADC pin.

The code starts by defining the minimum and maximum positions of the servo motor in terms of the PWM duty cycle values that will be sent to the motor.

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.

For calculating the minimum and maximum positions of the servo motor, we use the 1ms(1kHz) and 2ms(0.5kHz) in the above equation to calculate the value to be loaded into the OCR1A register.

(1) $$OCR1A =\frac{16MHz}{8\times 1kHz}-1 = 1999$$ for minimum position

(2) $$OCR1A =\frac{16MHz}{8\times 0.5kHz}-1 = 3999$$ for maximum position

These values are then used in the main loop of the program to map the potentiometer value (which can range from 0 to 1023) to the corresponding PWM duty cycle value that will set the servo motor to the desired position.

In the main loop, the program first starts an ADC conversion by setting the ADCSRA register's ADSC bit to 1. It then waits for the conversion to complete by checking the ADCSRA register's ADSC bit until it becomes 0. Once the conversion is complete, the result is stored in the ADC register.

The potentiometer value is then mapped to the corresponding PWM duty cycle value using the map function defined below the main function. The resulting PWM duty cycle value is then set as the output compare value for Timer/Counter1's channel A (OCR1A), which is used to generate the PWM signal for the servo motor.

Finally, there is a delay of 100ms to allow the servo motor to reach the desired position before the process repeats.

Overall, this code provides an example of how to read an analog input using the AVR microcontroller's ADC module and use that value to control a servo motor.

#### Testing the System

Once the program is uploaded to the ATmega328P, we can test the system by rotating the potentiometer and observing the movement of the servo motor. If the system is not working as expected, we can troubleshoot by checking the connections, the programming, and the values being sent to the servo motor.

Conclusion

In conclusion, controlling a servo motor with a potentiometer using the ATmega328P microcontroller is a simple and effective way to achieve precise position control in robotics and automation projects. By following the steps outlined in this blog post, you can easily set up and program the system to suit your specific requirements.

References and Further Readings

[4] Servo Control with Johnny-Five and Arduino