[Back to Home Page]


DC motor crystal clockwork!
A system for driving a cheap DC motor very slowly, and very accurately - 8th April 2013.

What is it?

This is a very simple way to use a cheap PIC to run a DC motor at a very slow speed, and for that speed to have very high accuracy.

This can do tasks that were previously done using a stepper motor;
* Silent clock movements
* Telescope driving
* Solar array tracking
* Video camera track movement

It gives these benefits;
* Uses much less power, and is quieter/smoother than a stepper motor
* Motor speed is locked to a crystal, keeps "clock" time accuracy
* Motor speed is adjustable in incredibly fine steps (parts per billion)
* Motor speed can be VERY slow (less gearing needed)
* Auto-recovers speed and position after being bumped
* Very simple driving electronics (no stepper driver needed)

How it works

The main goal of this was to replace stepper motors in precise clock-movement type applications. Unlike a stepper motor, a DC motor might speed up or slow down in response to short term load events, (ie; a few degrees or fractions of a second) so the main focus was on making the average speed perfect.

To maintain a perfect average speed the PIC generates a reference frequency using a Bresenham math algorithm (similar to the systems on my page here.) That has the benefit of being able to make any perfect average frequency, (which sets the motor speed) from any xtal value. It is also adjustable in incredibly fine steps, so it can be "tweaked" to make highly accurate clocks and telescope tracking.

Then the motor speed is recorded using a quadrature encoder mounted on the motor shaft. Many DC motors on ebay etc come with quadrature encoders already attached, but I just glued on a disc and sensor from an old PC "ball" mouse;

The big benefit of a quadrature encoder in this app is that it cannot lose count of the motor position, so even if the motor is bumped or jerked forward OR backward by some load force the whole system will just "catch up" afterward and still give perfect average motor speed.

The reference frequency is used to count forward and makes a "reference position". This count is constantly compared to the motor position, and if the motor lags behind the reference then PWM is increased.

I developed a similar system in 2011 for my "Pulse Counting Loop" algorithm, for mains synced clocks. This is similar in some ways to a "Phase Locked Loop" but is better as it can repair itself and recover perfect timing even after being out of phase by thousands of cycles.

(See above) By the time the motor has lagged about 10 encoder counts the PWM will reach max, so the difference between the "perfect" reference position and where the motor is at any time will generally only be a couple of encoder counts (a few degrees).

The system works very well! The digital system used to record the reference and motor position maintains a perfect average speed (as perfect as your xtal). Likewise it can be better than a stepper motor as it uses much less current, doesn't have the harsh vibration, and any short term over-driving or backdriving error is accounted for and will be fully corrected right after the fault is finished.

The algorithm as C code

Everything for motor movement is handled in a timer interrupt. This makes it very easy to use, just put the interrupt in your PIC C code and set one number to the speed you want, then the motor will run automatically;

// This constant sets the motor speed. The value is in nanoSeconds per pulse,
// as we are using a quadrature encoder there are 4 pulses per encoder slot.
// Example; 1 motor rev per second = 144 pulses /sec.
// nS per pulse = 1 billion / 144 = 6944444
//#define  MOTOR_PULSE_PERIOD  3472222    // 2 RPS
//#define  MOTOR_PULSE_PERIOD  6944444    // 1 RPS
#define  MOTOR_PULSE_PERIOD  13888889   // 0.5 RPS

void interrupt()
  // This is TMR0 int, prescaled at 2:1 so we get here every 512 instructions.
  // This int does the entire closed loop speed control;
  //  1. updates encoder to see if motor has moved, records it position
  //  2. updates reference freq generator, records its position
  //  3. compares the two, sets PWM if motor lags behind reference
  //  4. limit both counts, so they never roll, but still retain all error
  // clear int flag straight away to give max safe time
  INTCON.T0IF = 0;

  //  1. updates encoder to see if motor has moved, records it position
  enc_new = (PORTA & 0b00000011);   // get the 2 encoder bits
  if(enc_new != enc_last)
    if(enc_new.F1 != enc_last.F0) mpos++;   // record new motor position
    else                          mpos--;
    enc_last = enc_new;

  //  2. updates reference freq generator, records its position
  bres += 102400;                 // add nS per interrupt period (512 insts * 200nS)
  if(bres >= MOTOR_PULSE_PERIOD)  // if reached a new reference step
    rpos++;                       // record new xtal-locked reference position

  //  3. compares the two, set PWM% if motor lags behind reference
  if(mpos < rpos)   // if motor lagging behind
    mlag = (rpos - mpos);                            // find how far it's lagging behind
    if(mlag >= (100/MOTOR_PROP_GAIN)) CCPR1L = 100;  // full power if is too far behind
    else CCPR1L = (mlag * MOTOR_PROP_GAIN);          // else adjust PWM if slightly behind
  else            // else if motor is fast, cut all power!
    CCPR1L = 0;

  //  4. limit both counts, so they never roll, but still retain all error
  if(rpos>250 || mpos>250)
    rpos -= 50;
    mpos -= 50;


My test setup

I found a small DC motor in my junkbox. This one is a 12v motor from a CD or DVD player. It originally had a tiny pulley and belt and was used to slide the CD drawer in and out. These 12v motors are made for reasonably low RPM use, which is a good choice for this app. A 12v low speed motor will work better here than a 3v high speed motor!

Testing the motor.
I ran the motor from regulated 5v and it worked fine, and was quite energy efficient using only about 100mA no load. The next step was to hook up a driver transistor and run PWM to the motor to control its speed...

Motor RPM at 5v and different PWM duty %;
5v PWM 40%, RPM = 434
5v PWM 60%, RPM = 786
5v PWM 80%, RPM = 1138
5v PWM 100%, RPM = 1460

I also tested the stall speed of the motor. The motor would not run any slower than approx 70 RPM.

The encoder.
I have DC motors with quadrature encoders already attached, like these common examples;

But instead I wanted to show how you can just add an encoder to any motor, and I wanted to test a motor with no gearing. The 36-slot encoder wheel came out of an old Microsoft Mouse, as did the sensor electronics. I just cut through the original mouse PCB and soldered 4 wires direct to the pins of the opto devices.

The optos are extremely simple and are just an InfraRed LED on one side of the slotted disc, and a dual photo diode on the other side of the disc.

I glued the sensor PCB to the motor with a blob of hot melt glue, making sure it was in the right position (same as how it was in the mouse).

The 36 slots and two photo sensors are run in "quadrature" mode, so it gives total pulses of 144 per revolution (36 * 4).

It only takes 3 resistors to interface the quadrature encoder sensor to the PIC; a 1k series resistor for the IR LED, and two bias resistors for the dual photo diodes. I used 20k trimpots for those two bias resistors, as that makes it easy to adjust the biasing to give a nice 50:50 duty of the signals into the PIC digital input pins. (Quadrature encoders work best if both signals are squarewaves with 50:50 duty).

Above you can see my 'scope capture of the encoder waveform from one of the encoder's photodiode sensors (bottom in blue), and how the PIC Schmidt Trigger input sees it (top in yellow). The motor was running a bit over 4 revs per second, at this speed the photodiode wave looks like a sine. At slower speeds the photodiode wave looks more "digital".

I got the PIC to output on two spare digital output pins what it actually "sees" on its Schmidt Trigger encoder inputs. This is a handy trick that lets you bias the encoder photodiodes to give good 50:50 duty.

Above shows the pair of encoder inputs as the PIC sees them. They are not absolutely perfect in terms of 50:50 duty or phase, but this is quite ok for such a small cheap sensor that was glued together. :)

Motor driver electronics.
To show how simple it can be done I just used a single transistor to drive the motor with PWM. No IC's needed.

As this motor only needed <200mA I just used a tiny BC337 transistor which is good for 500mA continuous and 1A pulsed. The PIC PWM digital output pin drives the transistor base through a simple 560 ohm resistor. On a larger motor you would use a larger NPN transistor or better still, an N-channel FET.

The only other part needed was a back-EMF diode across the motor, I just used a tiny 1N4148 diode as this is a very small motor <200mA. With a larger motor < 1A you would use a 1N5819 schottky diode, or on a big motor of multiple amps you could use a T0-220 pack schottky diode (both the diode and FET must be rated for average motor current).

I controlled the motor from my EasyPIC6 development board. All the encoder hardware and PWM driver transistor etc are shown here. Apart from the PIC and its xtal, there are only a few parts needed.

Above is the entire schematic of my test setup, you can see how simple it is. Even with the added complexity of a quadrature encoder there are still very few parts with the PIC (and software!) doing all the clever work.

Notes on the schematic.
The PIC digital outputs TPA and TPB echo the two quadrature encoder signals. You can connect a 'scope to these 2 points and adjust the trimpots to get good 50:50 encoder sensor duty cycle. Once the system is set up these pins are no longer needed.

I used a tiny NPN transistor to drive the motor as my motor only needed a handful of mA. For a larger motor you could use a NFET as the motor PWM driver, and a large Schottky diode across the motor. You could also use a motor-driver h-bridge IC, especially if you wanted to run the motor in either direction (for track/return in solar panel tracking).

As my motor was tiny I chose to run it from the same regulated 5v supply as the PIC. For that reason the schematic has an extra 330uF cap and a 10 ohm resistor. These two parts stop any noise from the motor PWM from interfering with the encoder signals, and also allowed me to easily measure the average motor current consumption by measuring the voltage drop across the resistor. In most cases you would run a separate power supply for the motor (especially for large motors!) so these two parts are probably not needed.


Here is a short movie of the ungeared DC motor doing 0.5 revs per second (30 RPM) nice and smoothly. :) I put a blob of yellow paint on the disc so you can see it rotating.

Click for Movie (6.90 Mb)

Energy efficiency!

This was a very pleasant surprise! With the DC motor (ungeared) doing exactly 1 rev per second and no load, the entire motor power consumption was 5v at 4mA! The PIC and the encoder LED consumed more power than that!

Even with moderate load on the motor the power was still under 5v 20mA. For lightly loaded systems with high gearing (clocks or solar trackers etc) this is many times less power consumption than even a tiny stepper motor.


The download ZIP file contains the fully tested PIC MikroC source code, the HEX file for a PIC16F628A, and a couple of schematic diagrams etc.

Download DCmotor_xtal.ZIP file

Limitations of my code

The code supplied needs the interrupt to happen a few times faster than the encoder pulses, when the motor is doing the set speed. That allows the encoder to still track situations where the motor might run at 4 times faster than the set speed (like being bumped etc). So my PIC 16F628A 20MHz code above is limited to a max encoder speed of about 2500 Hz. (Note! At 1 rev per second my example was running at 144 Hz).

Going to a PIC 18F could double that 2500 Hz, and there are ways the code could be optimised for speed to make it significantly faster again.

I have not bothered with that as the main point of this system is to run the motor at an exact SLOW speed. If you need to run the motor at an exact fast speed one easy option is to reduce the number of slots on the encoder disk, and with a minimum 1 slot disc it will make 4 pulses per motor rev, allowing a motor speed of 2500/4 = 625 revs/sec or 37500 RPM!

Very slow motor speeds

The DC motor I was using was a nice low-RPM motor type, tested at only 1460 RPM at 5v. Motors designed for low-RPM use will be much more stable if you need very low RPM drive like this.

I had no trouble running this motor smoothly at 1 rev per second (60 RPM), and it even worked pretty good at 0.5 revs per second (30 RPM). Note; both these speeds were below the 70 RPM stall speed of the motor! (see video above).

Running a DC motor near or below its stall speed.
DC motors have strong springy magnetic "detents", caused by the permanent magnet stator pulling at the iron in the armature. Small DC motors often have 3 or 6 obvious detents per rotation, if you turn the motor by hand.

If the PWM (or the average motor voltage) gets low enough the motor will just stop rotating, a situation called "stall". For this reason, it is almost impossible to rotate the motor smoothly at speeds below the stall speed.

To run the motor at those low speeds requires some type of PWM cycling where the PWM is high enough to make the motor move a few degrees, then PWM needs to be lowered or cut so the motor does not run too fast. This is repeated in a very fast cycle and with the benefit of some rotating inertia the overall rotation can be smooth (or smooth enough) and a very low RPM can be achieved.

The system described here will give this effect automatically, and maintain the exact average motor speed. Whether the motor turns smoothly at the exact speed, or moves with twitches or jumps at the exact average speed, is determined by a number of factors.

How to get very low RPM from a permanent magnet DC motor

1. Use a geared motor. OK, this one is cheating. :) More gearing means the motor can be run above it's stall speed and still get the final shaft turning at very low RPM. Fortunately most DC motors with encoders you can buy from hobby suppliers also have gearboxes on the motor.

2. Add more constant load to the motor. Adding load means more PWM is applied making the torque more stable, and likewise the load will dampen tendencies for the rotation to jump around. Even driving a gearbox can be enough load to damp the motor and give smooth rotation. (This is the first thing to check if your motor is jumping around, just let a fingertip drag lightly on the spinning shaft and it will probably stabilise.)

3. Use a low-RPM motor. Some motors run better at low speeds than others! Some testing can be very helpful. Generally 12v and 24v motors can be run slower than 3v and 6v motors. Larger motors (especially larger diameters) usually are more stable at lower RPMs.

4. Use a finer encoder with more pulses per rev. An encoder with more slots runs the closed loop system more times per rev so will be more stable at lower RPM. With less slots you will need to run the motor faster, or tolerate some jumpiness in the rotation (average speed will still be perfect).

5. Add flywheel inertia to the motor. A flywheel or weighted disc directly on the motor shaft can help make the motor rotation more stable.

6. Reduce the PWM frequency. Lower frequency PWM will reduce the stall speed as the motor will tend to move in tiny pulses. My software uses PWM at 3125 Hz but commercial traction drivers using DC motors can run as low as a couple hundred Hz.

7. Tune the gain in my software. The proportional gain (how many encoder pulses lag does it take to go from 0-100% PWM) can be adjusted in software. I found a gain of about 10 steps worked well, so PWM would oscillate between 20,30,40% to maintain the correct speed, and that was enough torque ripple to avoid stall which would occur if the PWM was say at a constant 24%.

- end -

[Back to Home Page]