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()
|
|
|
|
|
|
+---> InitQDC()
|
+---> CLOCK_EnableClock(Xbar1)
|
+---> CLOCK_EnableClock(Enc1)
|
|
|
+---> Pins[4].setPull(STRONG)
|
+---> Pins[15].setPull(STRONG)
|
+---> Pins[4].function(XBAR_IN21)
|
+---> Pins[15].function(XBAR_IN03)
|
+---> [if DOINDEX]
| |
| |
| +---> Pins[14].setPull(STRONG)
| |
| +---> Pins[14].function(XBAR_IN02)
|
+---> [if PULSE_ACCUMULATOR]
| |
| +---> QDC1->CTRL |= 0x200U
|
+---> return
|
+---> while(1)
|
+---> 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 /
| |
| 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)
#define DOINDEX (0)
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)
#define PULSE_ACCUMULATOR_MODE (1)
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
- Open a web browser
- Navigate to your NetBurner device's IP address
- You'll see a large display box showing the current encoder count
- If index is enabled, a second box shows revolution count
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):**
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);
CLOCK_EnableClock(kCLOCK_Enc1);
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:**
#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:**
{
static int lastCount = 0;
int currentCount = ReadQDC();
int velocity = currentCount - lastCount;
lastCount = currentCount;
"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():
Pins[5].setPull(PinIO::PULL_UP_STRONG);
Pins[6].setPull(PinIO::PULL_UP_STRONG);
Pins[5].function(PIN_05_XBAR1_IN22);
Pins[6].function(PIN_06_XBAR1_IN23);
kXBARA1_InputIomuxXbarIn22,
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: