NetBurner 3.5.6
PDF Version
Quadrature Decoder Example

Quadrature Decoder Counter (QDC) Application

Overview

This application demonstrates the use of the i.MX RT1061's Quadrature Decoder Counter (QDC1/ENC1) peripheral to read encoder signals and display the position in real-time via both a web interface and serial console.

The application features a clean web interface that polls encoder data every 20ms, providing smooth real-time updates of encoder position and optional revolution counts.

Key Features

  • Real-time encoder position counting via QDC peripheral
  • Optional revolution counting via index signal
  • Web interface displaying live count and revolution data
  • JSON API endpoint for remote monitoring
  • Serial console output for debugging
  • Optional pulse accumulator mode (PhaseA counting only)
  • Hardware-based counting with no CPU overhead

Hardware Requirements

  • NetBurner SOMRT1061 module (or compatible i.MX RT1061-based platform)
  • Quadrature encoder with Phase A and Phase B outputs
  • Optional: Encoder with index/Z channel output for revolution counting
  • Network connection (Ethernet)

Hardware Connections

Connect your quadrature encoder to the following SOMRT1061 pins:

Pin 4 (J2-14) --> Encoder Phase A signal
Pin 15 (J2-41) --> Encoder Phase B signal
Pin 14 (J2-39) --> Encoder Index/Z signal (optional)

IMPORTANT:**

  • Encoder signals are assumed to be open-collector outputs
  • Strong internal pull-ups are automatically enabled on all QDC pins
  • If your encoder has push-pull outputs, verify voltage levels are compatible
  • Ensure proper grounding between encoder and NetBurner module

Signal Routing Architecture

The application uses the i.MX RT1061's XBARA1 crossbar to route signals:

External SOMRT1061 XBARA1 XBARA1 QDC1
Encoder --> Pin --> Input --> Output --> Input
------- --------- --------- --------- --------
Phase A --> Pin 4 --> XBAR_IN21 --> OUTPUT --> PHASEA
Phase B --> Pin 15 --> XBAR_IN03 --> OUTPUT --> PHASEB
Index --> Pin 14 --> XBAR_IN02 --> OUTPUT --> INDEX

Detailed Signal Flow

+-------------+ +----------+ +---------+ +----------+ +-------+
| Quadrature | | Physical | | XBARA1 | | XBARA1 | | QDC1 |
| Encoder | ---> | Pin | ---> | Input | ---> | Output | ---> | Input |
| Output | | (GPIO) | | Mux | | Route | | Logic |
+-------------+ +----------+ +---------+ +----------+ +-------+
| | | | |
v v v v v
Phase A Pin 4 XBAR_IN21 QDC1PhaseA Position Counter
Phase B Pin 15 XBAR_IN03 QDC1PhaseB Direction Logic
Index Pin 14 XBAR_IN02 QDC1Index Revolution Counter
@ Mux
Multiplexed address/data mode. Address and data share the same pins. Requires ADV signal to latch add...

Software Architecture

Application Initialization Flow

UserMain()
|
+---> init() // Initialize network stack
|
+---> EnableSystemDiagnostics() // Enable diagnostics
|
+---> StartHttp() // Start web server (port 80)
|
+---> WaitForActiveNetwork() // Wait for network ready
|
+---> EnableRemoteConsole() // Enable remote console
|
+---> InitQDC() // Configure QDC peripheral
|
+---> CLOCK_EnableClock(Xbar1) // Enable XBAR clock
|
+---> CLOCK_EnableClock(Enc1) // Enable QDC clock
|
+---> XBARA_SetSignalsConnection() // Route Phase A
|
+---> XBARA_SetSignalsConnection() // Route Phase B
|
+---> Pins[4].setPull(STRONG) // Configure pull-ups
|
+---> Pins[15].setPull(STRONG)
|
+---> Pins[4].function(XBAR_IN21) // Assign pins
|
+---> Pins[15].function(XBAR_IN03)
|
+---> [if DOINDEX]
| |
| +---> XBARA_SetSignalsConnection() // Route Index
| |
| +---> Pins[14].setPull(STRONG)
| |
| +---> Pins[14].function(XBAR_IN02)
|
+---> [if PULSE_ACCUMULATOR]
| |
| +---> QDC1->CTRL |= 0x200U // Enable mode
|
+---> return
|
+---> while(1) // Main loop
|
+---> OSTimeDly(1 second)
|
+---> printf(position, revolutions)
|
+---> (repeat)
void EnableRemoteConsole()
Enables the remote webconsole feature.
void StartHttp(uint16_t port, bool RunConfigMirror)
Start the HTTP web server. Further documentation in the Initialization section Initialization - Syste...
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
void EnableSystemDiagnostics()
Turn on the diagnostic reports from the config page.
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
void XBARA_SetSignalsConnection(XBARA_Type *base, xbar_input_signal_t input, xbar_output_signal_t output)
Sets a connection between the selected XBARA_IN[*] input and the XBARA_OUT[*] output signal.

Web Interface Flow

Browser
|
+---> HTTP GET / // Load index.html
| |
| v
| Display UI
| |
| v
| JavaScript starts
| |
| v
+---> fetchCount() ---> HTTP GET /count.json
| |
| v
| getJSONData() handler
| |
| v
| ReadQDC() + ReadQDCRev()
| |
| v
| Return JSON: {"count":X, "rev":Y}
| |
v<-------------------+
Update display
|
v
Wait 20ms
|
v
(repeat)

Configuration Options

Edit main.cpp to customize behavior:

DOINDEX Definition

Controls whether the index signal is used for revolution counting:

#define DOINDEX (1) // Enable index signal and revolution counter
#define DOINDEX (0) // Disable index signal (pins 4 and 15 only)

If you don't have an index signal, set DOINDEX to 0 to disable revolution counting and free up pin 14 for other uses.

PULSE_ACCUMULATOR_MODE Definition

Enables pulse accumulator mode instead of quadrature decoder mode:

#define PULSE_ACCUMULATOR_MODE (0) // Normal quadrature decoder mode
#define PULSE_ACCUMULATOR_MODE (1) // Pulse accumulator mode (PhaseA only)

Pulse Accumulator Mode:**

  • Counts only Phase A signal transitions
  • Phase B signal is ignored
  • Count direction is always UP
  • Useful for single-channel pulse counting applications
  • Refer to i.MX RT1060 Reference Manual Section 56.2.3.8

QDC Register Access

Position Counter Reading

The application reads the 32-bit position counter by combining two 16-bit registers:

+------------------+------------------+
| QDC1->UPOSH | QDC1->LPOS |
| (Upper 16 bits) | (Lower 16 bits) |
+------------------+------------------+
Bits 31:16 Bits 15:0
Combined into 32-bit signed integer:
Position = (UPOSH << 16) | LPOS
Range: -2,147,483,648 to +2,147,483,647

Revolution Counter Reading

When DOINDEX is enabled:

+------------------+
| QDC1->REV |
| (16-bit signed) |
+------------------+
Revolution Count = (int16_t)QDC1->REV
Range: -32,768 to +32,767

QDC Counting Behavior

Quadrature Decoder Mode (Default)

Direction Detection:
+--------+--------+---------------+
| PhaseA | PhaseB | Count |
| Lead | Lead | Direction |
+--------+--------+---------------+
| X | | UP (CW) |
| | X | DOWN (CCW) |
+--------+--------+---------------+
Encoding: 4X (counts on all edges of both phases)
Example Clockwise Rotation:
PhaseA: _______|-------|_______
PhaseB: ___|-------|_________|--
^ ^ ^ ^ ^ ^
1 2 3 4 5 6 (6 counts per cycle)

Pulse Accumulator Mode

Only Phase A is counted:
PhaseA: ___|---|___|---|___|---
^ ^ ^
UP UP UP
Phase B is ignored
Direction is always UP

Using the Application

Serial Console Output

After programming, connect to the device via serial console or remote console. You'll see output every second:

Web Application: QDC test
NNDK Revision: 3.x.x
Connect phase A and B to pins 4 and 15
1 : QDC 0 0
2 : QDC 48 0
3 : QDC 128 1

Format:** <seconds> : QDC <position> <revolutions>

Web Interface

  1. Open a web browser
  2. Navigate to your NetBurner device's IP address
  3. You'll see a large display box showing the current encoder count
  4. If index is enabled, a second box shows revolution count
  5. Display updates automatically every 20ms (50 Hz refresh rate)

    Display Elements:**

  • Top box: Position counter (always visible)
  • Bottom box: Revolution counter (visible only when DOINDEX enabled)
  • Updates in real-time with smooth animation

JSON API

Access raw encoder data programmatically:

Endpoint:** http://<device_ip>/count.json

Response format (with index enabled):**

{"count": 1234, "rev": 5}

Response format (without index):**

{"count": 1234}

HTTP Headers:**

HTTP/1.0 200 OK
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Expires: 0
Content-Type: application/json

Note:** Response includes cache-control headers to prevent stale data

Pin Configuration Details

SOMRT1061 Pin Mapping

+-------------+------------+------------------+------------------+-------------------+
| SOMRT1061 | Connector | GPIO Name | XBAR Function | QDC Function |
| Pin Number | (J2 Pin) | | | |
+-------------+------------+------------------+------------------+-------------------+
| Pin 4 | J2-14 | GPIO_AD_B0_03 | XBAR1_IN21 | Phase A Input |
| Pin 15 | J2-41 | GPIO_AD_B1_15 | XBAR1_IN03 | Phase B Input |
| Pin 14 | J2-39 | GPIO_AD_B1_14 | XBAR1_IN02 | Index Input |
+-------------+------------+------------------+------------------+-------------------+

Pin Electrical Configuration

All QDC input pins are configured with:

  • Pull-up: STRONG (22K ohm typical)
  • Direction: Input
  • Function: XBAR alternate function
  • Voltage: 3.3V logic levels

Troubleshooting

Encoder Count Doesn't Change

Problem:** Position counter remains at 0 or doesn't increment/decrement

Solutions:**

  • Verify encoder is powered and functioning
  • Check Phase A/B connections to pins 4 and 15
  • Verify encoder outputs are compatible with 3.3V logic
  • Check for proper grounding between encoder and module
  • Use a multimeter or oscilloscope to verify encoder signals
  • Verify encoder cable is not broken or damaged

Count Increases/Decreases Erratically

Problem:** Counter jumps randomly or counts wrong direction

Solutions:**

  • Check for loose connections on encoder cable
  • Verify encoder cable shielding and grounding
  • Ensure encoder signals are clean (use oscilloscope if available)
  • Check for electrical noise from nearby motors or power supplies
  • Try shorter cable runs between encoder and module
  • Verify encoder phases are not swapped
  • Check that encoder supply voltage is stable

Web Page Shows "Error" or Doesn't Update

Problem:** Browser displays "Error" in count box

Solutions:**

  • Verify device is on network (ping the IP address)
  • Check that HTTP server started (look for startup message in console)
  • Verify index.html was compiled into application (check htmldata.cpp)
  • Check browser console (F12) for JavaScript errors
  • Try clearing browser cache and reloading
  • Verify count.json endpoint responds: http://<ip>/count.json

Revolution Counter Doesn't Work

Problem:** Revolution count stays at 0 or doesn't increment

Solutions:**

  • Verify DOINDEX is set to 1 in main.cpp
  • Check index signal connection to pin 14
  • Verify encoder provides an index/Z channel output
  • Check index signal voltage levels (must be 3.3V compatible)
  • Ensure encoder completes full rotations to trigger index
  • Verify index signal timing matches encoder specifications
  • Use oscilloscope to verify index pulse is present

Wrong Count Direction

Problem:** Counter increases when it should decrease (or vice versa)

Solutions:**

  • Swap Phase A and Phase B connections
  • Or modify code to swap pin assignments
  • Verify encoder rotation matches expected direction

Compilation Errors

Problem:** Code won't compile or shows missing definitions

Solutions:**

  • Verify all required header files are included
  • Check PLATFORM is set correctly (SOMRT1061)
  • Ensure NNDK version supports QDC peripheral
  • Verify fsl_xbara.h and fsl_clock.h are available
  • Check that MIMXRT1061.h is in include path

Performance Characteristics

QDC Peripheral

  • Hardware-based counting: No CPU overhead
  • Position counter: 32-bit signed (-2.1B to +2.1B counts)
  • Revolution counter: 16-bit signed (-32K to +32K revolutions)
  • Maximum frequency: Limited by input signal rise/fall times
  • Decoding mode: 4X quadrature (default)
  • Pulse accumulator: Rising/falling edge selectable

Web Interface

  • Update rate: 50 Hz (every 20ms)
  • JSON endpoint: No rate limiting, safe for high-frequency polling
  • Network latency: Typical <5ms on local network
  • **Browser compatibility:** All modern browsers with fetch API

Serial Console

  • **Update rate:** 1 Hz (every second)
  • **Baud rate:** Default UART settings
  • **Format:** Human-readable text output

Technical Details

QDC Peripheral Registers

Key registers accessed by the application:

Register Size Description
QDC1->LPOS 16-bit Position Counter Lower 16 bits
QDC1->UPOSH 16-bit Position Counter Upper 16 bits (hold)
QDC1->REV 16-bit Revolution Counter (signed)
QDC1->CTRL 16-bit Control Register

XBARA Signal Connections

Internal signal routing configured in InitQDC():

XBARA1 Routing Table:
+----------------------+------------------------+
| Input Signal | Output Signal |
+----------------------+------------------------+
| kXBARA1_Input | kXBARA1_Output |
| IomuxXbarIn21 | Qdc1PhaseAInput |
| (from Pin 4) | (to QDC Phase A) |
+----------------------+------------------------+
| kXBARA1_Input | kXBARA1_Output |
| IomuxXbarIn03 | Qdc1PhaseBInput |
| (from Pin 15) | (to QDC Phase B) |
+----------------------+------------------------+
| kXBARA1_Input | kXBARA1_Output |
| IomuxXbarIn02 | Qdc1Index |
| (from Pin 14) | (to QDC Index) |
+----------------------+------------------------+

Clock Requirements

The following clocks must be enabled:

CLOCK_EnableClock(kCLOCK_Xbar1); // XBARA1 crossbar clock
CLOCK_EnableClock(kCLOCK_Enc1); // QDC/ENC1 peripheral clock

Clock Sources:**

  • XBARA1: AHB clock domain
  • ENC1: IPG clock domain (typically 75 MHz)

Application Files

Source Files

  • main.cpp - Main application code with QDC peripheral initialization
  • index.html - Web interface with real-time encoder display

Generated Files

  • htmldata.cpp - Auto-generated from html/ directory by comphtml tool
  • makefile - Build configuration (create separately)

Required Directories

QDC_Demo/
+-- src/
| +-- main.cpp
| +-- htmldata.cpp (generated)
|
+-- html/
| +-- index.html
|
+-- makefile

Customization Examples

Changing Update Rates

Serial Console Rate:**

// In UserMain() while loop
OSTimeDly(TICKS_PER_SECOND * 5); // Change to 5 seconds
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49

Web Interface Rate:**

// In index.html
setInterval(fetchCount, 100); // Change to 100ms (10 Hz)

Adding More Display Information

Add velocity calculation to JSON:**

int getJSONData(int sock, HTTP_Request &pd)
{
static int lastCount = 0;
int currentCount = ReadQDC();
int velocity = currentCount - lastCount;
lastCount = currentCount;
fdprintf(sock,
"HTTP/1.0 200 OK\r\n...\r\n"
"{\"count\":%d, \"rev\":%d, \"velocity\":%d}\r\n",
currentCount, ReadQDCRev(), velocity);
return 1;
}
int fdprintf(int fd, const char *format,...)
Print formatted output to a file descriptor.
HTTP Request Structure.
Definition http.h:87

Using Different Pins

To use different SOMRT1061 pins, modify InitQDC():

// Example: Use pins 5 and 6 instead of 4 and 15
Pins[5].setPull(PinIO::PULL_UP_STRONG);
Pins[6].setPull(PinIO::PULL_UP_STRONG);
Pins[5].function(PIN_05_XBAR1_IN22); // Check pins.h for valid functions
Pins[6].function(PIN_06_XBAR1_IN23);
kXBARA1_InputIomuxXbarIn22, // New input
kXBARA1_OutputQdc1PhaseAInput);

Technical References

Documentation

  • i.MX RT1060 Processor Reference Manual
    • Chapter 4: External Signals and Pin Multiplexing (IOMUXC)
    • Chapter 56: Quad Decoder (ENC)
    • Chapter 68: Crossbar Switch (XBARA, XBARB)
    • Section 56.2.3.8: Pulse Accumulator Functionality
  • NXP SDK Files
    • fsl_xbara.h: XBARA driver and signal enumerations
    • fsl_clock.h: Clock gating functions
    • MIMXRT1061.h: Register definitions
  • NetBurner Documentation
    • pins.h: Pin function definitions for SOMRT1061
    • Platform Guide: Hardware reference for your module
    • HTTP API: Web server documentation

Important Notes

Note:** The QDC peripheral automatically handles quadrature decoding in hardware. Once configured, no CPU intervention is required for counting.

Note:** Pull-up resistors are required because most encoders use open-collector outputs. The strong pull-ups (22K typical) are enabled automatically.

Warning:** Ensure encoder signal voltage levels are compatible with 3.3V logic. Voltages above 3.6V may damage the processor.

Warning:** Long cable runs between encoder and module may introduce noise. Keep cables as short as possible and use shielded cable for best results.

Warning:** The position counter is 32-bit and will wrap around after reaching maximum/minimum values. Implement overflow detection if needed for your application.

Version Information

  • Application Version: 1.0
  • Target Platform: NetBurner SOMRT1061
  • Required NNDK: 3.x or later
  • Processor: NXP i.MX RT1061

Support and Resources

For additional support: