BLDC six-step control speed scaling using S12ZVM

Document created by Matej Pacha Employee on Jan 30, 2018Last modified by Matej Pacha Employee on Jan 31, 2018
Version 2Show Document
  • View in full screen mode


The paper describes basic scaling procedure of a six-step BLDC motor control application with focus on S12ZVM MCU devices. 



Based on the various questions regarding the BLDC speed calculation, I've decided to write this document to make the scale calculation clear. Please use it for your reference and comment in case there is something to be explained in more details.

BLDC six-step control

This document is linked with the six-step control of BLDC motors, known for the trapezoidal back-EMF shaped voltage. The key is to create a torque using DC current in two phases while the third phase is not connected. As the rotor is moving, the phases are switched (commutated) to keep the stator flux ahead the rotor flux. The principles are well described in many application notes, such as AN4718 or AN4704. The instant of the commutations can be driven by Hall sensors or by the back-EMF signal monitoring (so-called "sensorless"). For field-oriented control, please refer to the PMSM control discussed in AN5135 or AN5327.

Speed scaling

It's more benefitial to describe in details the sensorless algorithm over the hall-sensor-based control. However, the approach is almost the same.


The motor speed is calculated based on the zero-cross detection of BEMF voltage on a non-active phase. These zero-cross events times are measured by capturing a timer (TIM0CNT) register at the time the ADC routine detects a zero-cross (or when the Hall sensor commutation event occurs). That means, the time is scaled by the timer TIM0 settings. There is no general guidance how to set up the timer. However, the timer should be set to cover some reasonable number of ticks between two commutations (which is linked to the speed precission at high speeds) and should be able to cover two commutations "far away" from each other at very low speeds, without the timer overflow.

A good practice would be:

  • at least 100 timer ticks between two commutations
  • maximum 16bit = 65535 ticks between two commutations

The same aproach can be followed using the Hall-sensor events.

Commutation periods

In the application, zero-cross (or hall-sensor) periods are captured with periodZC_F_PhA, periodZC_R_PhA, etc. or periodZC[6], defined as tU16 (16 bit unsigned). The periods are captured using a timer, as mentioned above.

In electric motor theory, we recognize "electrical" and "mechanical" speed. The "electrical" speed is linked with the rotational field. The mechanical speed is connected directly with the rotor speed. The relation between these two speeds is determined by number of poles or pole-pairs:

(1)   mechanical speed = electrical speed / number of pole-pairs = electrical speed / ( number of poles / 2 )

In the following text, 3-phase BLDC motor is discussed.

Speed calculation

To calculate one "electrical" revolution of a motor = 6 commutations (or 6 zero-crosses), all the 6 commutation time periods shall be summed. For that case, the resulting "period6ZC" is defined, formated as tU32 (or unsigned long) to prevent an overflow if all six 16-bit zero-cross periods are summed.

If you sum all the 6 zero-cross periods, you'll get the number of timer ticks per single "electric" revolution. That means, if the motor is 2-pole motor ( = 1 pole-pair motor), it would be the time per single "mechanical" revolution of the rotor. In thsi case:

(2)   mechanical speed = electrical speed;

You can easily get the time per mechanical revolution for higher-pole motor simply by multipling it by number of pole-pairs.

(3)   time per mechanical revolution = period6ZC * number of pole-pairs 

The period per 6 zero-crosses "period6ZC" is calculated within the control loop, usually in the 1 ms timer interrupt routine. Since the speed (rotor frequency) is just an inverted time period, we can use following code line to calculate the actualSpeed:

(4) actualSpeed = SPEED_CALC_NUMERATOR / period6ZC;

Now, how to read it's scale and what is the SPEED_CALC_NUMERATOR? Let's assume the "actualSpeed" is tFrac32 (1.31 formated signed 32bit number). It would mean the maximal fraction number is 1.0, which is represented by its integer value 2^31 = 2,147,483,648. Why to use 32-bit number over 16-bit is obvious - since the period6ZC is 32-bit value, the result should be of the same resolution or width.

The task is to find the right SPEED_CALC_NUMERATOR. Let's consider the S12ZVM device and the timer TIM0 used to capture the zero-cross events times. The prescaler is set to 16 (TIM0TSCR2_PR = 4) and the bus clock is 12.5MHz, the timer tick is 12.5MHz / 16 = 781.25 kHz, in time scale it is 1.28us.


Let's assume the maximum mechanical speed of the motor is 10,000 RPM and the motor has 6-pole-pairs. That would give us

(5)   (10,000 RPM) / (60 seconds) = 166.67 revolutions per second,

(6)   166.67*(6 pole-pairs) = 1000 electrical revolutions per second, so

(7)   1000 * (6 commutations) = 6000 commutations/zero-crosses per second.

(8)   That would make 1/6000 = 166.67us per one commutation.

(9)   Ticks per one period at max speed = 166.67 us / 1.28us = 130.28

With our 1.28us timer, we can catch upto 130 periods at maximal speed.


For maximal speed of the motor 10,000 RPM, we can simply rearrange the "actualSpeed" calulation shown in (4) into (13) and (14), assuming that:

(10)   actualSpeed = FRAC32(1.0) = 2,147,483,648

(11)   period6ZC = 6 periods * 130 = 780 (rounded down, since the value is still an integer)

In case of S12ZVM device, it is more convenient to use 16-bit (tFrac16) calculations than 32-bit. Hence, we can do a trick with the scaling to

  • use 32-bit actualSpeed to prevent an overflow
  • use the same 32-bit number as 16-bit number just by scaling it correctly
  • benefit from the 16bit calculation, which is good enough and fast enough at the same time

The trick is hidden in the casting of the actualSpeed from Frac32 to Frac16:

(12)   speedErr = requiredSpeed - (tFrac16) actualSpeed;

That means, only the lower 15 bits are considered the speed (assuming the actualSpeed will never exceed 32767 so the 16th "sign" bit will always be zero). For maximum speed and the SPEED_CALC_NUMERATOR determination, the "actualSpeed" should be 32767 (which is the range of signed tFrac16). Equation (4) can be used to calculate the SPEED_CALC_NUMERATOR as follows:

(13)   32767 = SPEED_CALC_NUMERATOR / 780

(14)   SPEED_CALC_NUMERATOR  = 780 * 32,767 = 25,558,260


#define SPEED_CALC_NUMERATOR 25558260

Using the equation (4), the speed for the minimum period6ZC would be:

(15)   actualSpeed = SPEED_CALC_NUMERATOR / period6ZC = 25,558,260 / 780 = 32,767;

Masking that value by lower 15 bits and casting it to tFrac16 would give us 1.0. Since the speed scale has been determined by the maximum speed of the motor 10,000 RPM, we can calculate the real speed simply by:

(16)   RealScaleSpeedRPM = (tFrac16)actualSpeed / 32,767 * MAX_SPEED = (tFrac16)actualSpeed / 32,767 * 10,000.

In the application note AN4704, there are several macro constants used to control the application. These constants should be calculated using following definitions, with a little help of the SPEED_SCALE - floating point representation of the real scale of the speed.

#define SPEED_SCALE 10000.0  // Used for correct calculation of the following:
#define REQUIRED_RUN_SPEED FRAC16(2000.0/SPEED_SCALE) // 2000 rpm
#define MIN_SPEED FRAC16(500.0/SPEED_SCALE) // 500 rpm minimal speed for Down button control (should be min 10% of nominal motor speed)
#define MAX_SPEED FRAC16(5000.0/SPEED_SCALE) // 5 krpm maximum speed for Up button control
#define SPEED_STEP FRAC16(100.0/SPEED_SCALE) // 100 rpm, Up/Down step for button controls


If the application uses FreeMASTER to display or control, the scales shall be updated in the FreeMASTER project as well. The new FM scales for AN4704 are:

Variable nameVariable watch settings
requiredSpeedReal type transformation: Linear: a = SPEED_SCALE = 10,000; b = 0

Signed int, Size = 4

Show as: REAL

Bit fields maks with: word (0xffff)

Real type transformation: Linear: a = SPEED_SCALE / 32767.0 = 0.3051851; b = 0 

Calculation accuracy

Now, let's have a look on the speed resolution (or accuracy):

At the full speed, if one period changes by 1 tick, then you'll get the period6ZC from 780 to 781 at max speed, which makes 12.8041 RPM difference. The higher the SPEED_SCALE is, the higher the error is. If the assumption would be a change in all of the 6 periods (thus the period6ZC would change from 780 to 786), the resulting change to the speed would be 76.336 RPM. That makes the speed error 10,000 RPM +- 76, which is 0.76%.

In terms of accuracy, it makes sense to set the maximum speed (or the SPEED_SCALE) to the maximal speed of the application instead of copying some high number from the datasheet of the motor. Well, the max speed of the application should not exceed the max speed of the motor, but it doesn't help if the speed scale is unnecessarily high.

Real applications

In real applications, the calculated speed (actualSpeed) reflects all the deviations and changes of the period6ZC. Therefore, it is a good practice to do some kind of filtering of the period6ZC, either by averaging or using a low-pass filter. In case of one or more zero-crosses are lost, there should a mechanism to process and detect if the motor is stalled (mechanical speed loses lock with the commutation) or if it is just a disturbance to the signal. The stall detection or the Hall-signal fault/damage detection is one of the challenges towards high quality application.


S12ZVM MCU - NXP main S12ZVM page

AutoMCDevKits - NXP Automotive Motor Control Development Solutions

AN4718 - 3-Phase BLDC Hall Sensor Application Using S12ZVM

AN4704 - 3-phase Sensorless BLDC Motor Control Kit with the S12 MagniV MC9S12ZVM

AN5201 - Integrating the LIN driver with BLDC sensorless motor controller In the S12ZVM128 device

AN5330 - Migration Guide for S12ZVM Devices



1 person found this helpful