Practical case: Potentiometer ADC to LED Bar DE10-Lite FPGA

Practical case: Potentiometer ADC to LED Bar DE10-Lite FPGA — hero

Objective and use case

What you’ll build: You will read the on-board potentiometer using the Intel MAX 10 ADC and display its value on a 10-LED bargraph with the DE10-Lite FPGA.

Why it matters / Use cases

  • Real-time monitoring of analog signals in educational projects using the DE10-Lite FPGA.
  • Demonstrating ADC capabilities of the Intel MAX 10 in embedded systems applications.
  • Creating interactive displays that respond to user input through potentiometers.
  • Utilizing LED bargraphs for visual feedback in robotics and automation projects.

Expected outcome

  • Accurate representation of potentiometer values on the LED bargraph with a response time of less than 100ms.
  • Ability to display values ranging from 0 to 10, corresponding to the full scale of the potentiometer.
  • Demonstration of ADC throughput at 1k samples per second.
  • Reduction of latency in user input feedback to under 50ms.

Audience: Students and hobbyists; Level: Intermediate

Architecture/flow: ADC reads potentiometer values, processes them in Verilog, and drives the LED bargraph output.

Basic Hands‑On: ADC Potentiometer to LED Bargraph on DE10‑Lite (Intel MAX 10)

Objective: Read the on‑board potentiometer through the Intel MAX 10 integrated ADC and display its value as a 10‑LED bargraph on the DE10‑Lite.

Device family and model: FPGA, DE10‑Lite (Intel MAX 10)

Preferred HDL: Verilog (concise subset)

Toolchain: Intel Quartus Prime Lite Edition

Prerequisites

  • You are comfortable creating a new Quartus project, adding HDL files, and compiling.
  • You can open and use Platform Designer (formerly Qsys) to generate IP.
  • OS: Windows 10/11 (64‑bit) or Ubuntu 20.04/22.04 LTS (64‑bit).
  • Installed toolchain:
  • Quartus Prime Lite 23.1std (Build 991 or newer; Lite edition works with MAX 10)
  • Includes Platform Designer and USB‑Blaster II driver.
  • Board support material:
  • Terasic DE10‑Lite board documentation and assignments (from the official DE10‑Lite “Board CD”/Downloads). We’ll import the official pin assignments so we don’t have to hand‑enter pin locations.
  • Administrative rights to install drivers (Windows) or set udev rules (Linux).

Materials

  • 1× Development board: DE10‑Lite (Intel MAX 10, model 10M50)
  • 1× USB‑Micro cable (for USB‑Blaster II JTAG + power)
  • PC or laptop with Quartus Prime Lite 23.1std installed
  • Optional: anti‑static mat and wrist strap

No external wiring is required. We will use the on‑board potentiometer and user LEDs.

Setup/Connection

  • Connect the DE10‑Lite to your computer using the USB‑Micro cable to the USB‑Blaster II (JTAG) port.
  • The board will be powered from USB by default.
  • Leave jumpers at factory defaults so that:
  • The USB‑Blaster II JTAG is enabled.
  • The on‑board potentiometer is routed to the MAX 10 ADC analog channel (factory default on DE10‑Lite).
  • Verify JTAG visibility:
  • Windows: Open an “Intel FPGA Command Prompt for 23.1std”.
  • Linux: In a terminal, add Quartus to PATH (see below) and run the same command.
  • Command:
    jtagconfig
  • Expected: A line showing USB‑Blaster II and the MAX 10 device, e.g., “10M50” detected.

Tools and Paths

  • Windows default installation:
  • Quartus: C:\intelFPGA_lite\23.1std\quartus\bin64
  • Linux default installation:
  • Quartus: /opt/intelFPGA_lite/23.1std/quartus/bin

If needed, set your PATH temporarily:

  • Windows (PowerShell):
    $env:PATH="C:\intelFPGA_lite\23.1std\quartus\bin64;$env:PATH"
  • Linux (bash):
    export PATH=/opt/intelFPGA_lite/23.1std/quartus/bin:$PATH

High‑Level Plan

  • Create a Quartus project for the DE10‑Lite (MAX 10 10M50… device).
  • Import the official Terasic DE10‑Lite pin assignments so the 50 MHz clock, LEDs, keys, etc., are correctly constrained.
  • Use Platform Designer to instantiate the “Modular ADC” IP in streaming sequencer mode and a PLL for the ADC clock.
  • Write a small Verilog top that:
  • Receives 12‑bit ADC samples for the potentiometer channel,
  • Performs a tiny low‑pass filter to reduce flicker, and
  • Lights up 1..10 LEDs depending on the analog value (bargraph).
  • Add a simple SDC for timing (50 MHz input clock).
  • Compile, program, and validate by rotating the potentiometer.

Table: Logical Connections

Logical Signal Description Board Resource on DE10‑Lite
CLOCK_50 50 MHz system clock On‑board 50 MHz oscillator into MAX 10
KEY[0] Active‑low reset input (we use KEY0) Push button KEY0
LEDR[9:0] 10 LED bargraph outputs Ten red LEDs labeled LEDR0..LEDR9
ADC POT channel ADC sequencer channel for on‑board potentiometer MAX 10 integrated ADC analog input (routed by Terasic; see board docs)

Note: The analog pin mapping for the on‑board potentiometer is fixed by the DE10‑Lite design. You do not route wires; you only enable the correct ADC channel in the Modular ADC IP sequencer.

Project Creation and Board Assignments

  1. Create a workspace directory:
  2. Windows:
    mkdir C:\fpga\de10lite_adc_leds
    cd C:\fpga\de10lite_adc_leds
  3. Linux:
    mkdir -p ~/fpga/de10lite_adc_leds
    cd ~/fpga/de10lite_adc_leds

  4. Create a new Quartus project (GUI: File → New Project Wizard) or CLI:
    quartus_sh --tcl_eval "project_new de10lite_adc_leds -overwrite -revision de10lite_adc_leds"

  5. Set the device in Quartus (GUI: Assignments → Device…):

  6. Family: Intel MAX 10
  7. Device: 10M50DAF484C7G (the DE10‑Lite’s MAX 10)
  8. If you prefer TCL:
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name FAMILY \"MAX 10\"; set_global_assignment -name DEVICE 10M50DAF484C7G; project_close"

  9. Import Terasic’s official DE10‑Lite pin assignments:

  10. Download the DE10‑Lite Board CD from Terasic and locate the pin assignment .qsf or .tcl provided for the board’s default IO (LEDs, KEYs, CLOCK_50, etc.). Place it in your project folder, e.g., “boards/DE10_Lite_default_pins.qsf”.
  11. Merge it (GUI: Assignments → Import Assignments…) or add this line to your project’s .qsf to include it automatically:
    set_global_assignment -name IMPORT_FILE boards/DE10_Lite_default_pins.qsf
  12. Verify that the following logical names exist after import:

    • CLOCK_50 (50 MHz)
    • LEDR[9..0]
    • KEY[?] (at least KEY[0])
  13. Create a constraints folder and SDC (we will add content later):
    mkdir -p constraints

Platform Designer System: ADC + PLL

We will build a tiny system that creates a 10 MHz (typ.) ADC clock via PLL and emits streaming ADC samples for the potentiometer channel.

  1. Open Platform Designer (Tools → Platform Designer).

  2. New System → Name it “adc_sys”.

  3. Add IP:

  4. “Clock Source” (optional if you export top‑level clock, but we will export the top clock into the system):
    • Or simply export a clk input named “clk_clk”.
  5. “ALTPLL Intel FPGA IP” (aka “Altera PLL”), name it “adc_pll”:
    • Input: 50 MHz (connected to the system clock)
    • Output c0: 10.000 MHz (50% duty cycle)
    • Export the “locked” signal (we’ll gate sampling with it).
  6. “Modular ADC Intel FPGA IP” (MAX 10):

    • Device Family: MAX 10
    • Mode: Sequencer only, streaming output
    • Enable only the channel wired to the on‑board potentiometer (refer to DE10‑Lite manual; typically a single‑ended channel—enable that one in the channel mask).
    • Data width: 12 bits (MAX 10 ADC is 12‑bit)
    • Clocking:
    • ADC core clock input ← adc_pll.c0 (10 MHz)
    • Control/CSR clock ← system clock (50 MHz)
    • Expose streaming outputs:
    • samples_valid
    • samples_channel[4:0]
    • samples_data[11:0]
    • Export the ADC PLL clock and locked (depending on the IP version, the generated wrapper may expose these as named ports).
  7. Connections:

  8. clk_clk (system 50 MHz) → adc_pll.inclk0
  9. adc_pll.c0 → modular_adc.adc_pll_clock (or equivalent port for ADC core clock)
  10. system reset → both adc_pll and modular_adc resets
  11. Export:

    • clk_clk (as a top port of adc_sys)
    • reset
    • modular_adc.sequencer_samples_* (valid/channel/data)
    • adc_pll.locked (for top‑level gating)
  12. Generate HDL:

  13. Output language: Verilog
  14. Output directory: ip/adc_sys
  15. After generation, add the generated QIP to the Quartus project:
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name QIP_FILE ip/adc_sys/adc_sys.qip; project_close"

  16. Important: Configure the Modular ADC’s sequencer to sample only the potentiometer’s channel. If you’re unsure which channel corresponds to the on‑board potentiometer, check the DE10‑Lite User Manual and schematics. The channel index you enable here must match the POT_CHANNEL parameter used in the example top‑level code below.

Full Code (Verilog + SDC)

Create the following files in your project.

1) Verilog top level: rtl/de10lite_adc_leds_top.v

`timescale 1ns/1ps

// Top-level for DE10-Lite (Intel MAX 10): ADC potentiometer to LED bargraph
module de10lite_adc_leds_top (
    input  wire        CLOCK_50,  // 50 MHz system clock (from board assignment)
    input  wire [1:0]  KEY,       // KEY[0] used as active-low reset
    output reg  [9:0]  LEDR       // 10 user LEDs as bargraph
);

    // Active-low pushbutton -> active-high reset
    wire reset_n = KEY[0];
    wire reset   = ~reset_n;

    // Wires to/from the Platform Designer system (adc_sys)
    wire        adc_pll_locked;
    wire        samp_valid;
    wire [4:0]  samp_channel;
    wire [11:0] samp_data;

    // Instantiate the generated Platform Designer system
    // Make sure adc_sys.v/.qip are generated and added to the project.
    adc_sys u_adc_sys (
        .clk_clk                             (CLOCK_50),
        .reset_reset_n                       (reset_n),

        // These port names match common Modular ADC wrapper generations.
        // Adapt the names if your generated ports differ.
        .modular_adc_0_adc_pll_locked_export (adc_pll_locked),

        .modular_adc_0_sequencer_samples_valid   (samp_valid),
        .modular_adc_0_sequencer_samples_channel (samp_channel),
        .modular_adc_0_sequencer_samples_data    (samp_data)
    );

    // Choose the channel number you enabled for the potentiometer in Modular ADC.
    // Consult the DE10-Lite manual to confirm which channel is wired to the on-board POT.
    // Example uses POT_CHANNEL = 5. Adjust if needed.
    localparam [4:0] POT_CHANNEL = 5;

    // Simple IIR low-pass filter to reduce LED flicker
    // Filter: y += (x - y) / 16  -> implement as y = y + (x<<4 - y) using 16-bit accumulator
    reg [15:0] filt_accum;
    always @(posedge CLOCK_50 or posedge reset) begin
        if (reset) begin
            filt_accum <= 16'd0;
        end else if (adc_pll_locked && samp_valid && (samp_channel == POT_CHANNEL)) begin
            // scale input to 16-bit space: (x << 4) so division by 16 feels natural
            filt_accum <= filt_accum + ({samp_data, 4'b0000} - filt_accum);
        end
    end

    // Filtered 12-bit value (rounded/truncated)
    wire [11:0] filtered = filt_accum[15:4];

    // Convert 12-bit value (0..4095) to 10-segment bargraph thresholds
    // Thresholds at k/10 of full scale: approximately
    // 409, 819, 1228, 1638, 2048, 2457, 2867, 3276, 3686, 4095
    reg [9:0] leds_next;
    always @* begin
        leds_next[0] = (filtered >= 12'd409);
        leds_next[1] = (filtered >= 12'd819);
        leds_next[2] = (filtered >= 12'd1228);
        leds_next[3] = (filtered >= 12'd1638);
        leds_next[4] = (filtered >= 12'd2048);
        leds_next[5] = (filtered >= 12'd2457);
        leds_next[6] = (filtered >= 12'd2867);
        leds_next[7] = (filtered >= 12'd3276);
        leds_next[8] = (filtered >= 12'd3686);
        leds_next[9] = (filtered >= 12'd4095);
    end

    // Register outputs for clean timing
    always @(posedge CLOCK_50 or posedge reset) begin
        if (reset) begin
            LEDR <= 10'b0;
        end else begin
            LEDR <= leds_next;
        end
    end

endmodule

Notes:
– KEY[0] is used as an active‑low reset (press to reset). If you prefer, invert logic or tie reset_n high.
– If your generated adc_sys port names differ (they sometimes do depending on IP version), adapt the instance accordingly. The important signals are the streaming outputs (valid/channel/data) and the PLL lock indicator.

2) SDC constraints: constraints/de10lite_adc_leds.sdc

create_clock -name CLK50 -period 20.000 [get_ports {CLOCK_50}]

# Derive PLL clocks (if any in IP)
derive_pll_clocks
derive_clock_uncertainty

# Cut false paths on asynchronous reset
set_false_path -from [get_ports {KEY[*]}]

Add the SDC to the project:

quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name SDC_FILE constraints/de10lite_adc_leds.sdc; project_close"

3) Set the top‑level entity and add the HDL:

quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name TOP_LEVEL_ENTITY de10lite_adc_leds_top; set_global_assignment -name VERILOG_FILE rtl/de10lite_adc_leds_top.v; project_close"

Make sure the Platform Designer QIP is already added (see prior step).

Build/Flash/Run (Quartus Prime Lite 23.1std)

You can compile via the GUI (Processing → Start Compilation) or with CLI:

  • One‑shot flow:
    quartus_sh --flow compile de10lite_adc_leds

  • Step‑by‑step:
    quartus_map de10lite_adc_leds -c de10lite_adc_leds
    quartus_fit de10lite_adc_leds -c de10lite_adc_leds
    quartus_asm de10lite_adc_leds -c de10lite_adc_leds
    quartus_sta de10lite_adc_leds -c de10lite_adc_leds

  • Confirm JTAG cable:
    jtagconfig

  • Program the SOF to SRAM:
    quartus_pgm -m jtag -o "p;output_files/de10lite_adc_leds.sof"

If you see multiple cables or devices, specify the index with “-c ”, e.g., “-c 1”.

Step‑by‑Step Validation

  1. Power and program:
  2. Ensure the board is powered via the USB‑Blaster II connection.
  3. Run the quartus_pgm command above. The LEDs should briefly reset.

  4. Baseline check:

  5. With the potentiometer at the minimum position (fully counter‑clockwise), the LEDs should be all OFF (or only the last LED remains off until the input crosses its threshold). Since we apply thresholds, the first LED only lights once the 12‑bit reading is ≥ 409.

  6. Sweep the potentiometer slowly from minimum to maximum:

  7. Observe the LEDR[9:0] fill from right to left (or left to right depending on your board’s LED numbering). In our code, LEDs are ordered as LEDR[0] the first threshold, up to LEDR[9] for near‑full‑scale.
  8. The bargraph should grow monotonically as you turn the knob clockwise. At mid‑scale (approximately), around 5 LEDs should be lit.

  9. Flicker and responsiveness:

  10. The simple IIR filter smooths the signal so that minor jitters do not toggle LEDs rapidly. The response should feel smooth and stable.

  11. Reset behavior:

  12. Press KEY0 briefly. LEDs should clear during reset and then resume tracking once released.

  13. Optional: Confirm ADC stream internal signals with SignalTap (if desired):

  14. Add a new SignalTap file (.stp), clock it with CLOCK_50.
  15. Probe: samp_valid, samp_channel[4:0], samp_data[11:0], adc_pll_locked, filtered[11:0].
  16. Trigger when samp_valid == 1 and samp_channel == POT_CHANNEL.
  17. Observe samp_data increasing/decreasing as you turn the knob, and filtered tracking it with smoothing.

Troubleshooting

  • USB‑Blaster not detected:
  • Windows: Install the USB‑Blaster II driver from Quartus (Tools → Programmer → Hardware Setup → Driver).
  • Linux: Add udev rules per Intel docs, replug the cable, run “jtagconfig” as your user.

  • “Error (209040): Can’t access JTAG chain”:

  • Use a different USB port/cable.
  • Ensure no other tools have claimed the JTAG.
  • Try “jtagconfig -n” then “jtagconfig” again.

  • Device mismatch:

  • Confirm device setting is 10M50DAF484C7G (Assignments → Device).
  • If you imported a .qsf from another project, ensure your own .qsf isn’t overriding device choice.

  • No LEDs light when turning the pot:

  • Check that you imported the official DE10‑Lite pin assignments and that LEDR[9:0] and CLOCK_50 are correctly assigned.
  • Verify ADC PLL lock (adc_pll_locked should be 1). If not, verify the PLL input is 50 MHz and c0 is 10 MHz.
  • Check Modular ADC sequencer configuration:
    • The correct channel for the potentiometer must be enabled.
    • The “samples” streaming interface must be exported.
  • Confirm POT_CHANNEL constant in your top matches the enabled channel index.
  • Try removing filtering temporarily and drive LEDs directly from samp_data thresholds to confirm activity.

  • LED order reversed:

  • If turning CW reduces the number of LEDs, your physical LED index is opposite of your mental model. Just reverse the display mapping or swap the order by assigning LEDR <= {leds_next[0], leds_next[1], …} reversed, or flip the thresholds orientation.

  • Compile errors referencing adc_sys port names:

  • IP port names can vary slightly across Quartus versions. Open the generated adc_sys/synthesis/adc_sys.v to confirm exact signal names and update the instance mapping accordingly.

  • Timing warnings:

  • Ensure the SDC is added. The simple logic here should meet timing easily at 50 MHz. If not, check that your SDC path is correct and that clocks are recognized (see TimeQuest “Report Clocks”).

  • ADC channel uncertainty:

  • If you’re unsure which channel number corresponds to the on‑board potentiometer, consult the DE10‑Lite manual/schematics. Alternatively, enable a small set of candidate channels in the sequencer, and in SignalTap watch which “samp_channel” toggles when you rotate the potentiometer. Set POT_CHANNEL accordingly and disable others afterward to reduce switching noise and power.

Improvements and Variations

  • Gamma/Nonlinear mapping:
  • Human perception is nonlinear; you can remap thresholds to a perceptual scale so the bargraph “feels” more uniform.

  • Hysteresis:

  • Add small hysteresis around thresholds to avoid toggling when the reading is near a boundary.

  • Dynamic averaging:

  • Make the filter time constant depend on signal slope: faster response on large changes, stronger filtering when steady.

  • PWM brightness:

  • Instead of binary on/off, use PWM to vary LED brightness proportionally to the fractional bucket between two thresholds.

  • 7‑segment display:

  • Convert the 12‑bit value to a 0..99 percentage and show on the 7‑segments while still driving the bargraph.

  • Multi‑channel scan:

  • Sample other analog sources on the board (if available) or external sources through headers by enabling more channels in the sequencer and multiplexing outputs.

  • Software control:

  • Wrap the ADC with an Avalon‑MM accessible CSR block and read values from a softcore (Nios II/e), printing to UART/USB‑Blaster‑JTAG console for logging.

  • Calibration:

  • If the ends of the potentiometer travel are slightly below/above full scale, implement calibration to map min/max ADC readings to 0%/100% and store in nonvolatile memory (if desired).

Exact Commands Summary

  • Create project:
    quartus_sh --tcl_eval "project_new de10lite_adc_leds -overwrite -revision de10lite_adc_leds"

  • Set device (if not done in GUI):
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name FAMILY \"MAX 10\"; set_global_assignment -name DEVICE 10M50DAF484C7G; project_close"

  • Import board pin assignments (example path; adjust to your environment):
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name IMPORT_FILE boards/DE10_Lite_default_pins.qsf; project_close"

  • Add SDC:
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name SDC_FILE constraints/de10lite_adc_leds.sdc; project_close"

  • Add top‑level Verilog:
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name TOP_LEVEL_ENTITY de10lite_adc_leds_top; set_global_assignment -name VERILOG_FILE rtl/de10lite_adc_leds_top.v; project_close"

  • Add Platform Designer QIP (after generating adc_sys):
    quartus_sh --tcl_eval "project_open de10lite_adc_leds; set_global_assignment -name QIP_FILE ip/adc_sys/adc_sys.qip; project_close"

  • Build:
    quartus_sh --flow compile de10lite_adc_leds

  • Program:
    jtagconfig
    quartus_pgm -m jtag -o "p;output_files/de10lite_adc_leds.sof"

Why the Design Works (Brief)

  • The MAX 10’s integrated ADC outputs 12‑bit samples. The Modular ADC IP in sequencer streaming mode pushes samples tagged with a channel ID. We select only the potentiometer channel, filter slightly in logic, and then compare against 10 evenly spaced thresholds to drive the LED bargraph.
  • The ADC’s analog core requires a low‑frequency clock (~10 MHz typical), which we generate using a PLL locked to the 50 MHz board clock. The PLL’s lock status is used to gate sampling to ensure valid timing.

Final Checklist

  • Quartus Prime Lite 23.1std installed; USB‑Blaster II driver OK.
  • Project created with device: 10M50DAF484C7G.
  • Terasic DE10‑Lite pin assignments imported; CLOCK_50, LEDR[9:0], KEY[0] present.
  • Platform Designer system (adc_sys) generated:
  • PLL c0 = 10 MHz (locked exported)
  • Modular ADC in sequencer streaming mode
  • Correct potentiometer channel enabled
  • QIP added to project
  • Verilog top compiled with no errors; SDC added.
  • quartus_sh –flow compile succeeds; TimeQuest shows clean clocks.
  • quartus_pgm successfully programs the .sof; jtagconfig sees the board.
  • Turning the on‑board potentiometer grows/shrinks the LED bargraph smoothly.
  • Optional: SignalTap confirms samp_data and filtered behavior is sensible.

If all items above are checked, your “adc‑potentiometer‑to‑leds‑bargraph” on DE10‑Lite is complete.

Find this product and/or books on this topic on Amazon

Go to Amazon

As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.

Quick Quiz

Question 1: What is the primary objective of the project described in the article?




Question 2: Which device family and model is used in the project?




Question 3: What is the preferred HDL for this project?




Question 4: Which toolchain is specified for use in the project?




Question 5: What operating systems are compatible with the project setup?




Question 6: What is included in the installed toolchain for this project?




Question 7: What is the model of the development board used?




Question 8: What type of cable is required to connect the DE10-Lite to the computer?




Question 9: What is the purpose of the anti-static mat and wrist strap?




Question 10: What is the maximum number of LEDs that can be displayed on the bargraph?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Telecommunications Electronics Engineer and Computer Engineer (official degrees in Spain).

Follow me:
error: Contenido Protegido / Content is protected !!
Scroll to Top