Advertisement
Beginner Time: 1–2 weeks Electrical Engineering

Motor Speed Control using PWM

Control DC motor speed and direction using PWM signals with L298N H-bridge and closed-loop PID controller.

PWMDC MotorH-BridgeL298NArduinoPID
DifficultyBeginner
Duration1–2 weeks
Components10 items
Steps6 steps

Introduction

Control DC motor speed and direction using PWM signals with L298N H-bridge and closed-loop PID controller. This comprehensive guide covers everything from design through implementation, testing, and deployment.

Theory & Background

PWM (Pulse Width Modulation) controls the average voltage delivered to the motor by rapidly switching a fixed supply voltage on and off. At 50% duty cycle, the motor receives 6V average from a 12V supply. Increasing duty cycle increases average voltage and therefore speed. The L298N H-bridge allows current to flow in either direction through the motor, enabling both forward and reverse direction control by toggling the IN1/IN2 logic inputs.

Advertisement

Components & Requirements

10 components required for this project.

#ComponentPurposeQty
1Arduino UnoPWM signal generation and PID controlx1
2L298N H-Bridge ModuleMotor driver capable of bidirectional controlx1
3DC Motor (12V, 3000 RPM)Controlled loadx1
4Rotary Encoder (600 PPR)Speed feedback for closed-loop controlx1
510kΩ PotentiometerSpeed setpoint inputx1
612V 2A Power SupplyMotor powerx1
70.1µF Decoupling CapacitorsMotor noise suppressionx4
81N4007 Flyback DiodesBack-EMF protection (if using discrete H-bridge)x4
9OLED Display 0.96"Speed displayx1
10Heatsink for L298NThermal managementx1

Step-by-Step Implementation

Follow these 6 steps carefully.

1
Understanding PWM and Motor Control

PWM (Pulse Width Modulation) controls the average voltage delivered to the motor by rapidly switching a fixed supply voltage on and off. At 50% duty cycle, the motor receives 6V average from a 12V supply. Increasing duty cycle increases average voltage and therefore speed. The L298N H-bridge allows current to flow in either direction through the motor, enabling both forward and reverse direction control by toggling the IN1/IN2 logic inputs.

2
Hardware Connections

Connect L298N: ENA pin to Arduino PWM pin (pin 9), IN1 to pin 7, IN2 to pin 8 for motor A direction control. Connect 12V motor supply to L298N power input (remove 5V jumper). Connect encoder VCC to 5V, GND to GND, and A/B channels to Arduino interrupt pins 2 and 3. Wire potentiometer middle pin to A0 for speed setpoint.

3
Encoder Interrupt and Speed Calculation

Attach interrupt to encoder channel A: attachInterrupt(0, encoder_ISR, RISING). In ISR, increment pulse counter. Calculate RPM every 100ms: RPM = (pulse_count × 60) / (PPR × time_seconds). For 600 PPR encoder with quadrature decoding: RPM = (pulses × 60 × 1000) / (600 × dt_ms). This gives real-time speed feedback for the PID controller.

4
PID Controller Implementation

Error = setpoint_RPM - actual_RPM. P_output = Kp × error. I_output += Ki × error × dt (integrate error over time). D_output = Kd × (error - prev_error) / dt. PID_output = P + I + D, clamped to 0–255 for analogWrite. Start tuning: Kp=0.5, Ki=0.01, Kd=0.05. Increase Kp until oscillation, then reduce 30%, add Ki to eliminate steady-state error, add Kd to reduce overshoot.

5
Direction Control Logic

Set forward: IN1=HIGH, IN2=LOW, ENA=PWM. Set reverse: IN1=LOW, IN2=HIGH, ENA=PWM. Stop: IN1=LOW, IN2=LOW (coast) or IN1=HIGH, IN2=HIGH (brake). When changing direction, ramp speed down to 0 first to prevent motor and driver stress. Implement software braking by briefly applying reverse PWM to decelerate quickly — useful for precise position applications.

6
Soft-Start and Overspeed Protection

Never command full speed instantly from stop — the inrush current can trip the power supply and stress gears. Implement a soft-start: ramp the PWM from 0 to setpoint over 2 seconds. Set a maximum RPM limit in code: if actual RPM > max_RPM (e.g., 2800), override PID output and reduce duty cycle. Monitor L298N temperature; if the module is too hot to touch, reduce maximum duty cycle by 10%.

Code & Implementation

Core code for motor_pid.ino:

motor_pid.ino C/C++
volatile long encoderCount = 0; float kp = 0.8, ki = 0.02, kd = 0.05; float setpoint = 1000;  float integral = 0, prevError = 0;  void encoderISR() { encoderCount++; }  float getRPM() {   static long lastCount = 0;   static unsigned long lastTime = 0;   unsigned long now = millis();   long count = encoderCount;   float rpm = ((count - lastCount) * 60000.0) / (600.0 * (now - lastTime));   lastCount = count; lastTime = now;   return rpm; }  void loop() {   setpoint = map(analogRead(A0), 0, 1023, 0, 2800);   float actual = getRPM();   float error  = setpoint - actual;   integral += error * 0.1;   integral = constrain(integral, -100, 100);   float derivative = (error - prevError) / 0.1;   float output = constrain(kp*error + ki*integral + kd*derivative, 0, 255);   prevError = error;   analogWrite(9, (int)output);   delay(100); }

Testing & Troubleshooting

Test Motor Speed Control using PWM by verifying each subsystem individually before full integration.

!
Troubleshooting Tips

Verify power voltages, check ground connections, use serial monitor for debug.

Real-World Applications

*DC servo motor positioning
*Conveyor speed regulation
*Fan and pump variable speed
*Electric toy/vehicle drivetrain
*Camera gimbal motor control
*3D printer extruder motor
*Robotic arm joint control
*DIY CNC axis drive

Extensions & Next Steps

  • Implement full PID auto-tuning algorithm
  • Add encoder-based position control for servo-like operation
  • Scale up to 3-phase BLDC motor control
  • Build a multi-axis synchronized drive system
  • Implement regenerative braking energy recovery

Interactive Playground

Coming Soon

An interactive simulator will be available here — simulate circuits and run code in-browser without hardware.

Frequently Asked Questions

Why does my motor vibrate at low PWM duty cycles?
At very low duty cycles (below ~15%), the PWM on-time may be shorter than the motor's mechanical time constant, causing it to stutter rather than spin smoothly. Solutions: increase PWM frequency to 20–30kHz (above audible range) using Timer1 direct register manipulation to eliminate audible whine, add a small amount of minimum duty cycle to ensure the motor is always slightly above its friction threshold, or use a linear amplifier instead of PWM for very low speed applications.
What is the difference between coast and brake in motor stopping?
Coast: set both IN1 and IN2 LOW — both H-bridge switches open, motor terminals float, motor decelerates purely through friction — slow stop. Brake: set both IN1 and IN2 HIGH — both motor terminals connected to ground (short-circuit braking) — motor's back-EMF drives current through its own windings, creating strong opposing torque for rapid deceleration. Brake is preferred for precise positioning; coast for gentle stopping.
Can L298N control stepper motors too?
Yes — the L298N can drive bipolar stepper motors using both H-bridge channels (Channel A and B). For a 4-wire bipolar stepper, connect the two coil pairs to the two motor output channels. Generate the 4-step or 8-step (half-step) sequence using digital output pins in the correct order. However, L298N has no microstepping capability and its 2V voltage drop limits efficiency — dedicated stepper drivers (A4988, DRV8825) are preferred for steppers.
What causes L298N to get very hot?
L298N is a bipolar junction transistor (BJT) H-bridge with approximately 2V voltage drop per switch. At 2A motor current, each switch dissipates 4W — up to 8W total in the IC. This is very inefficient compared to MOSFET-based drivers (0.1V drop). Mitigate: attach a large heatsink, reduce maximum current if possible, consider replacing with a MOSFET-based driver (BTS7960, DRV8876) for higher efficiency and lower heat.
Advertisement