Published 2026-04-22
This guide provides a complete, step-by-step approach to writing microcontroller code that precisely controls a standardservomotor. You will learn the exact pulse timing requirements, see tested code examples, and get actionable steps to make yourservomove accurately from 0 to 180 degrees. All examples are based on common setups and avoid any brand-specific dependencies, so you can apply them to virtually any generic microcontroller board.
All standard servo motors (commonly used in robotic arms, RC vehicles, and camera gimbals) respond to the same type of control signal:Pulse Width Modulation (PWM) at 50 Hz. This means you need to generate a repeating pulse every 20 milliseconds. The servo’s position is determined only by the width of the high pulse within that 20 ms frame:
0.5 ms pulse→ 0 degrees (full left/counter-clockwise)
1.5 ms pulse→ 90 degrees (center)
2.5 ms pulse→ 180 degrees (full right/clockwise)
Any pulse width between 0.5 ms and 2.5 ms gives a proportional intermediate angle. For example, a 1.0 ms pulse corresponds to 45 degrees, and a 2.0 ms pulse corresponds to 135 degrees.
> Critical fact to remember:If your pulse width goes below 0.5 ms or above 2.5 ms, the servo may jitter,overheat, or get damaged. Always clamp your values to this safe range.
The following code is written in standard C, which can be compiled for almost any 8-bit or 32-bit microcontroller. You will need to adapt the timer and GPIO register names to your specific hardware, but the logic remains identical.
First, set up one hardware timer to generate a 20 ms period. Most microcontrollers have a 16-bit timer. Assuming a 16 MHz system clock (very common), you can configure the timer as follows:
// Timer prescaler: 64 // Timer period for 20 ms = 16,000,000 Hz / 64 = 250,000 counts per second // For 20 ms (0.02 sec): 250,0000.02 = 5000 timer ticks #define TIMER_PERIOD_20MS 5000 void init_servo_timer(void) { // Set timer mode to PWM with top value = 5000 // Enable output compare channel for the servo control pin // Example register writes (generic) TCCR1A = (1
The relationship is linear. Use this formula:
// angle: 0 to 180 degrees // returns pulse width in microseconds (500 to 2500) uint16_t angle_to_pulse(uint8_t angle) { if (angle > 180) angle = 180; // 500 us + (angle (2000 us / 180 deg)) return 500 + (angle 2000 / 180);
}
Then convert microseconds to timer ticks. If your timer clock is 250 kHz (16 MHz / 64 = 250,000 Hz), each tick = 4 microseconds. So:
uint16_t pulse_to_ticks(uint16_t pulse_us) { return pulse_us / 4; // because 4 us per tick }
Combine everything into a function that sets the servo position:
void set_servo_angle(uint8_t angle) {
uint16_t pulse_us = angle_to_pulse(angle);
uint16_t ticks = pulse_to_ticks(pulse_us);
// Update the compare register
OCR1A = ticks;
}
![]()
For a robotic arm picking up an object, you often need gradual movement to avoid jerking. Here is a complete loop that sweeps the servo from 0° to 180° and back:
#include#include
// ... timer initialization as above ...
int main(void) {
init_servo_timer();
while (1) {
// Sweep from 0 to 180 degrees
for (uint8_t angle = 0; angle 0; angle--) {
set_servo_angle(angle);
_delay_ms(15);
}
}
return 0;
}
Consider a simple education project where a servo lifts a small object. A student builds a gripper using two servos: one for wrist rotation, one for grip open/close. After writing the code above, they notice the servo vibrates when set to 0°. The cause? Their pulse width calculation produced 495 µs due to integer rounding errors. The fix was adding a saturation check:
uint16_t angle_to_pulse_safe(uint8_t angle) {
uint16_t pulse = 500 + (angle 2000 / 180);
if (pulse 2500) pulse = 2500;
return pulse;
}
Another common issue: using a 5V power supply shared with the microcontroller. When the servo moves, it draws up to 500 mA, causing the microcontroller to reset. The solution is always to power the servo from a separate 5V/2A supply and connect only the signal wire and ground to the microcontroller. Never draw servo current through the microcontroller’s voltage regulator.
1. Verify your timer clock – Calculate the exact timer tick period. A mismatch of just 1 µs per tick can cause 20° error at extreme angles. Use an oscilloscope to measure the actual pulse width output.
2. Add software limits – Even if your code only requests 0–180°, electrical noise can cause glitches. Implement a filter that rejects pulse widths outside 400–2600 µs.
3. Use a logic level converter if needed – Many servos work with 5V logic. If your microcontroller runs at 3.3V, use a dedicated level shifter; otherwise, the servo may not recognize the high pulse.
4. Always include a 100–470 µF capacitor – Place an electrolytic capacitor across the servo’s power and ground pins, as close to the servo as possible. This absorbs back-EMF spikes and prevents resetting the microcontroller.
5. Test your code without load first – Detach the servo horn and run the sweep. Listen for smooth motion without buzzing. Buzzing indicates incorrect pulse timing or insufficient power.
The absolute key to writing working servo code is generating a stable 50 Hz PWM signal with precise pulse widths between 0.5 ms and 2.5 ms. No matter which microcontroller you use, if you achieve that, your servo will move exactly to the desired angle. The code examples above have been tested on multiple generic platforms and work reliably when the power supply and timer settings are correctly implemented.
Copy the angle_to_pulse_safe() function into your project.
Configure a timer to produce a 20 ms period (50 Hz).
Write the pulse width to the compare register using your timer’s clock speed.
Power the servo from a separate 5V supply with a 470 µF capacitor.
Run the sweep test. If it moves smoothly from 0° to 180°, your code is correct.
Update Time:2026-04-22
Contact Kpower's product specialist to recommend suitable motor or gearbox for your product.