BLDC motor control using Atmega328

As part of my 3D printer project, one of the big electronics hurdles to overcome was a motor controller for a BLDC (BrushLess Direct Current) motor. Searching for a cheap, off the shelf controllers that would interface easily with a microcontroller turned up fruitless, so I took the opportunity to design my own circuit. It was a major project in itself, and while there’s still a few hardware tweaks to make I’m quite happy with how it has turned out.

Before starting any project it always goes smoother when you take the time to clearly define the features/requirements. After a lot of thought and conversations with others, I came up with the following list of features:

  • A single input for motor direction
  • A single input for PWM (motor speed/torque)
  • Commutate the motor windings based on hall sensor feedback
  • Keep track of position through hall sensor feedback and communicate position to external microcontroller

BLDC motor control using Atmega328

  • Inputs for optional quadrature encoder
  • A “sample and hold” input to save the position at an instant in time into a buffer (for synchronizing multiple motors/controllers)
  • Some kind of communication protocol to communicate with external microcontroller
  • Works with 3.3v or 5v control circuitry and 8-36v motor power
  • As fast as possible using pin change interrupts and hardware communication

At the heart of this control board is an atmega328p microcontroller running at 16MHz, the same as used on the Arduino Uno, and programming is accomplished via an external ISP programmer. I played around with the idea of using a smaller/cheaper microcontroller, but after including all the features above I ended up using all but 3 of the available pins on the 328. Plus, going with the standard Arduino chip has the benefit of familiarity for me and many others.

Direction and PWM Inputs

A necessary part of any DC motor is that the current direction through the windings alternates as the motor rotates (commutation). The way a typical (brushed) DC motor handles this requirement is via brushes riding on a split ring on the shaft of the motor.

This design makes controlling the motor very easy. Simply apply voltage to the two wires and the motor spins. If you reverse the two wires the motor direction reverses. Then for speed control you add PWM to the motor power.
A BLDC motor has no brushes (by definition) and no split ring to do the commutating. Instead, the commutating is controlled by some kind of logic circuit. This circuit must know the position of the magnets relative to the windings in order to commutate correctly. Also, the majority of BLDC motors have 3 wires coming out of them (3 phase = 3 sets of windings in the motor). Voltage is applied across only 2 of the wires at any given time. Which 2 wires and the polarity of the voltage across them is always changing as the motor rotates. It is the job of the control circuit to do this correctly.

In the image above you can see that there are a total of 6 switches (3 phases * 2 polarities), and for this instant in time voltage is applied across coil A (positive) and coil B (negative). Replace the switches with MOSFETs and you have a basic driver circuit. To get variable speed control you would switch the MOSFETs on and off with a PWM signal at the gate. A total of 3 PWM signals are needed (only one for each coil), the other 3 MOSFETs are plain on or off

For my control board I wanted to have a single PWM input from an external microcontroller control all 3 coils. I accomplished this with some AND gates on board. The control circuit would set the 6 pins for the 6 MOSFETs, 3 of them are routed into one side of the AND gates (separate AND for each of the 3) and the PWM into the other side of the AND. The outputs from the AND gates are PWM modulated versions of what the control circuit is providing, and these signals are then routed to the MOSFETs.
The direction of the motor is handled by an input pin from an external microcontroller (or switch) to the control circuit. The on-board software then takes care of changing the control signals to reverse the motor.
The choice to have the PWM and direction be inputs from an external source was to keep the control board as close as possible to the way you would drive a typical DC motor. Of course, with a change in software these things could be handled from the control board itself.

Hall Sensor Feedback for Commutation and Position

As far as I know there are 3 ways to commutate a BLDC motor: manually at a given rate, sensing the back EMF in the unpowerd coil to deduce rotor position, and sensing the rotor position with hall effect sensors.

Manually commutating at a given speed is basically the same idea as controlling a stepper motor, but is usually frowned upon in BLDC motors. One of the reasons is that it wastes a lot of energy because the winding resistance of a BLDC motor is a lot less than a stepper motor, so a lot more current flows, generating excess head in the motor and the MOSFETs.

Sensing back EMF is usually what ESC controllers do. They are more complicated with the added sensing hardware, but allow the motors to be cheaper since they don’t have to come with sensors built in. They also have the advantage of being able to adjust the motors timing on the fly. This kind of control is good for controlling the speed of the motor at high spin speeds, but is not a good solution for low speed/high torque applications. This control board does not have the necessary hardware built in to run in this mode.

Hall sensors are generally more expensive and the motor timing is hard set, but they give good low speed performance. This is the intended operating mode for my control board. There are many good articles available on the internet that explain how to commutate the motor once you have feedback from the sensors. I’ll leave the explanation to those other sources and present here only this handy diagram that shows the polarity of each of the 3 motor phases with respect to the hall sensor feedback (referred to as switches in the image).

Turns out that finding motors with integrated sensors is hard to do unless you’re looking for industrial sized motors. There are a few intended for RC cars, but they have a low pole count and are not suitable for precise positioning. In lieu of finding good sensored motors, I decided to add sensors to my own motor. Turns out adding sensors to an outrunner BLDC is pretty straight forward (see my blog post here for more info). Plug these sensors into the control board, make sure you have the 3 motor wires plugged in in the proper order (trial and error is a good approach), and set the timing appropriately and you have all the makings of a sensored BLDC driver.

One thing I’ve found particularly exciting is that I’ve come up with a way to use the hall sensor information to simultaneously keep track of the position of the motor. It’s only as precise to 1/(number of magnets * 3) revolutions, but if you have a high enough pole count on the motor you can get away with using the hall sensors in place of an external encoder. That is a significant cost saver on position feedback. In case a higher resolution is needed, there are two additional pins broken out for the A and B channels of a quadrature encoder, and the software can be updated to monitor the position of the encoder rather than the hall sensors.

As for how the position feedback from the hall sensors work, it is very similar to the method I presented in my quadrature post. If you haven’t done so already, I suggest you go and read it since I’ll only talk about the differences here rather than going through all the details again.

The operating principle of reading quadrature was to use the previous and current states of the two channels combined together to become the index for a lookup table. The value in the lookup table would then be added to the position to either increment it, decrement it, or do nothing. In the quadrature case the index value is a 4-bit integer (2 bits for the previous state of channels A and B, 2 bits for the current state), so the lookup table is 2^4 = 16 elements long. Using the same thought process, it’s possible to build a lookup table using the previous and current states of the 3 hall sensors. Since there are 3 sensors, the index value becomes a 6-bit integer and the lookup table becomes 2^6 = 64 elements long. It’s a lot more work to fill out a 64 element table as opposed to a 16 element table, but it’s a one time deal and the performance is just as fast as the 4-bit quadrature version.

The end result of all this trickery is a free encoder (if you count the cost of the hall sensors as required even without the position feedback). Although it is admittedly coarse in comparison to most quadrature encoders, when a high pole count motor is coupled with a geared down drive (such as a lead screw) the resolution becomes good enough for fine position control. For example, a 22 pole motor gives 66 counts per revolution, and when combined with a 16 threads per inch lead screw the positioning accuracy comes in at (1/16) / 66 = 0.00095 inches per count.

See this post for details on the hall effect sensor PCB.

Sample and Hold

It’s not uncommon practice in data acquisition hardware for there to be a feature called “sample and hold.” What this feature does is take a signal from the host microcontroller/computer/whatever which then causes the hardware to freeze the values on all the analog inputs. The reason this is important is because it takes a finite amount of time to read the analog signal, especially if there are multiple pins to read. During this read time it’s quite likely that the signals are changing, and by the time the microcontroller gets around to reading the last input some amount of time has passed since reading the first input and the value has changed from what it was when you first started sampling.
Imagine you had analog signals coming in to each of the 6 analog inputs on an Arduino. These incoming signals are changing fast, perhaps reading accelerometers on a vibrating object. You want to sample all 6 sensors at the same instant in time. Without sample and hold, you would read the first input at t=0. Say it takes y amount of time to complete the analog read. Now the Arduino moves on to read the second input at t=y, third input at t=2y… sixth input at t=5y. The sixth sensor has not in fact been measured at the same time as the first sensor, but rather 5y amount of time later. Usually y is not a lot of time (on the microsecond scale), but depending on your application and what else is happening with the sample (perhaps lots of math) it may be important. Sample and hold will eliminate this problem by sampling all sensors instantaneously and holding the value while the measurements are being taken.
In my 3D printer I plan to have multiple motors, each with their own control board. The control boards are keeping track of the position of their respective motors and sending that position back to the master controller when requested. In order to keep the closed loop control of these three motors as accurate as possible, I decided to implement a sample and hold feature on the control boards. It works like this: when the master controller is ready to read the position of all the motors, it sets the sample and hold pin high. This one pin is routed to all of the control boards. Once the control boards see that sample and hold has gone high (on a pin change interrupt), they take their current position and save it into a buffer. Then, when communication for the position begins it is read out of the buffer (rather than the actual current position, which may have changed from the time that sample and hold was set). Once the position has been read from all the control boards, the master sets the sample and hold pin low and the control boards are ready to do the whole thing over again.

Communication to External Microcontroller

Everything I’ve explained to this point is all taken care of by the on-board software and the only hookups required to operate a BLDC motor are power connections (logic power, motor power and ground), motor connections (coils ABC), hall effect sensors, and direction and PWM inputs. For the minimalist user it is very easy to get a motor spinning.

But since the hall effect sensors can keep track of motor position there needs to be a way to get that information from the control board to an external microcontroller. I opted to go with SPI communication for two main reasons: the SPI pins already need to be broken out to a header for programming the on-board microcontroller (via an ISP programmer), and SPI is FAST (up to 4MHz clock rate on Arduino, 10x faster than I2C).

To avoid redundant headers on the board, I chose to go with the standard 6-pin ISP programing header (2×3) used on Arduino and expand it with 4 additional pins to a 10-pin header (2×5) for slave select, direction, PWM, and sample and hold. I particularly like 2 x whatever headers because connection cables are easy to make with ribbon cable and clamp on insulation displacement connectors like this.

The control board is running as a slave on the SPI bus. When the board detects that the sample and hold pin has been set high it takes a snapshot of the position (32 bit integer) and breaks it into 4 byte sized chunks. The master then requests each chunk individually and the slave responds with the appropriate byte. It’s then up to the master to re-assemble the 4 bytes into a single 32 bit integer. So far this is the only SPI communication available on the control board, but in the future I plan to be able to have the master write contents directly to the control boards EEPROM which will be read on start-up to configure things such as the order the motors 3 wires are plugged in.

The Arduino SPI library only supports SPI in master mode. I was a little bit bummed by this, but not too bad since I’ve been digging more into the bare atmega instructions anyway. Through a lot of datasheet surfing, head banging against the computer screen, and trial and error I eventually got hardware SPI working in slave mode. As far as the amount of code it’s not all that bad. Straight forward, really, once you get your head wrapped around it. But getting to that point… not the easiest task ever.

Operating Voltage Levels

As stated at the top of this post, the end goal is to be able to run the control circuit on 3.3 or 5 volts and drive a motor with 8 – 36 volt supply. My current version of hardware falls short of these specs. Currently I am limited to 5v only on the control side and 5-12v for the motor. I have identified the problem in the schematic and updated the design so that it should work to spec, but I have not yet ordered the new PCB’s to verify the design.
For my 3D printer, which was the motivation behind this control board, I plan to use an Arduino Due as the external controller because of it’s impressive speed. The Due only operates at 3.3v, so it is imperative that I get 3.3v working on the control board.
That’s it for the hardware description of this board. I’m really pleased with the progress I’ve made here. I think I’ve come up with a design that offers both simplicity for interfacing with external controllers and a good selection of features for a high performance controller. In an upcoming post I will describe the software that I am running on the controller. Until then, check out this video of the thing in action.

As with all my circuit boards, I plan on selling this one to anybody interested. However, at this time the board has the limitations detailed in the Operating Voltage Levels section. If you want to get your hands on one of this version, or if you’re dying to get the updated version, leave a comment or email me. Depending on the response I get from the public I may choose to accelerate my plans.

UPDATE: V2 has been tested and is running after a few modifications. A V3 design has captured these latest modifications and is on order (hopefully the last version). If V3 works as expected I will post the design files and start selling it in the Makeatronics Store. In the meantime here’s a video of V2 in action. I will post more videos shortly with position and velocity control.

For more detail: BLDC motor control using Atmega328

About The Author

Scroll to Top