Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Arduino & Microcontrollers

Arduino PWM Motor Speed Control: Beyond analogWrite()

Arduino PWM Motor Speed Control: Beyond analogWrite()

March 11, 2026 /Posted byJayesh Jain / 0

When it comes to Arduino PWM motor speed control, most tutorials stop at analogWrite() — a single line that gets you moving but leaves enormous capability on the table. True motor control, the kind you see in robotics, CNC machines, and industrial automation, demands a deeper understanding of Pulse Width Modulation: custom frequencies, complementary outputs, dead-time insertion, and hardware timer registers. This guide takes you from beginner analogWrite() all the way to direct Timer manipulation, so you can achieve smooth, precise, and efficient motor control on any Arduino project.

Table of Contents

  • 1. PWM Basics: What analogWrite() Actually Does
  • 2. Hardware Timers: The Engine Behind PWM
  • 3. Setting Custom PWM Frequencies
  • 4. Choosing the Right Motor Driver
  • 5. L298N H-Bridge Wiring and Code
  • 6. MOSFET-Based High-Current Control
  • 7. Smooth Acceleration and Deceleration
  • FAQ

1. PWM Basics: What analogWrite() Actually Does

Pulse Width Modulation is not a true analog signal — it is a digital signal rapidly switched between HIGH and LOW. The ratio of HIGH time to the total period is called the duty cycle. A 50% duty cycle means the pin is HIGH for exactly half the time, delivering 50% of the supply voltage to a motor on average.

analogWrite(pin, value) accepts a value from 0 to 255, mapping to 0%–100% duty cycle. On the Arduino Uno, this runs at approximately 490 Hz on most pins and 980 Hz on pins 5 and 6. For many small DC motors, these frequencies work fine. But problems arise with larger motors, servo-style drivers, and audio — you hear a whine at 490 Hz, or the motor stutters because the frequency is too low for its inductive load to smooth out.

The duty cycle calculation is simple:

Duty Cycle (%) = (value / 255) × 100

At value=128, you get ~50% duty, which delivers approximately half the supply voltage to the motor. At value=255, the motor runs at full speed. At value=0, it stops.

Recommended: Arduino Uno R3 Beginners Kit — Includes the Uno board, motor driver breakout, and essential components to start PWM motor control experiments right away.

2. Hardware Timers: The Engine Behind PWM

The Arduino Uno (ATmega328P) has three hardware timers — Timer0, Timer1, and Timer2. Each timer controls specific PWM pins:

  • Timer0 (8-bit): Pins 5 and 6. Also drives millis() and delay() — modify with caution.
  • Timer1 (16-bit): Pins 9 and 10. The most flexible for custom frequencies and high resolution.
  • Timer2 (8-bit): Pins 3 and 11. Used by the tone() function.

The Arduino Mega 2560 adds Timer3, Timer4, and Timer5, giving you PWM on pins 2, 3, 5, 6, 7, 8, 11, 12, 13, 44, 45, and 46 — enormously useful for multi-motor projects.

Each timer has two key registers:

  • TCCRnA / TCCRnB: Timer/Counter Control Registers — set the waveform mode and prescaler.
  • OCRnA / OCRnB: Output Compare Registers — set the duty cycle threshold.

The prescaler divides the 16 MHz system clock before feeding it to the timer. Prescaler options are 1, 8, 64, 256, and 1024. A higher prescaler = lower PWM frequency but wider range of counter values.

Recommended: Arduino Mega 2560 R3 Board — Six hardware timers and 15 PWM pins make the Mega the ideal board for multi-motor, multi-axis speed control projects.

3. Setting Custom PWM Frequencies

To change the PWM frequency without a library, you manipulate the prescaler bits directly. Here is how to set Timer1 (pins 9 and 10) to various frequencies on the Uno:

// Fast PWM, 8-bit resolution on Timer1 (pins 9 and 10)
// Prescaler 1 → ~62.5 kHz
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10);
TCCR1B = _BV(WGM12) | _BV(CS10); // CS10 = prescaler 1

// Use analogWrite(9, value) or analogWrite(10, value) as normal after this

Common prescaler settings for Timer1 (16 MHz Uno):

  • Prescaler 1 (CS10): ~62,500 Hz — near-silent for most motors
  • Prescaler 8 (CS11): ~7,812 Hz — good balance of speed and resolution
  • Prescaler 64 (CS11+CS10): ~976 Hz — close to default
  • Prescaler 256 (CS12): ~244 Hz — low frequency, visible flicker on LEDs

For even more control, use Timer1’s 16-bit mode with a custom TOP value (ICR1 register). This lets you set virtually any frequency:

// Set frequency to exactly 20 kHz on pins 9 and 10
int freq = 20000;
int prescaler = 1;
int top = (16000000 / (prescaler * freq)) - 1; // = 799

TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
ICR1 = top; // Set TOP

// Set duty cycle (e.g., 50%)
OCR1A = top / 2; // Pin 9
OCR1B = top / 2; // Pin 10

At 20–25 kHz, PWM is above the audible range — motors run silently and MOSFETs switch efficiently. This is the sweet spot for most DC motor applications.

4. Choosing the Right Motor Driver

Arduino GPIO pins source only 40 mA maximum — not nearly enough to drive even a small DC motor directly. You need a driver IC between the Arduino and the motor. The right choice depends on your motor’s voltage and current rating:

  • L298N H-bridge: 2A per channel, up to 46V. Common, inexpensive, but inefficient (2V drop per direction). Best for 6–12V motors up to 2A.
  • L293D: 600 mA per channel, similar architecture to L298N. Good for micro motors and prototyping.
  • TB6612FNG: 1.2A continuous (3.2A peak), very low dropout. More efficient than L298N, smaller footprint.
  • DRV8833: 1.5A per channel, ideal for small bots and 3.3V/5V systems.
  • IBT-2 (BTS7960): 43A continuous, up to 27V. For high-current motors in e-bikes, go-karts, and heavy robotics.
  • N-channel MOSFET (IRLZ44N, IRFZ44N): For uni-directional (one-way) PWM control of large motors. Extremely efficient, no built-in direction control.

For most Arduino hobby projects, the L298N or TB6612FNG covers 90% of use cases. Use MOSFET-based drivers only when current exceeds 3A or efficiency is critical.

5. L298N H-Bridge Wiring and Code

The L298N module is the most common motor driver found in beginner kits. Here is the complete wiring for a single DC motor with PWM speed control:

Wiring (Arduino Uno ↔ L298N):

  • Arduino Pin 9 → L298N ENA (Enable A, PWM pin)
  • Arduino Pin 4 → L298N IN1
  • Arduino Pin 5 → L298N IN2
  • L298N OUT1 → Motor terminal 1
  • L298N OUT2 → Motor terminal 2
  • 12V supply → L298N VS (motor power)
  • L298N GND → Arduino GND (common ground is mandatory)
#define ENA 9
#define IN1 4
#define IN2 5

void setup() {
  pinMode(ENA, OUTPUT);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
}

void setMotor(int speed, bool forward) {
  // speed: 0-255, forward: true/false
  digitalWrite(IN1, forward ? HIGH : LOW);
  digitalWrite(IN2, forward ? LOW : HIGH);
  analogWrite(ENA, speed);
}

void loop() {
  setMotor(200, true);  // Forward at ~78% speed
  delay(2000);
  setMotor(0, true);    // Stop
  delay(500);
  setMotor(180, false); // Reverse at ~70% speed
  delay(2000);
  setMotor(0, false);   // Stop
  delay(500);
}

For higher PWM frequency on pin 9 (Timer1), add this to setup():

// Change Timer1 prescaler to 1 → ~62.5 kHz silent PWM
TCCR1B = TCCR1B & B11111000 | B00000001;
Recommended: Arduino Nano Every with Headers — The ATMega4809 processor on the Nano Every offers TCB timers with more flexible PWM configuration, and its compact size fits perfectly in motor driver breadboard setups.

6. MOSFET-Based High-Current Control

When your motor draws more than 2–3A, or when efficiency matters (battery-powered robots), replace the H-bridge with an N-channel logic-level MOSFET like the IRLZ44N or IRFZ44N. These are rated for 47A and switch fully on with just 5V gate voltage — perfect for direct Arduino control.

Wiring for unidirectional MOSFET control:

  • Arduino PWM pin → 10Ω resistor → MOSFET Gate
  • MOSFET Gate → 10kΩ resistor → GND (pull-down, prevents floating gate)
  • MOSFET Drain → Motor negative terminal
  • Motor positive terminal → 12V supply positive
  • 12V supply negative → MOSFET Source → Arduino GND
  • Flyback diode (1N5819 or 1N4007) across motor terminals: anode to drain, cathode to 12V rail

The flyback diode is critical — it protects the MOSFET from the voltage spike generated when a motor’s magnetic field collapses during PWM off-time. Skipping it will destroy the MOSFET within minutes of operation.

#define MOTOR_PIN 9

void setup() {
  pinMode(MOTOR_PIN, OUTPUT);
  // Set Timer1 to ~20 kHz for silent operation
  TCCR1A = _BV(COM1A1) | _BV(WGM11);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
  ICR1 = 799; // 20 kHz TOP value
}

void setSpeed(int percent) {
  // percent: 0-100
  OCR1A = map(percent, 0, 100, 0, 799);
}

void loop() {
  for (int i = 0; i <= 100; i += 5) {
    setSpeed(i);
    delay(100); // Ramp up
  }
  delay(2000);
  setSpeed(0);
  delay(1000);
}

7. Smooth Acceleration and Deceleration

Abrupt speed changes — jumping from 0 to 255 in one line — cause mechanical stress on gearboxes, overcurrent spikes that trigger driver protection, and jerky motion that makes robot movement look unpolished. Smooth ramping solves all three problems.

The simplest approach is a linear ramp:

void rampMotor(int startSpeed, int endSpeed, int stepDelay) {
  int step = (endSpeed > startSpeed) ? 1 : -1;
  for (int s = startSpeed; s != endSpeed; s += step) {
    analogWrite(ENA, s);
    delay(stepDelay); // ms per step
  }
  analogWrite(ENA, endSpeed);
}

// Ramp from 0 to full speed over 2.55 seconds
rampMotor(0, 255, 10);

For an S-curve (exponential feel), use a lookup table or a sine-based mapping:

// S-curve acceleration: smoother start and stop
for (int i = 0; i <= 90; i++) {
  float radians = (float)i * PI / 180.0;
  int pwm = (int)(127.5 * (1 - cos(radians)));
  analogWrite(ENA, pwm);
  delay(15);
}

For closed-loop speed control (maintaining constant RPM regardless of load), add a Hall effect sensor or encoder to your motor and implement a PID controller. The Arduino PID_v1 library makes this straightforward — measure actual RPM every 100ms, compute the error, and adjust the PWM accordingly.

Recommended: Arduino Starter Kit with 170 Pages Project Book — Covers PWM, motor control, and sensor integration with step-by-step projects — the ideal companion for building on the techniques in this guide.
Recommended: Arduino Nano 33 IoT with Header — Built-in WiFi lets you add remote speed control via a web interface or MQTT broker, turning any PWM motor project into an IoT-connected system.

Frequently Asked Questions

Why does my motor hum or whine when using PWM?

The whine is the motor’s coils vibrating at the PWM frequency. The default 490 Hz frequency falls in the audible range. Set your timer prescaler to achieve 15–25 kHz (above human hearing range) to eliminate the noise completely.

Can I use analogWrite() on any Arduino pin?

No. PWM is only available on hardware timer-connected pins, marked with a tilde (~) on the board. On the Uno: pins 3, 5, 6, 9, 10, 11. On the Mega: pins 2–13 and 44–46. Attempting analogWrite() on a non-PWM pin just outputs HIGH or LOW based on the value.

My L298N gets very hot. What is wrong?

The L298N has an internal voltage drop of about 2V per channel due to its bipolar transistor architecture. At high currents this dissipates significant power as heat. Add a heatsink, or switch to a MOSFET-based driver (TB6612FNG or DRV8833) which has near-zero voltage drop and runs much cooler.

How do I control two motors independently at different speeds?

Use both channels of the L298N (ENA→pin 9, ENB→pin 10) and set different PWM values independently. Both pins 9 and 10 share Timer1, so a prescaler change affects both channels simultaneously — useful for differential-drive robots where you want identical frequencies but independent duty cycles.

What is the difference between fast PWM and phase-correct PWM?

Fast PWM counts up from 0 to TOP and resets — highest frequency for a given prescaler. Phase-correct PWM counts up then counts back down, halving the effective frequency but producing a symmetrical waveform. Phase-correct is preferred for motor control because it reduces switching noise and is gentler on motor windings.

Ready to take your motor control further? Browse our full range of Arduino boards and motor driver accessories at Zbotic — India’s go-to store for electronics components and development boards.

Tags: analogWrite, Arduino, DC motor, H-bridge, L298N, motor control, motor driver, PWM
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Arduino RF 433MHz: Wireless Co...
blog arduino rf 433mhz wireless communication without wifi 594760
blog arduino interrupt service routine write fast isr code 594767
Arduino Interrupt Service Rout...

Related posts

Svg%3E
Read more

Arduino Batch Programming: Flash Multiple Boards Quickly

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Based Radar System with Ultrasonic Sensor

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Automatic Plant Monitor: Sunlight, Moisture, Temperature

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Lie Detector: GSR Sensor Polygraph Project

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Metal Detector: Build a Treasure Finder

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now