Alternate eMotor Speed Calcuation Scheme

This article is a supplement to my previous eMotor Phones Home, which discussed PID control of the eMotor angular velocity. There the QEI module of dsPIC was initialized to reset POSCNT by the index pulse. Then the angles were sampled and velocity was calculated from the difference between new angle value and previous one. Some drawbacks of such approach are

  1. Angle read timing must somehow be synchronized with the rotation speed; i.e., overflow should not happen between readouts. This imposes constraints on the interrupt period of the timer used to capture angles (we have used timer 2). This works reliably when the timing is calibrated to some fixed angular velocity, but becomes problematic when the velocity varies significantly.
  2. The scheme does not suit applications that require encoder count accumulation, such as CNC linear axes or robot joints with gear ratio, for example.

So this article proposes a velocity calculation scheme when the encoder counts are accumulated. The computation is actually very easy, assuming that we could get an efficient and reliable position* accumulation. Below we describe a technique for such purpose.

* When motor angles are accumulated, it makes sense to refer to the counts as position. That covers linear unit as well when the movement is tranformed via a ball screw, say.

Constructing a 32-bit position counter

The hardware counter in QEI module of dsPIC is 16-bit. That means it can handle counts between 32767 and -32768. So overflow could occur within a short time, depending on the number of encoder lines (often x4 for best resolution) and motor velocity. In certain applications with more travel range, at least 32-bit counter is required to accumulate the position. The most economical approach is to extend the range in software.

A naive method is to declare a global variable of type long. Whenever overflow (underflow) occurs, we increase (decrease) its value by 1. So when the overall position is needed, shift-left this variable 16 bits and add it with the value from POSCNT. This method works, though it is not so efficient. A better way is declaring a union structure

typedef union 
    int half[2];
    long full;
} enc_cnt;

to keep the encoder counts. Using union is a convenient way to superimpose two types of variable at the same address. So when we instantiate a variable of type enc_cnt

enc_cnt EncCnt;

the 32-bit , lower 16-bit, and upper 16-bit values can be retrieved from EncCnt.full, EncCnt.half[0], and EncCnt.half[1], respectively. Now, whenever overflow/underflow occurs, we simply update the value of EncCnt.half[1], while the count value of POSCNT is copied to EncCnt.half[0]. This takes care of everything. No need to shift a variable and add to another one. The overall encoder count lies ready in EncCnt.full at anytime*.

* as always, beware of the read-modify-write issue. The priority of TSR that writes to enc_cnt must be higher than the one that reads it.



Comments are closed.