Microcontroller V's 555 Timer - Motor Speed Controller Showdown

by TomGoff in Circuits > Microcontrollers

8648 Views, 46 Favorites, 0 Comments

Microcontroller V's 555 Timer - Motor Speed Controller Showdown

IC_FIGHT_Crop.png
both.jpg

I frequently need a simple DC motor speed controller for my projects. Typically, these controllers are driven by a 0-5V analog signal, which could come from a microcontroller, PLC, or other control devices. There are many ways to build a speed controller, whether using a dedicated chip, a microcontroller, or analog circuitry.

These speed controllers differ from the simple manual speed controllers that use a manually operated potentiometer to control motor speed.

In the hardware hacking community, there's a common phrase when someone builds something simple with a microcontroller: 'You could have just used a 555!.' This project seeks to explore that question: Should you just use a 555?

In this Instructable, I will build both a simple 555-based DC motor speed controller and an equivalent microcontroller-based version. We'll then compare the strengths and weaknesses of each design at the end of the project.

For the micro controller I decided to go for a PIC microcontroller as opposed to an Arduino. I love the Arduino microcontroller system and I think it's the perfect platform for prototyping and experimenting. However using a PIC microcontroller, programmed in the C language, demonstrates more closely what's involved professional embedded system design. Using the PIC with the MPLAB IDE helps you understand all of the areas of embedded design that are hidden from you when using the Arduino system.

This project is aimed at those who have already experimented with basic electronics and Arduino and are now ready to take there electronics design to a more advanced level.

Supplies

555 BOM.jpg
MCU BOM.jpg

Here's a list off all the components and tools I used to build each of the speed controllers.


555 Based Motor Speed Controller PCB

1 × NE555 IC

1 × LM358 Operational Amplifier

4 × 1N4001 Diode

1 × IRFZ44 Mosfet

1 x 2n3906 PNP Transistor

4 × 1K Resistor 0.25W

1 × 2.2K Resistor 0.25W

1 × 4.7K Resistor 0.25W

1 × 10K Resistor 0.25W

1 × 100nF Capacitor

2 × 5K Resistor 0.25W

1 × LM7805 Voltage Regulator

1 × 0.33uF Electrolytic Capacitor

1 × 0.1uF Electrolytic Capacitor

1 × 3 pin Male HeaderPins

2 × 2-way Screw Terminal 5.08mm pitch

1 × 3-Way Screw Terminal 5.08 pitch

1 × PCB


Micro Controller Based Motor Speed Controller PCB

1 x PIC12F1840 Micro Controller

3 × 1K Resistor 0.25W

2 × 5K Resistor 0.25W

1 × 10K Resistor 0.25W

1 × LM7805 Voltage Regulator

1 × 0.33uF Electrolytic Capacitor

1 × 0.1uF Electrolytic Capacitor

1 × 3 pin Male HeaderPins

2 × 2-way Screw Terminal 5.08mm pitch

1 × 3-Way Screw Terminal 5.08 pitch

1 × 100nF Capacitor

1 x 2n3904 NPN Transistor


For Both

1 x DC Motor

1x 9V battery and holder


Tools, Consumables and Equipment

Soldering Iron

Solder

Solder Flux

Soldering Fume Extractor

Tweezers

Electronics Cutters

Magnifying Glass

Laptop

Microchip MPLAB X IDE (I used version 6.15)

Signal Generator

Microcontroller PCB Design

pic_pcb.png
pic_sch.png
MCU.jpg

To keep the comparison between the microcontroller and 555 based DC motor speed controller fair I decided to use a microcontroller with the same footprint as the 555 timer, an 8-pin dual inline package (DIP).

The Microcontroller PCB is a very simple design especially as I used the internal clock on the PIC12F1840 Microcontroller keeping the component count as low as possible.

The key components of this PCB are:

  1. The PCB is powered by a 9V battery. 9V goes directly to the positive motor motor terminal to power the motor. The 9V gets reduced to 5V to via an LM7805 linear regulator to power the micro controller.
  2. The 0-5V Signal voltage goes directly to pin 7 of the microcontroller (RA0) however if you can use a 0-10V signal that gets reduced in half by a voltage divider (R1 and R2). There's three header pins of which you connect the middle pin to with a jumper connector depending on which signal voltage you want to use.
  3. The Output of the micro controller is used to drive a NPN transistor which in-turn is used to drive a MOSFET which controls the power supplied to the motor.

Microcontroller Code

MPLAB X.png
PIC12F1840 DC Motor Speed Controller - programming with PIC KIT 3

I wrote the code for the PIC12F1840 Microcontroller in C using the Microchip MPLAB X IDE. The MPLAB X IDE is similar to the Arduino IDE except that it requires more input from the programmer, for example you need to set the configuration bits using MPLAB whereas this is done for you in Arduino. Here's a detailed explanation of how the code works.

Configuration bits setup - This section contains configuration bit settings that define the operational characteristics of the microcontroller. These bits configure hardware features like clock selection, power-up behaviour, watchdog timer, etc. This is one of the key areas of microcontroller programming which is done for you when using an Arduino.

// CONFIG1
#pragma config FOSC = INTOSC // Internal oscillator selected (no external oscillator).
#pragma config WDTE = OFF // Watchdog Timer is disabled to prevent unintended resets.
#pragma config PWRTE = OFF // Power-up Timer is disabled (short startup time).
#pragma config MCLRE = OFF // MCLR pin functions as a digital input, not a reset pin.
#pragma config CP = OFF // Code protection is disabled (allows reading of program memory).
#pragma config BOREN = OFF // Brown-out Reset is disabled, so the device won't reset on low voltage.
#pragma config CLKOUTEN = OFF // CLKOUT function is disabled (the pin can be used as I/O).
#pragma config IESO = OFF // No switching between internal and external oscillators.
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor is disabled.
// CONFIG2
#pragma config WRT = OFF // Flash memory is not write-protected.
#pragma config PLLEN = OFF // 4x PLL (Phase Locked Loop) is disabled (not needed for 16 MHz).
#pragma config STVREN = OFF // Stack Overflow/Underflow will not cause a reset.
#pragma config BORV = LO // Low trip-point for Brown-out Reset Voltage.
#pragma config LVP = OFF // Low-Voltage Programming is disabled (high voltage is required for programming).


Adding Header Files - This line includes the XC8 compiler's header file, providing definitions and functions to work with the PIC microcontroller's registers and hardware features.

#include <xc.h>


Setting Clock Frequency and ADC Sample Definition - _XTAL_FREQ is used for delay calculations in functions like __delay_ms(). ADC_SAMPLES is used to define how many ADC samples to average when reading the ADC (for stability).

#define _XTAL_FREQ 16000000 // Define clock frequency for the internal oscillator at 16 MHz.
#define ADC_SAMPLES 16 // Define the number of samples to take when averaging the ADC readings.


Setting up Function Prototypes - These are function prototypes, declaring the functions to be used later in the code. This helps to organize the code and allow functions to be called before their actual definitions.

void initADC(void);
void initPWM(void);
unsigned int readADC(void);
void setPWMDutyCycle(unsigned int duty);


The main Function.

The main() function initializes the ADC and PWM modules.

The while (1) inside the main() function (the "1" value means true) loop continuously reads the ADC value from analog input AN0, then sets the PWM duty cycle based on this value, generating a corresponding PWM output signal. There's also a short delay to stabilize the ADC readings.

void main(void) {
initADC(); // Initialize the ADC for analog input.
initPWM(); // Initialize the PWM for generating the pulse width signal.
OSCCON = 0x78; // Set the internal oscillator to 16 MHz.
while (1) {
unsigned int adcValue = readADC(); // Read ADC value from AN0.
setPWMDutyCycle(adcValue); // Adjust PWM duty cycle based on ADC value.
__delay_ms(10); // Small delay to stabilize the readings.
}
}


The ADC Initialisation.

This function configures the Analog-to-Digital Converter (ADC):

  1. Enables the ADC, selects channel AN0 (RA0).
  2. Configures the clock source for conversion and right-justifies the result.
  3. Sets RA0 as an analog input.

It can be seen in this section of the code that you are working directly with registers, another key area of microcontroller programming that is usually hidden from you with Arduino programming.

void initADC(void) {
ADCON0 = 0x01; // Enable ADC and select AN0 (RA0) as input channel.
ADCON1 = 0x80; // Set conversion clock (Fosc/8), result is right-justified.
ANSELAbits.ANSA0 = 1; // Set RA0 as an analog input (AN0).
TRISAbits.TRISA0 = 1; // Configure RA0 as input.
}


PWM Initialisation.

This function initializes the Pulse-Width Modulation (PWM):

  1. Configures RA2 as an output.
  2. Sets up Timer2, which drives the PWM module, and configures the CCP1 module for PWM mode with a 16x pre-scaler.
  3. Initializes the duty cycle to 0.
void initPWM(void) {
TRISAbits.TRISA2 = 0; // Set RA2 as output (used for CCP1 pin for PWM).
PR2 = 0xFF; // Set Timer2 period register to 0xFF, determining the PWM frequency.
CCPR1L = 0x00; // Initialize duty cycle to 0.
CCP1CON = 0x0C; // Set CCP1 module to PWM mode.
T2CON = 0x06; // Enable Timer2 with a 1:16 prescaler.
}


Reading the ADC (Analog to Digital Convertor) Value.

This function reads the ADC value from AN0:

  1. Starts the conversion and waits until it completes.
  2. The result is combined from the two 8-bit ADC result registers (ADRESH and ADRESL) into a 10-bit result.
  3. The returned value is inverted (1023 - result), which means higher input voltage will give a lower digital result.
unsigned int readADC(void) {
__delay_us(5); // Wait for the acquisition time.
ADCON0bits.GO_nDONE = 1; // Start the ADC conversion.
while (ADCON0bits.GO_nDONE); // Wait until conversion is complete.
return (unsigned int)((1023-((ADRESH << 8) + ADRESL))); // Return the 10-bit ADC result.
}


Reading the ADC (Analog to Digital Convertor) Value - Alternative with Averaging

This is an alternative function for reading the ADC, which averages 16 samples to get a smoother, more stable value. It’s currently commented out but can be swapped in for the single-read readADC() function if more precision is needed.

// Function to read the ADC value from AN0 and average it over 16 values for smoothing
//unsigned int readADC(void) { //
//unsigned long sum = 0;
//for (int i = 0; i < ADC_SAMPLES; i++) {
//__delay_us(5); // Acquisition time
//ADCON0bits.GO_nDONE = 1; // Start conversion
//while (ADCON0bits.GO_nDONE); // Wait for conversion to complete
//sum += ((ADRESH << 8) + ADRESL); // Accumulate the ADC result
//}
//return (unsigned int)(1023-(sum / ADC_SAMPLES)); // Return the averaged result and reverse it
//}


Setting the PWM Value

This final function sets the duty cycle of the PWM based on the ADC reading:

  1. The 10-bit ADC result is scaled down to fit into the 8-bit CCPR1L register.
  2. The remaining 2 bits are set in the DC1B bits of CCP1CON to achieve finer control of the duty cycle.


void setPWMDutyCycle(unsigned int duty) {
unsigned int pwmValue = (duty >> 2) & 0xFF; // Scale down the 10-bit ADC result to 8 bits.
CCPR1L = pwmValue; // Set the 8 MSBs of the PWM duty cycle.
CCP1CONbits.DC1B = duty & 0x03; // Set the 2 LSBs of the PWM duty cycle.
}


Once the code was written I uploaded the code the MCU (Microcontroller) using a PIC Kit 3 programming tool. I've included a video showing how this is done.

I've attached a copy of the code if you want to use it yourself.

555 PCB Design

555_sch.png
555_pcb.png
Simulation of 555 Analog in (0-5V) PWM DC Motor Speed Controller
555.jpg

This circuit can really be thought of as 2 stages. Stage 1 is a linear ramp generator (sawtooth wave) and Stage 2 is a comparator stage which gives the PWM output to the DC motor.

Stage 1 - Linear Ramp Generator (Sawtooth Wave)

In this stage the 555 circuit is based on a sawtooth wave generator. The sawtooth wave is generated by charging up a capacitor through a PNP transistor which results in linear charging of the capacitor (a constant current source by passing through a transistor). By changing the values of the resistors and capacitors you can change the frequency of the sawtooth wave output.

As the capacitor charges through the PNP transistor it reaches 2/3 of the 555 supply voltage. When it reaches 2/3 supply voltage it activates the threshold voltage of the 555 which causes the capacitor to discharge through the internal transistor of the 555. 

The control pin of the 555 is connected to the output of the 555 so that the bottom of the sawtooth wave goes down closer to zero volts, about 0.2V.


Stage 2 - PWM Output Stage

Once the sawtooth wave has been generated the sawtooth wave is fed into an operational amplifier being used an a comparator. The LM358 operational amplifier works as a comparator by comparing two input voltages. The sawtooth wave is fed into the inverting input (−) and the 0-5V signal is fed into the non-inverting input (+). The 0-5V is reduced down to 0- 3.3V through a voltage divider before it enters the Op Amp. If the LM358 non-inverting input (+) is higher than the inverting input (−), the output goes high. If the inverting input (−) is higher, the output goes low, the output is a square wave. The signal voltage controls how long the output of the Op Amp is high. As the voltage of the sawtooth wave ramps up it increases until it is greater than the signal voltage at this point it turns the output off. At this point the width of the LM358 output is inversely proportional to the signal voltage.

The output of the Op Amp drives the IRFZ44 MOSFET. The motor positive terminal is always connected to the positive voltage (9V on my design) and when the MOSFET is turned on it connects the other motor terminal to ground therefore allowing current to flow through the motor coil.

Please take a look of the video to see a simulation of the circuit.

Testing

555 Analog in (0-5V) PWM DC Motor Speed Controller
PIC12 Analog in (0-5V) PWM DC Motor Speed Controller

To test both speed controllers I used my Signal Generator. The signal generator I use is designed for setting up or testing industrial controls and can be set to provide 0-5V, 0-10V, 0-20mA and 4-20mA which are standard control signals used in industrial controls. I used the 0-5V setting for testing both circuits.

I used a simple hobby gearmotor for testing purposes however the circuits would be both cable of handling much larger motors because of the size of the MOSFET I utilised.

Both circuits worked fine as expected, please take a look at the two videos to see the Speed Controllers in action.

Conclusion

If I were to compare the two PCB's for DC motor speed control I would say that the microcontroller version is far simpler from a hardware point-of view and the 555 version is obviously far simpler in respect to software. That said I did go about the software the hard way and if I'd used the Arduino IDE to write the software it would have been almost trivially easy.

If you were to do this project with an Arduino compatible 8-pin DIP chip such as an ATiny85 and used the arduino platform the project would be very simple in resect to hardware and software. But you wouldn't learn as much!

The 555 PCB has the disadvantage of being a bigger footprint which then has the knock-on effect of requiring a bigger enclosure etc.. But I do like being able to simply adjust the frequency on the fly.

If I were to develop a commercial speed controller I would most likely go down the microcontroller route as the small PCB footprint and the option to add additional peripheral devices such as indicator LEDs etc..

When it comes down to it the most important thing is to use the most appropriate technology for your particular project this might come down to your individual skills, resource availability (such as tools and materials) or simply personal preference.

A big thank you to my daughter for the artwork showing the two IC's having a fight!