QEI Block Icon
Quadrature Encoder Interface - Position and Velocity Measurement

Overview

The MCHP_QEI block provides an interface to the Quadrature Encoder Interface (QEI) peripheral available on dsPIC and PIC32 microcontrollers. This peripheral decodes quadrature encoder signals to measure position, direction, and velocity of rotating shafts, making it ideal for motor control and precision positioning applications. Key Features:

  • Quadrature decoding (X2 and X4 modes)
  • 16-bit or 32-bit position counter
  • Index pulse detection and position reset
  • Direction sensing
  • Programmable digital filtering
  • Velocity measurement (32-bit QEI only)
  • Multiple operating modes: encoder, pulse counter, up/down counter
  • Position modulo and reset options

Device Support

FamilyQEI TypeMax ModulesFeatures
dsPIC30F16-bit1-2Quadrature decoding, index reset
dsPIC33F16-bit1-2Quadrature decoding, index reset
dsPIC33E32-bit1-2Extended position counter, velocity measurement
dsPIC33C32-bit1-2Extended position counter, velocity measurement
dsPIC33A32-bit1-2Enhanced position/velocity, advanced filtering
PIC32MK32-bit1-2Extended features, modulo with index reset

Block Parameters

Operating Mode

ParameterOptionsDescription
QEI Modeβ€’ Quadrature Encoderβ€’ External Pulse Counter
X2/X4 Modeon/off (16-bit QEI only)X2: 2 counts/line, X4: 4 counts/line

Position Counter Configuration

ParameterOptionsDescription
Position Outputβ€’ is not an outputβ€’ 16-bit unsigned
Position Reset Modeβ€’ Never resetβ€’ Reset on INDEX
Modulo Limits[Low High] vectorPosition counter modulo boundaries
Initial ValueIntegerPosition initialization value

Index Pulse Configuration

ParameterOptionsDescription
Index Counter Outputβ€’ is not an outputβ€’ 16-bit unsigned/signed
Index Match ValueQEA/QEB state (00, 01, 10, 11)Encoder state when index is valid
Index PolarityActive high/lowIndex pulse polarity

Velocity Measurement (32-bit QEI)

ParameterOptionsDescription
Velocity Outputβ€’ is not an outputβ€’ 16-bit unsigned
Period Outputβ€’ is not an outputβ€’ 16-bit unsigned
Max PeriodTime (seconds)Maximum period for velocity calculation

Signal Conditioning

ParameterOptionsDescription
Digital Filteron/offEnable input signal filtering
Filter Cutoff FreqFrequency (Hz)Low-pass filter cutoff frequency
Swap QEA/QEBon/offReverse encoder direction
Invert PolarityQEA, QEB, INDEX, HOMEInvert input signal polarity

Pin Configuration (Remappable Devices)

PinFunctionDescription
QEA/QEIAPhase A InputEncoder channel A (or pulse input)
QEB/QEIBPhase B InputEncoder channel B (or direction input)
INDEX/QEINDXIndex InputIndex pulse for position reset
HOME/QEIHOMHome InputHome reference signal (optional)

Block Output Datatype

The block supports 4 output datatype representations for position:

Output TypeRangeDescription
16-bit unsigned0 to 65535Raw encoder counts (unsigned)
16-bit signed-32768 to 32767Raw encoder counts (signed, bidirectional)
32-bit unsigned0 to 4,294,967,295Extended position (32-bit QEI only)
32-bit signedΒ±2,147,483,647Extended signed position (32-bit QEI only)

Integer Outputs (4 options)

All outputs are raw encoder counts transferred directly from peripheral registersβ€”no runtime computation. Users handle conversion to physical units (degrees, radians, RPM).

Hardware Considerations: dsPIC30F/33F/33E/33C lack hardware FPUβ€”perform fixed-point arithmetic in time-critical loops. dsPIC33A has hardware FPU.

Position Conversion

Counts_Per_Rev = PPR Γ— 4;  % X4 mode (or PPR Γ— 2 for X2 mode)
Position_rad = QEI_Position Γ— (2 Γ— Ο€ / Counts_Per_Rev);
Position_deg = QEI_Position Γ— (360 / Counts_Per_Rev);

Example: 500 PPR encoder in X4 mode = 2000 counts/rev:

  • Position_deg = QEI_Position Γ— 0.18
  • Position_rad = QEI_Position Γ— 0.00314

Velocity Conversion

Velocity output (if enabled) is position difference per sample period:

Velocity_RPM = Velocity_counts Γ— (Sample_Rate Γ— 60 / Counts_Per_Rev);

Quadrature Encoding Principles

X4 Quadrature Decoding

In X4 mode, the QEI peripheral counts on both rising and falling edges of both channels, providing 4 counts per encoder line: QEA β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”___ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ QEB β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”___β”Œβ”€β”€β”€ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ____β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ └─── Count: 0 1 2 3 4 5 6 7 8 9 10 11 12 Direction: FORWARD (QEA leads QEB by 90Β°)

X2 Quadrature Decoding (16-bit QEI)

In X2 mode, only one edge per channel is counted, providing 2 counts per encoder line: QEA β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”___ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ Count: 0 1 2 3 4 5 6 (Rising edges of QEA when in X2 mode)

Index Pulse Operation

The index pulse provides absolute position reference. It can reset the position counter when specific conditions are met: QEA β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ QEB __β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”_β”Œβ”€β” INDEX ______β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ β”‚ Position: … β†’ 47 β†’ 48 β†’ 0 β†’ 1 β†’ 2 β†’ … ↑ Reset on INDEX

Key Registers (dsPIC)

16-bit QEI Registers (dsPIC30F/33F)

// QEI Control Register QEICONbits.QEIM = 0b110; // X4 mode, reset on index match QEICONbits.POSRES = 1; // Index pulse resets position QEICONbits.SWPAB = 0; // No swap of QEA/QEB QEICONbits.TQCS = 0; // Internal clock QEICONbits.UPDN = 1; // Direction (read-only)// Position Counter POSCNT = 0; // Current position MAXCNT = 1999; // Modulo at 2000 (500 PPR Γ— 4)// Digital Filter Control DFLTCONbits.QECK = 0b100; // Clock divider for filter DFLTCONbits.QEOUT = 1; // Enable digital filter

32-bit QEI Registers (dsPIC33E/33C/33A)

// QEI Control Register QEICONbits.QEIEN = 1; // Enable QEI QEICONbits.PIMOD = 0b110; // Modulo count mode QEICONbits.IMV = 0b11; // Index match: QEA=1, QEB=1 QEICONbits.INTDIV = 0b011; // Velocity timer prescaler QEICONbits.CCM = 0b00; // Quadrature encoder mode// Position Counter (32-bit) POSCNTH = 0; // Position high word POSCNTL = 0; // Position low word// Modulo Limits QEILEC = 0; // Lower limit (32-bit) QEIGEC = 1999; // Upper limit (32-bit)// I/O Control Register QEIOCbits.SWPAB = 0; // Swap QEA/QEB QEIOCbits.OUTFNC = 0b00; // No output function QEIOCbits.QFDIV = 0b100; // Digital filter clock divider QEIOCbits.FLTREN = 1; // Enable digital filter// Velocity/Period Capture VELCNT = …; // Position delta (velocity) INTCNT = …; // Period counter

PIC32MK QEI Registers

// Similar to dsPIC33E with additional features: QEICONbits.PIMOD = 0b111; // Modulo + index reset (MK only)

Examples

Example 1: 500 PPR Incremental Encoder (X4 Mode)

Application: BLDC Motor Position Control

Hardware: 500 PPR (Pulses Per Revolution) incremental encoder

Configuration:

  • QEI Mode: Quadrature Encoder (X4)
  • Position Output: 16-bit signed
  • Position Reset: Modulo between [0, 1999]
  • Digital Filter: 50 kHz cutoff

Resolution Calculation: // 500 PPR Γ— 4 (X4 mode) = 2000 counts/revolution Counts_per_rev = 500 * 4 = 2000; Degrees_per_count = 360 / 2000 = 0.18Β°; Radians_per_count = 2Ο€ / 2000 = 0.00314 rad; // In Simulink/MATLAB Position_rad = QEI_Position * (2pi/2000); Position_deg = QEI_Position * 0.18; Note: Set modulo limits to [0, 1999] so position wraps at 2000 counts (one full revolution). For signed output, use [-1000, 999] for symmetric range.

Example 2: Velocity Measurement from Position Difference

Application: Motor Speed Feedback

Method 1: Position Difference // Simulink blocks: QEI Position β†’ [Unit Delay] β†’ Subtract β†’ Velocity (counts/sample) β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ // Convert to RPM (1 kHz sample rate, 2000 counts/rev) Velocity_RPM = (Position - Position_z) * (1000 samples/s) * (60 s/min) / (2000 counts/rev) Velocity_RPM = Velocity_counts * 30 Method 2: Period Measurement (32-bit QEI, Better at Low Speed) // Use QEI period output Period_Output: 32-bit unsigned Max_Period: 0.1 seconds // Period in timer ticks, convert to RPM Velocity_RPM = (60 / Max_Period) / (Period_ticks / QEI_max) Tip: Position difference method works well at high speeds. Period measurement (INTCNT) is better for low speeds where counts are infrequent.

Example 3: Index-Referenced Absolute Position

Application: Multi-Turn Absolute Position with Index Pulse

Configuration:

  • Position Output: 16-bit unsigned (0-1999)
  • Index Counter Output: 16-bit signed (revolution counter)
  • Position Reset: Reset on INDEX event
  • Index Match: QEA=1, QEB=1 // Absolute position calculation: Absolute_Position_counts = (Revolution_Count * 2000) + Position_Within_Rev; // In degrees: Absolute_Position_deg = Absolute_Position_counts * 0.18; // Example: After 3.5 revolutions from index Revolution_Count = 3; Position_Within_Rev = 1000; // Halfway through 4th revolution Absolute_Position_counts = (3 * 2000) + 1000 = 7000 counts Absolute_Position_deg = 7000 * 0.18 = 1260Β° Index Homing Procedure:
  • Power on: position and revolution counters are undefined
  • Rotate motor until first index pulse β†’ counters reset to 0
  • Now position is referenced to index
  • Multi-turn position = (revolution Γ— 2000) + position

Example 4: External Pulse Counter with Direction

Application: Conveyor Belt Position Tracking

Hardware: Proximity sensor generates pulses, separate direction signal

Configuration:

  • QEI Mode: External Pulse Counter with Direction
  • QEA Pin: Pulse input (proximity sensor)
  • QEB Pin: Direction input (forward/reverse)
  • Position Output: 32-bit signed // Wiring:// QEA ← Proximity sensor pulses (1 pulse per 10 mm)// QEB ← Direction: HIGH = forward, LOW = reverse// Position in millimeters: Position_mm = QEI_Position * 10; // Velocity in mm/s (1 kHz sample): Velocity_mm_s = (Position - Position_prev) * 10 * 1000;

Troubleshooting

Common Issues and Solutions

Issue: Position Counter Counts Backward

Cause: Encoder wiring reversed (QEA and QEB swapped)

Solution: Enable “Swap QEA/QEB” parameter, or physically swap encoder wires

Issue: Noisy Position Reading (Jumps/Jitter)

Cause: Electrical noise on encoder signals

Solutions:

  • Enable digital filter with appropriate cutoff frequency (set to 1/2 of max encoder frequency)
  • Use shielded cable for encoder wiring
  • Add external RC filter on encoder inputs
  • Check encoder power supply quality

Issue: Missed Counts at High Speed

Cause: Encoder frequency exceeds QEI peripheral maximum input frequency

Solutions:

  • Reduce digital filter cutoff frequency (increases max input frequency)
  • Check encoder frequency: Max_freq = (MCU_freq / Filter_divider) / 2
  • For 70 MIPS dsPIC33: with QFDIV=3, max freq β‰ˆ 11.7 MHz / 6 = 1.95 MHz
  • Consider using lower resolution encoder or gear reduction

Issue: Index Pulse Not Detected

Causes and Solutions:

  • Check INDEX pin wiring and polarity
  • Verify “Index Match Value” corresponds to encoder state when index is active
  • Ensure index pulse width is sufficient (minimum 1 QEI clock cycle)
  • Check that encoder is rotating through the index position

Issue: Position Overflow/Underflow

Cause: Position exceeds selected datatype range

Solutions:

  • Use larger datatype (32-bit instead of 16-bit)
  • Use signed datatype for bidirectional motion
  • Enable modulo mode to wrap position at defined boundaries
  • Use revolution counter for multi-turn applications

Filter Cutoff Frequency Selection

// Rule of thumb for filter selection: Max_Encoder_Freq = Max_RPM * (Counts_per_Rev / 60); Filter_Cutoff = Max_Encoder_Freq * 2; // Nyquist criterion// Example: 3000 RPM motor with 500 PPR encoder (X4 = 2000 counts/rev) Max_Encoder_Freq = 3000 * (2000 / 60) = 100 kHz Filter_Cutoff = 100 kHz * 2 = 200 kHz // Select β‰₯ 200 kHz filter

Performance Considerations

Maximum Encoder Frequency

The maximum encoder input frequency depends on the device clock and filter settings: // For dsPIC33E/33C at 70 MIPS (140 MHz SYSCLK) QEI_Clock = 140 MHz; Filter_Divider = [1 2 4 8 16 32 64 256] * 3; // Actual divider values// Maximum input frequency: Max_Input_Freq = QEI_Clock / (Filter_Divider * 2); // Examples:// QFDIV=0 (Γ·3): Max = 140M / (32) = 23.3 MHz// QFDIV=1 (Γ·6): Max = 140M / (62) = 11.7 MHz// QFDIV=7 (Γ·768): Max = 140M / (768*2) = 91 kHz

Velocity Measurement Resolution

For 32-bit QEI with period measurement: // Period counter resolution: Timer_Prescaler = [1 2 4 8 16 32 64 256]; // INTDIV settings Timer_Frequency = QEI_Clock / Timer_Prescaler; Period_Resolution = 1 / Timer_Frequency; // Velocity range: Max_Velocity = 1 / (Period_Resolution); Min_Velocity = 1 / (Period_Resolution * 65535); // 16-bit counter// Example with INTDIV=4 (Γ·8) at 140 MHz: Period_Resolution = 1 / (140M / 8) = 57.1 ns Max_Velocity = 17.5 MHz Min_Velocity = 267 Hz

Interrupt Latency

When using QEI interrupts for index detection or position limits: Interrupt Response Time:

  • Typical latency: 10-20 instruction cycles
  • At 70 MIPS: ~140-280 ns interrupt response
  • Index position accuracy: Β±1-2 encoder counts at maximum speed
  • For precise index homing: use index match value (IMV) instead of interrupt
  • MCHP_QDEC_SAMx - Quadrature Decoder for SAM devices
  • MCHP_PDEC - Position Decoder for SAM E5x/E7x (Hall sensors, stepper)
  • MCHP_CN - Change Notification (pin interrupt, can be used for encoders without dedicated QEI)
  • MCHP_IC - Input Capture (alternative for encoder pulse timing)

References

  • dsPIC30F/33F Family Reference Manual - Section 15: Quadrature Encoder Interface
  • dsPIC33E/33C Family Reference Manual - Section 15: QEI ([DS70601B])
  • dsPIC33A Family Reference Manual - Section 15: QEI Module
  • PIC32MK Family Reference Manual - Section 22: QEI
  • [AN1078]: Sensorless Field Oriented Control (Application Note using QEI)
  • [AN1162]: Sensored FOC for PMSM/BLDC (QEI-based motor control)

Last Updated: 2024 | MCHP Blockset for MATLAB/Simulink

See Also

Application Notes

Datasheets