Published 2026-04-01
This guide provides a complete, practical explanation of Pulse Width Modulation (PWM) code for controlling standardservomotors. If you are looking for the exact code, wiring instructions, and timing principles to make aservomove to a precise angle, you have come to the right place. This article focuses on the universally accepted standard forservocontrol—the 50 Hz PWM signal with a pulse width between 1 ms and 2 ms—and provides working code examples that are hardware-agnostic, ensuring you can apply them to any microcontroller platform.
At its heart, controlling a standard analog servo is not about sending complex data packets but about generating a specific, repeating electrical pulse. The servo’s internal circuitry interprets the width of this pulse to determine the target position of its output shaft.
The standard control signal is defined by two key parameters:
1. Period (Frequency):The signal repeats every 20 milliseconds (ms). This equates to a frequency of 50 Hz (1 / 0.02 s = 50 Hz).
2. Pulse Width (Duty Cycle):The duration the signal is high (logic 1) within that 20 ms period. This value directly maps to the servo's angle.
The mapping between pulse width and angle is the critical piece of information. For the vast majority of standard servos, the relationship is linear and follows this industry-standard specification:
1.0 ms pulse:Rotates the servo to 0 degrees (full counter-clockwise).
1.5 ms pulse:Rotates the servo to 90 degrees (center position).
2.0 ms pulse:Rotates the servo to 180 degrees (full clockwise).
While many servos adhere to this standard, it is essential to verify the exact range for your specific model, as some high-torque or specialized servos may have slightly different endpoints (e.g., 0.9 ms to 2.1 ms). The code examples below are structured to allow easy adjustment of these minimum and maximum pulse width constants.
The code logic is consistent across all platforms: initialize a timer/counter to generate a 50 Hz signal and then modulate the "on-time" of that signal to set the servo angle. The following pseudo-code demonstrates the core logic, which you can adapt to any programming environment.
// --- Configuration Constants --- #define PWM_FREQUENCY_HZ 50 #define PERIOD_MS (1000 / PWM_FREQUENCY_HZ) // Calculates to 20 ms #define PULSE_MIN_MS 1.0 // Pulse width for 0 degrees #define PULSE_MAX_MS 2.0 // Pulse width for 180 degrees #define ANGLE_MIN 0 #define ANGLE_MAX 180 // --- Function to map an angle to a pulse width --- float angleToPulseWidth(int angle_degrees) { // Constrain angle to valid range if (angle_degrees ANGLE_MAX) angle_degrees = ANGLE_MAX; // Linear mapping from angle range to pulse width range float pulse_width = PULSE_MIN_MS + ( (float)(angle_degrees - ANGLE_MIN) / (ANGLE_MAX - ANGLE_MIN) )(PULSE_MAX_MS - PULSE_MIN_MS); return pulse_width; } // --- Main Control Loop Concept --- // In a real implementation, the following would be handled by a hardware timer. // 1. Set the output pin HIGH. // 2. Wait (delay) for the calculated pulse width (e.g., 1.5 ms). // 3. Set the output pin LOW. // 4. Wait for the remainder of the 20 ms period (PERIOD_MS - pulse_width).
To make this immediately actionable, here are concrete implementations for two of the most common development platforms. The principles remain identical, demonstrating the portability of the 50 Hz, 1-2 ms standard.
This is the most common entry point for hobbyists. The ArduinoServolibrary abstracts the hardware timer complexity, providing a clean interface. This example is based on the standard practice of connecting the servo's signal wire to a PWM-capable digital pin.
#include// Create a servo object Servo myServo; // Define the pin connected to the servo's signal wire const int servoPin = 9; void setup() { // Attach the servo object to the pin // The library automatically sets up the 50 Hz signal myServo.attach(servoPin); // --- Common Scenario: Center the servo on startup --- // In many robotic applications, it is crucial to start from a known, // safe position to avoid mechanical strain. Centering at 90 degrees // is a universal best practice. myServo.write(90); // Move to center position delay(1000); // Allow time to reach position } void loop() { // Sweep from 0 to 180 degrees for (int angle = 0; angle = 0; angle--) { myServo.write(angle); delay(15); } }
Note: Thewrite()function automatically converts the angle (0-180) to the correct pulse width (1-2 ms).
On a Raspberry Pi, you typically generate the PWM signal in software, which requires precise timing. TheRPi.GPIOlibrary provides a hardware-backed PWM interface on specific pins for more accurate signals. This example reflects the standard approach for single-servo control on a Linux-based single-board computer.
import RPi.GPIO as GPIO import time # Pin Definitions servo_pin = 18 # Use a pin that supports hardware PWM, like GPIO 18 GPIO.setmode(GPIO.BCM) GPIO.setup(servo_pin, GPIO.OUT) # PWM Setup: 50 Hz frequency pwm = GPIO.PWM(servo_pin, 50) pwm.start(0) # Start with 0% duty cycle def set_angle(angle): """Convert an angle (0-180) to a duty cycle for a 50 Hz signal.""" # Constrain angle angle = max(0, min(180, angle)) # Map angle (0-180) to pulse width (1-2 ms) # For a 20 ms period (50 Hz), duty cycle = (pulse_width_ms / 20) 100 pulse_width_ms = 1.0 + (angle / 180.0)1.0 # Maps to 1.0-2.0 ms duty_cycle = (pulse_width_ms / 20.0)100.0 pwm.ChangeDutyCycle(duty_cycle) # --- Common Scenario: Calibrating a new servo --- # When first integrating a servo, it is critical to verify its true # mechanical limits. The code below demonstrates a safe calibration routine. try: # Test center position print("Moving to 90 degrees...") set_angle(90) time.sleep(2) # Test 0-degree position (1.0 ms pulse) print("Moving to 0 degrees...") set_angle(0) time.sleep(2) # Test 180-degree position (2.0 ms pulse) print("Moving to 180 degrees...") set_angle(180) time.sleep(2) # Return to center print("Returning to center.") set_angle(90) time.sleep(1) except KeyboardInterrupt: print("Stopped by user") finally: pwm.stop() GPIO.cleanup()
Note: This method uses duty cycle calculation. For a 50 Hz signal, a 1 ms pulse corresponds to a duty cycle of (1/20)100 = 5%. A 2 ms pulse is (2/20)100 = 10%.
Even with correct code, several hardware-related issues can prevent a servo from functioning. Understanding these common scenarios will help you quickly diagnose and resolve problems.
1. Insufficient Power Supply:This is the most frequent issue. A typical servo can draw 200-500 mA when moving and even higher stall currents. Microcontroller USB ports (typically 500 mA) are often insufficient, especially for multiple servos.
Solution:Use a dedicated external power supply (e.g.,5V from a battery pack or a regulated bench supply). Ensure the ground (GND) of the microcontroller is connected to the ground of the external power supply.
2. Incorrect Timing Values:If the servo jitters, buzzes, or doesn't move to the full range, the pulse width limits likely do not match the servo's specification.
Solution:Consult the servo's datasheet for the exact pulse width range. A common variation is 0.9 ms to 2.1 ms. Adjust thePULSE_MIN_MSandPULSE_MAX_MSconstants in your code accordingly.
3. Frequency Drift in Software PWM:On platforms like the Raspberry Pi without dedicated hardware PWM pins, software-generated PWM can suffer from timing inconsistencies due to operating system multitasking.
Solution:For critical or high-speed applications, use a dedicated servo controller board (like a PCA9685) that offloads the timing to a dedicated hardware chip, ensuring stable 50 Hz generation.
To successfully drive a servo with PWM code, remember these three core principles:
1. Standardize the Signal:Always aim to generate a precise 50 Hz signal. The pulse width is the only variable that controls position.
2. Map Angles to Pulses:Use the linear mapping from 1.0 ms (0°) to 2.0 ms (180°) as your baseline. This formula works for over 90% of standard servos.
3. Prioritize Power:Verify your power source can deliver the required current. A servo that twitches erratically is almost always a power problem, not a code problem.
Action Step:Begin with the simple sweep code on your chosen platform using a single servo. Once the sweep works, replace the hardcoded angles with sensor inputs, joystick values, or calculated positions to integrate the servo into your larger project. By adhering to the 50 Hz standard and verifying your power supply, you create a robust foundation for any motion-control application.
Update Time:2026-04-01
Contact Kpower's product specialist to recommend suitable motor or gearbox for your product.