FPGA Project: ADC → FFT → UART

ampheoampheo
3 min read

This project reads analog signals using an ADC, performs FFT on the digital data in real time using FPGA, and sends the frequency-domain results to a PC via UART (serial port).


Project Overview

Flow Diagram

scss

Analog Signal
     ↓
[ ADC (e.g., SPI/I2C) ]
     ↓
[ FPGA ]
 ├─ Input Buffer
 ├─ FFT Core (Radix-2, 64 or 128 points)
 └─ UART Transmitter
     ↓
PC Terminal (Plot FFT data)

Hardware Requirements

ComponentPurpose
FPGA Boarde.g., Xilinx Artix-7, Cyclone IV/V
ADC Modulee.g., MCP3008 (SPI), AD7606 (Parallel)
Clock SourceExternal oscillator or onboard clock
Serial-USB Convertere.g., FTDI, CH340 for UART output
PC Terminale.g., PuTTY, RealTerm, or Python GUI

Functional Blocks

1. ADC Interface (SPI or Parallel)

  • Connect ADC to FPGA.

  • Sample signal at regular intervals.

  • Store N samples (e.g., 64 or 128) in a buffer.

Example: MCP3008 (10-bit SPI ADC)
Use SPI master FSM to read values and convert to 16-bit fixed-point numbers.


2. FFT Core (Custom or IP Core)

  • Use pipelined or iterative Radix-2 FFT.

  • Can be:

    • Xilinx FFT IP (Vivado)

    • Open-source FFT HDL core

  • Input: N samples (real only; imaginary = 0)

  • Output: Complex frequency bins (magnitude or raw)


3. Magnitude Calculation (Optional)

  • Compute |X(k)| = √(Re² + Im²)

  • Use CORDIC or approximation (e.g., abs(Re) + abs(Im)/2)


4. UART Transmitter

  • Transmit FFT output (e.g., 64 magnitudes) at baud rate like 115200

  • Packet structure: Start byte → data → Stop byte

verilog

// UART byte-wise output FSM
always @(posedge clk) begin
  case (state)
    SEND_START: tx_data <= 8'hAA;
    SEND_DATA: tx_data <= fft_result[counter];
    SEND_END: tx_data <= 8'h55;
  endcase
end

Implementation Tips

FFT Word Length

  • Use fixed-point (e.g., Q1.15) to save logic

  • Scale down intermediate results to avoid overflow

Clock Domains

  • Use clock domain crossing if ADC and FFT run at different rates

  • FIFO buffer is helpful

UART Format

  • Send 8-bit or 16-bit FFT magnitudes

  • Add framing (start/stop byte) to avoid misalignment


Example Output (UART Stream)

css

[0xAA] [mag_bin0_H] [mag_bin0_L] ... [mag_bin63_H] [mag_bin63_L] [0x55]

PC-side Visualization (Python + PySerial)

python

import serial
import matplotlib.pyplot as plt

ser = serial.Serial('COM3', 115200)
while True:
    if ser.read() == b'\xAA':
        data = ser.read(128)
        mags = [int.from_bytes(data[i:i+2], 'big') for i in range(0, 128, 2)]
        plt.plot(mags)
        plt.pause(0.01)

Summary

BlockDescription
ADCCaptures analog input
FFT CoreComputes frequency spectrum
UART TxStreams result to PC
PC SoftwareDisplays real-time FFT graph
0
Subscribe to my newsletter

Read articles from ampheo directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

ampheo
ampheo