---
# System prepended metadata

title: How to interface a potentiometer with an FPGA using an external SPI ADC?
tags: [FPGA]

---

Here's a simple Verilog example that demonstrates how to interface a potentiometer with an [FPGA](https://www.ampheo.com/c/fpgas-field-programmable-gate-array) using an external SPI ADC (e.g., MCP3008), and display the 10-bit analog value using LEDs or send it via UART.
![image-10-1024x768](https://hackmd.io/_uploads/SyT05yzdex.png)

 **Setup Overview**
 **Components**
* [Potentiometer](https://www.onzuu.com/category/potentiometers)
* [MCP3008](https://www.onzuu.com/search/MCP3008) (10-bit SPI ADC)
* [FPGA Board](https://www.onzuu.com/category/embedded-complex-logic-fpga-cpld) (e.g., Xilinx or [Intel](https://www.ampheo.com/manufacturer/intel))
* Optional: UART interface / LEDs / 7-segment displays

 **MCP3008 Pinout (Important)**
* VDD → 3.3V or 5V
* VREF → Same as VDD
* AGND & DGND → GND
* CH0 → Potentiometer wiper
* DIN (MOSI), DOUT (MISO), CLK, CS → Connect to [FPGA](https://www.ampheoelec.de/c/fpgas-field-programmable-gate-array) SPI interface

 **MCP3008 SPI Protocol Recap**
To read CH0:

* Send: 00000001 (Start bit)
* Send: 10000000 (SGL/DIFF = 1, D2-D0 = 000 for CH0)
* Read: 10-bit result (after null bit)

 **Verilog Modules Overview**
We'll implement:

1. SPI Master Module
2. MCP3008 Reader
3. Top-Level Module

 **1. SPI Master Module (Basic, Mode 0)**
```
verilog

module spi_master (
    input clk,              // System clock
    input start,            // Start signal
    input [7:0] data_in,    // Data to send
    output reg [7:0] data_out,
    output reg done,
    output reg sclk,
    output reg mosi,
    input miso,
    output reg cs
);
    reg [3:0] bit_cnt;
    reg [7:0] shift_reg;
    reg [2:0] state;

    localparam IDLE = 0, LOAD = 1, TRANSFER = 2, DONE = 3;

    always @(posedge clk) begin
        case (state)
            IDLE: begin
                done <= 0;
                cs <= 1;
                sclk <= 0;
                if (start) state <= LOAD;
            end
            LOAD: begin
                cs <= 0;
                shift_reg <= data_in;
                bit_cnt <= 7;
                state <= TRANSFER;
            end
            TRANSFER: begin
                sclk <= ~sclk;
                if (sclk == 0) begin
                    mosi <= shift_reg[bit_cnt];
                end else begin
                    shift_reg[bit_cnt] <= miso;
                    if (bit_cnt == 0)
                        state <= DONE;
                    else
                        bit_cnt <= bit_cnt - 1;
                end
            end
            DONE: begin
                cs <= 1;
                sclk <= 0;
                data_out <= shift_reg;
                done <= 1;
                state <= IDLE;
            end
        endcase
    end
endmodule
```
 **2. MCP3008 Reader Module**
```
verilog

module mcp3008_reader (
    input clk,
    input miso,
    output sclk,
    output mosi,
    output cs,
    output reg [9:0] adc_value
);
    // SPI wires
    wire done;
    wire [7:0] data_out;
    reg start;
    reg [7:0] tx_data;
    reg [3:0] state;

    spi_master spi (
        .clk(clk),
        .start(start),
        .data_in(tx_data),
        .data_out(data_out),
        .done(done),
        .sclk(sclk),
        .mosi(mosi),
        .miso(miso),
        .cs(cs)
    );

    localparam IDLE = 0, SEND1 = 1, SEND2 = 2, READ1 = 3, READ2 = 4, DONE = 5;

    reg [7:0] rx1, rx2;

    always @(posedge clk) begin
        case (state)
            IDLE: begin
                tx_data <= 8'b00000001;
                start <= 1;
                state <= SEND1;
            end
            SEND1: if (done) begin
                start <= 0;
                tx_data <= 8'b10000000;
                start <= 1;
                state <= SEND2;
            end
            SEND2: if (done) begin
                start <= 0;
                tx_data <= 8'b00000000;
                start <= 1;
                state <= READ1;
            end
            READ1: if (done) begin
                start <= 0;
                rx1 <= data_out;
                tx_data <= 8'b00000000;
                start <= 1;
                state <= READ2;
            end
            READ2: if (done) begin
                start <= 0;
                rx2 <= data_out;
                adc_value <= {rx1[1:0], rx2[7:0]};  // Combine 10-bit result
                state <= IDLE;
            end
        endcase
    end
endmodule
```
 **3. Top-Level Module (Display on LEDs)**
```
verilog

module top (
    input clk,
    input miso,
    output sclk,
    output mosi,
    output cs,
    output [9:0] leds
);
    wire [9:0] adc_value;

    mcp3008_reader reader (
        .clk(clk),
        .miso(miso),
        .sclk(sclk),
        .mosi(mosi),
        .cs(cs),
        .adc_value(adc_value)
    );

    assign leds = adc_value;
endmodule
```
 **Simulation and Testing**
* Simulate SPI behavior using a testbench.
* Use a Saleae logic analyzer or [oscilloscope](https://www.onzuu.com/category/oscilloscopes) to verify SPI waveform.
* Turn the potentiometer and observe LED brightness or value changes.

✅ Summary
![企业微信截图_20250807164338](https://hackmd.io/_uploads/HkoSDyfOle.png)
