10 SPI Diagnostic Programs For all your Diagnostic Needs!

We collect up Claude Opus 4.6 prompt results to create 10 example SPI programs for your benefit!

10 SPI Diagnostic Programs For all your Diagnostic Needs!

We had Claude Opus 4.6 write these programs, and here they are for your benefit!

  • These programs have not been human reviewed. At least we are honest. You will expect about 98% working boilerplate code, but typically we find that the pinouts might be off reference etc.  So DYOD (Do Your Own Diligence)
  • LLM's are GREAT for creating example documentation!

10 C Programs for SPI Diagnostics

Below are 10 comprehensive SPI diagnostic programs covering loopback testing, data integrity, timing, error detection, and more. Most use the Linux spidev interface (/dev/spidevX.Y).


Table of Contents

# Program Purpose
1 SPI Loopback Test Verify MOSI↔MISO with loopback wire
2 SPI Register Read/Write Read/write device registers
3 SPI Clock Frequency Sweep Test multiple SPI clock speeds
4 SPI Data Integrity (CRC) Validate data with CRC-8
5 SPI Slave Device Scanner Detect devices on multiple chip-selects
6 SPI Bus Stress Test Sustained high-throughput transfer
7 SPI Timing & Latency Analyzer Measure round-trip latency
8 SPI Bit Error Rate Test (BERT) Calculate BER over large transfers
9 SPI Multi-Slave Diagnostic Communicate with multiple slaves
10 SPI Bus Protocol Logger Capture and log SPI transactions

Program 1: SPI Loopback Test

Connect MOSI to MISO physically. Sends known patterns and verifies they return correctly.
/*
 * spi_loopback_test.c
 * SPI Loopback Diagnostic - Connect MOSI to MISO
 * Compile: gcc -o spi_loopback spi_loopback_test.c
 * Run:     sudo ./spi_loopback /dev/spidev0.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define MAX_BUF_SIZE 256

/* Test patterns */
static const uint8_t test_patterns[][8] = {
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},  /* All zeros    */
    {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  /* All ones     */
    {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},  /* Alternating  */
    {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55},  /* Alternating  */
    {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80},  /* Walking ones */
    {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F},  /* Walking zeros*/
    {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE},  /* Random data  */
    {0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF},  /* Byte toggle  */
};

static const char *pattern_names[] = {
    "All Zeros (0x00)",
    "All Ones (0xFF)",
    "Alternating (0xAA)",
    "Alternating (0x55)",
    "Walking Ones",
    "Walking Zeros",
    "Random Data (DEADBEEF...)",
    "Byte Toggle (0x00/0xFF)",
};

#define NUM_PATTERNS (sizeof(test_patterns) / sizeof(test_patterns[0]))

static int spi_transfer(int fd, const uint8_t *tx, uint8_t *rx, size_t len, uint32_t speed)
{
    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = len,
        .speed_hz      = speed,
        .delay_usecs   = 0,
        .bits_per_word = 8,
        .cs_change     = 0,
    };

    return ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
}

static void print_buffer(const char *label, const uint8_t *buf, size_t len)
{
    printf("  %s: ", label);
    for (size_t i = 0; i < len; i++)
        printf("0x%02X ", buf[i]);
    printf("\n");
}

int main(int argc, char *argv[])
{
    const char *device = "/dev/spidev0.0";
    uint32_t speed     = 1000000;  /* 1 MHz */
    uint8_t mode       = SPI_MODE_0;
    uint8_t bits       = 8;
    int fd, ret;
    int pass_count = 0, fail_count = 0;

    if (argc > 1) device = argv[1];
    if (argc > 2) speed  = (uint32_t)atoi(argv[2]);

    printf("==============================================\n");
    printf("   SPI LOOPBACK DIAGNOSTIC TEST\n");
    printf("==============================================\n");
    printf("Device : %s\n", device);
    printf("Speed  : %u Hz\n", speed);
    printf("Mode   : SPI_MODE_0\n");
    printf("Bits   : %u\n", bits);
    printf("----------------------------------------------\n\n");

    fd = open(device, O_RDWR);
    if (fd < 0) {
        perror("ERROR: Cannot open SPI device");
        return EXIT_FAILURE;
    }

    /* Configure SPI */
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret < 0) { perror("Cannot set SPI mode"); close(fd); return EXIT_FAILURE; }

    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret < 0) { perror("Cannot set bits per word"); close(fd); return EXIT_FAILURE; }

    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret < 0) { perror("Cannot set max speed"); close(fd); return EXIT_FAILURE; }

    /* Run each test pattern */
    for (size_t p = 0; p < NUM_PATTERNS; p++) {
        uint8_t rx_buf[8] = {0};
        size_t len = sizeof(test_patterns[p]);

        printf("Test %zu: %s\n", p + 1, pattern_names[p]);

        ret = spi_transfer(fd, test_patterns[p], rx_buf, len, speed);
        if (ret < 0) {
            perror("  SPI transfer failed");
            fail_count++;
            continue;
        }

        print_buffer("TX", test_patterns[p], len);
        print_buffer("RX", rx_buf, len);

        if (memcmp(test_patterns[p], rx_buf, len) == 0) {
            printf("  Result: *** PASS ***\n\n");
            pass_count++;
        } else {
            printf("  Result: *** FAIL ***\n");
            /* Show mismatched bytes */
            for (size_t i = 0; i < len; i++) {
                if (test_patterns[p][i] != rx_buf[i])
                    printf("    Byte[%zu]: expected 0x%02X, got 0x%02X\n",
                           i, test_patterns[p][i], rx_buf[i]);
            }
            printf("\n");
            fail_count++;
        }
    }

    /* Incremental length test */
    printf("Test %d: Incremental Length (1-%d bytes)\n", (int)NUM_PATTERNS + 1, MAX_BUF_SIZE);
    {
        uint8_t tx_buf[MAX_BUF_SIZE], rx_buf[MAX_BUF_SIZE];
        int length_fail = 0;

        for (int len = 1; len <= MAX_BUF_SIZE; len++) {
            for (int i = 0; i < len; i++)
                tx_buf[i] = (uint8_t)(i & 0xFF);
            memset(rx_buf, 0, sizeof(rx_buf));

            ret = spi_transfer(fd, tx_buf, rx_buf, len, speed);
            if (ret < 0 || memcmp(tx_buf, rx_buf, len) != 0) {
                printf("  Length %d: FAIL\n", len);
                length_fail++;
            }
        }

        if (length_fail == 0) {
            printf("  All lengths 1-%d: *** PASS ***\n\n", MAX_BUF_SIZE);
            pass_count++;
        } else {
            printf("  %d length(s) failed: *** FAIL ***\n\n", length_fail);
            fail_count++;
        }
    }

    /* Summary */
    printf("==============================================\n");
    printf("   LOOPBACK TEST SUMMARY\n");
    printf("==============================================\n");
    printf("Passed: %d\n", pass_count);
    printf("Failed: %d\n", fail_count);
    printf("Result: %s\n", (fail_count == 0) ? "ALL TESTS PASSED" : "SOME TESTS FAILED");
    printf("==============================================\n");

    close(fd);
    return (fail_count == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 2: SPI Register Read/Write Diagnostic

Reads and writes to device registers (common for sensors, EEPROMs, etc.)
/*
 * spi_register_rw.c
 * SPI Register Read/Write Diagnostic
 * Compile: gcc -o spi_reg_rw spi_register_rw.c
 * Run:     sudo ./spi_reg_rw /dev/spidev0.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define SPI_SPEED       1000000
#define SPI_BITS        8

/* Common register operations (adjust for your device) */
#define REG_READ_CMD    0x80    /* Bit 7 = 1 for read (common convention) */
#define REG_WRITE_CMD   0x00    /* Bit 7 = 0 for write */

/* Example register map (adjust for your target device) */
typedef struct {
    uint8_t     addr;
    const char *name;
    uint8_t     expected;       /* Expected default value, 0xFF = don't check */
    uint8_t     writable;       /* 1 = writable, 0 = read-only */
} reg_entry_t;

static const reg_entry_t test_registers[] = {
    { 0x00, "WHO_AM_I",        0x68, 0 },  /* Example: MPU-6050 */
    { 0x01, "CONFIG_REG_1",    0xFF, 1 },  /* Writable register  */
    { 0x02, "CONFIG_REG_2",    0xFF, 1 },
    { 0x0D, "STATUS_REG",      0xFF, 0 },  /* Read-only          */
    { 0x19, "SAMPLE_RATE_DIV", 0xFF, 1 },
    { 0x1A, "CONFIG",          0xFF, 1 },
    { 0x6B, "PWR_MGMT_1",     0xFF, 1 },
};

#define NUM_REGISTERS (sizeof(test_registers) / sizeof(test_registers[0]))

static int spi_open_configure(const char *device, uint32_t speed)
{
    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return -1; }

    uint8_t mode = SPI_MODE_0;
    uint8_t bits = SPI_BITS;

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    return fd;
}

static int spi_read_register(int fd, uint8_t reg, uint8_t *value)
{
    uint8_t tx[2] = { reg | REG_READ_CMD, 0x00 };
    uint8_t rx[2] = { 0 };

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = 2,
        .speed_hz      = SPI_SPEED,
        .bits_per_word = SPI_BITS,
    };

    int ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 0) return ret;

    *value = rx[1];
    return 0;
}

static int spi_write_register(int fd, uint8_t reg, uint8_t value)
{
    uint8_t tx[2] = { reg | REG_WRITE_CMD, value };
    uint8_t rx[2] = { 0 };

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = 2,
        .speed_hz      = SPI_SPEED,
        .bits_per_word = SPI_BITS,
    };

    return ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
}

int main(int argc, char *argv[])
{
    const char *device = (argc > 1) ? argv[1] : "/dev/spidev0.0";
    int pass = 0, fail = 0, skip = 0;

    printf("==============================================\n");
    printf("   SPI REGISTER READ/WRITE DIAGNOSTIC\n");
    printf("==============================================\n");
    printf("Device: %s\n\n", device);

    int fd = spi_open_configure(device, SPI_SPEED);
    if (fd < 0) return EXIT_FAILURE;

    /* ---- Test 1: Register Read ---- */
    printf("[Test 1] Register Read Verification\n");
    printf("%-6s %-20s %-10s %-10s %-8s\n",
           "Addr", "Name", "Expected", "Read", "Status");
    printf("--------------------------------------------------------------\n");

    for (size_t i = 0; i < NUM_REGISTERS; i++) {
        uint8_t val = 0;
        int ret = spi_read_register(fd, test_registers[i].addr, &val);

        if (ret < 0) {
            printf("0x%02X   %-20s %-10s %-10s FAIL (ioctl)\n",
                   test_registers[i].addr, test_registers[i].name, "-", "-");
            fail++;
            continue;
        }

        if (test_registers[i].expected == 0xFF) {
            printf("0x%02X   %-20s %-10s 0x%02X       SKIP\n",
                   test_registers[i].addr, test_registers[i].name, "N/A", val);
            skip++;
        } else if (val == test_registers[i].expected) {
            printf("0x%02X   %-20s 0x%02X       0x%02X       PASS\n",
                   test_registers[i].addr, test_registers[i].name,
                   test_registers[i].expected, val);
            pass++;
        } else {
            printf("0x%02X   %-20s 0x%02X       0x%02X       FAIL\n",
                   test_registers[i].addr, test_registers[i].name,
                   test_registers[i].expected, val);
            fail++;
        }
    }

    /* ---- Test 2: Register Write-Read-Back ---- */
    printf("\n[Test 2] Write-Read-Back Verification\n");
    printf("%-6s %-20s %-10s %-10s %-8s\n",
           "Addr", "Name", "Written", "ReadBack", "Status");
    printf("--------------------------------------------------------------\n");

    uint8_t test_values[] = { 0xA5, 0x5A, 0x0F, 0xF0 };
    int num_test_vals = sizeof(test_values) / sizeof(test_values[0]);

    for (size_t i = 0; i < NUM_REGISTERS; i++) {
        if (!test_registers[i].writable) {
            printf("0x%02X   %-20s %-10s %-10s SKIP (R/O)\n",
                   test_registers[i].addr, test_registers[i].name, "-", "-");
            skip++;
            continue;
        }

        /* Save original value */
        uint8_t original = 0;
        spi_read_register(fd, test_registers[i].addr, &original);

        int reg_pass = 1;
        for (int v = 0; v < num_test_vals; v++) {
            spi_write_register(fd, test_registers[i].addr, test_values[v]);
            usleep(1000);  /* Short delay for write to settle */

            uint8_t readback = 0;
            spi_read_register(fd, test_registers[i].addr, &readback);

            if (readback != test_values[v]) {
                printf("0x%02X   %-20s 0x%02X       0x%02X       FAIL\n",
                       test_registers[i].addr, test_registers[i].name,
                       test_values[v], readback);
                reg_pass = 0;
                break;
            }
        }

        if (reg_pass) {
            printf("0x%02X   %-20s %-10s %-10s PASS\n",
                   test_registers[i].addr, test_registers[i].name,
                   "all vals", "match");
            pass++;
        } else {
            fail++;
        }

        /* Restore original value */
        spi_write_register(fd, test_registers[i].addr, original);
    }

    /* ---- Test 3: Read Consistency ---- */
    printf("\n[Test 3] Read Consistency (100 consecutive reads)\n");
    for (size_t i = 0; i < NUM_REGISTERS; i++) {
        uint8_t first_val = 0, val = 0;
        int consistent = 1;

        spi_read_register(fd, test_registers[i].addr, &first_val);
        for (int r = 0; r < 100; r++) {
            spi_read_register(fd, test_registers[i].addr, &val);
            if (val != first_val && !test_registers[i].writable) {
                /* Status registers may change - only flag config regs */
                consistent = 0;
                break;
            }
        }

        printf("  Reg 0x%02X %-20s: %s\n",
               test_registers[i].addr, test_registers[i].name,
               consistent ? "CONSISTENT" : "INCONSISTENT (may be normal for status regs)");
    }

    /* Summary */
    printf("\n==============================================\n");
    printf("   REGISTER DIAGNOSTIC SUMMARY\n");
    printf("==============================================\n");
    printf("Passed : %d\n", pass);
    printf("Failed : %d\n", fail);
    printf("Skipped: %d\n", skip);
    printf("Result : %s\n", (fail == 0) ? "PASS" : "FAIL");
    printf("==============================================\n");

    close(fd);
    return (fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 3: SPI Clock Frequency Sweep

Tests SPI communication at various clock speeds to find reliable operating range.
/*
 * spi_clock_sweep.c
 * SPI Clock Frequency Sweep Diagnostic
 * Compile: gcc -o spi_clock_sweep spi_clock_sweep.c
 * Run:     sudo ./spi_clock_sweep /dev/spidev0.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define TEST_SIZE       256
#define ITERATIONS      100

/* Frequencies to test (Hz) */
static const uint32_t test_frequencies[] = {
    100000,         /*  100 kHz */
    250000,         /*  250 kHz */
    500000,         /*  500 kHz */
    1000000,        /*    1 MHz */
    2000000,        /*    2 MHz */
    4000000,        /*    4 MHz */
    8000000,        /*    8 MHz */
    10000000,       /*   10 MHz */
    16000000,       /*   16 MHz */
    20000000,       /*   20 MHz */
    25000000,       /*   25 MHz */
    33000000,       /*   33 MHz */
    40000000,       /*   40 MHz */
    50000000,       /*   50 MHz */
};

#define NUM_FREQS   (sizeof(test_frequencies) / sizeof(test_frequencies[0]))

static const char *format_freq(uint32_t hz)
{
    static char buf[32];
    if (hz >= 1000000)
        snprintf(buf, sizeof(buf), "%u.%u MHz", hz / 1000000, (hz % 1000000) / 100000);
    else
        snprintf(buf, sizeof(buf), "%u kHz", hz / 1000);
    return buf;
}

static double get_time_ms(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6;
}

static int spi_loopback_test(int fd, uint32_t speed, int iterations,
                              int *errors_out, double *throughput_mbps,
                              double *avg_latency_us)
{
    uint8_t tx_buf[TEST_SIZE], rx_buf[TEST_SIZE];
    int total_errors = 0;
    double total_time = 0;

    /* Fill TX buffer with known pattern */
    for (int i = 0; i < TEST_SIZE; i++)
        tx_buf[i] = (uint8_t)((i * 7 + 13) & 0xFF);

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx_buf,
        .rx_buf        = (unsigned long)rx_buf,
        .len           = TEST_SIZE,
        .speed_hz      = speed,
        .bits_per_word = 8,
    };

    /* Set speed */
    if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0)
        return -1;

    for (int iter = 0; iter < iterations; iter++) {
        memset(rx_buf, 0, TEST_SIZE);

        double start = get_time_ms();
        int ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        double end = get_time_ms();

        if (ret < 0) return -1;

        total_time += (end - start);

        /* Count byte errors */
        for (int i = 0; i < TEST_SIZE; i++) {
            if (rx_buf[i] != tx_buf[i])
                total_errors++;
        }
    }

    *errors_out     = total_errors;
    *avg_latency_us = (total_time / iterations) * 1000.0;  /* ms -> us */

    /* Throughput: total bytes / total time */
    double total_bytes = (double)TEST_SIZE * iterations;
    double total_secs  = total_time / 1000.0;
    *throughput_mbps   = (total_bytes * 8.0) / (total_secs * 1e6);

    return 0;
}

int main(int argc, char *argv[])
{
    const char *device = (argc > 1) ? argv[1] : "/dev/spidev0.0";
    uint8_t mode = SPI_MODE_0;
    uint8_t bits = 8;

    printf("==============================================\n");
    printf("   SPI CLOCK FREQUENCY SWEEP DIAGNOSTIC\n");
    printf("==============================================\n");
    printf("Device     : %s\n", device);
    printf("Test Size  : %d bytes\n", TEST_SIZE);
    printf("Iterations : %d per frequency\n", ITERATIONS);
    printf("Note       : Connect MOSI->MISO for loopback\n");
    printf("==============================================\n\n");

    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return EXIT_FAILURE; }

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

    printf("%-14s | %-8s | %-14s | %-14s | %-6s\n",
           "Frequency", "Errors", "Throughput", "Avg Latency", "Status");
    printf("------------------------------------------------------------------------\n");

    int total_pass = 0, total_fail = 0, total_skip = 0;

    for (size_t f = 0; f < NUM_FREQS; f++) {
        int errors = 0;
        double throughput = 0, latency = 0;

        int ret = spi_loopback_test(fd, test_frequencies[f], ITERATIONS,
                                     &errors, &throughput, &latency);

        if (ret < 0) {
            printf("%-14s | %-8s | %-14s | %-14s | SKIP\n",
                   format_freq(test_frequencies[f]), "-", "-", "-");
            total_skip++;
            continue;
        }

        const char *status = (errors == 0) ? "PASS" : "FAIL";
        if (errors == 0) total_pass++; else total_fail++;

        printf("%-14s | %-8d | %8.2f Mbps  | %10.1f us  | %s\n",
               format_freq(test_frequencies[f]),
               errors, throughput, latency, status);
    }

    /* Mode sweep at best frequency */
    printf("\n--- SPI Mode Sweep at 1 MHz ---\n");
    printf("%-12s | %-6s | %-8s\n", "Mode", "CPOL/CPHA", "Status");
    printf("--------------------------------------\n");

    uint32_t test_speed = 1000000;
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &test_speed);

    for (uint8_t m = 0; m <= 3; m++) {
        ioctl(fd, SPI_IOC_WR_MODE, &m);

        int errors = 0;
        double throughput = 0, latency = 0;
        int ret = spi_loopback_test(fd, test_speed, 10, &errors, &throughput, &latency);

        printf("SPI_MODE_%d   | %d / %d     | %s\n",
               m, (m >> 1) & 1, m & 1,
               (ret < 0) ? "ERROR" : (errors == 0 ? "PASS" : "FAIL"));
    }

    printf("\n==============================================\n");
    printf("   FREQUENCY SWEEP SUMMARY\n");
    printf("==============================================\n");
    printf("Passed : %d\n", total_pass);
    printf("Failed : %d\n", total_fail);
    printf("Skipped: %d\n", total_skip);
    printf("==============================================\n");

    close(fd);
    return (total_fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 4: SPI Data Integrity with CRC-8

Sends data with embedded CRC-8 checksums and validates on receive.
/*
 * spi_crc_integrity.c
 * SPI Data Integrity Diagnostic with CRC-8
 * Compile: gcc -o spi_crc spi_crc_integrity.c
 * Run:     sudo ./spi_crc /dev/spidev0.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define PAYLOAD_SIZE    255     /* Max payload per packet */
#define PACKET_SIZE     (PAYLOAD_SIZE + 1)  /* +1 for CRC */
#define NUM_PACKETS     1000
#define SPI_SPEED       1000000

/* CRC-8 polynomial: x^8 + x^2 + x + 1 (0x07) */
static uint8_t crc8_table[256];

static void crc8_init(void)
{
    for (int i = 0; i < 256; i++) {
        uint8_t crc = (uint8_t)i;
        for (int bit = 0; bit < 8; bit++) {
            if (crc & 0x80)
                crc = (crc << 1) ^ 0x07;
            else
                crc <<= 1;
        }
        crc8_table[i] = crc;
    }
}

static uint8_t crc8_compute(const uint8_t *data, size_t len)
{
    uint8_t crc = 0x00;
    for (size_t i = 0; i < len; i++)
        crc = crc8_table[crc ^ data[i]];
    return crc;
}

static int spi_transfer(int fd, const uint8_t *tx, uint8_t *rx, size_t len)
{
    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = len,
        .speed_hz      = SPI_SPEED,
        .bits_per_word = 8,
    };
    return ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
}

typedef struct {
    int total_packets;
    int crc_pass;
    int crc_fail;
    int transfer_errors;
    int byte_errors;
    int total_bytes;
} diag_stats_t;

static void run_pattern_test(int fd, const char *name,
                              void (*fill_fn)(uint8_t *, size_t, int),
                              diag_stats_t *stats)
{
    uint8_t tx_buf[PACKET_SIZE], rx_buf[PACKET_SIZE];

    printf("\n  Pattern: %s\n", name);

    int local_pass = 0, local_fail = 0;

    for (int pkt = 0; pkt < NUM_PACKETS; pkt++) {
        /* Fill payload */
        fill_fn(tx_buf, PAYLOAD_SIZE, pkt);

        /* Append CRC */
        tx_buf[PAYLOAD_SIZE] = crc8_compute(tx_buf, PAYLOAD_SIZE);

        memset(rx_buf, 0, PACKET_SIZE);

        int ret = spi_transfer(fd, tx_buf, rx_buf, PACKET_SIZE);
        if (ret < 0) {
            stats->transfer_errors++;
            continue;
        }

        stats->total_packets++;
        stats->total_bytes += PAYLOAD_SIZE;

        /* Verify CRC on received data */
        uint8_t rx_crc = crc8_compute(rx_buf, PAYLOAD_SIZE);

        if (rx_crc == rx_buf[PAYLOAD_SIZE] &&
            memcmp(tx_buf, rx_buf, PACKET_SIZE) == 0) {
            stats->crc_pass++;
            local_pass++;
        } else {
            stats->crc_fail++;
            local_fail++;

            /* Count individual byte errors */
            for (int i = 0; i < PAYLOAD_SIZE; i++) {
                if (tx_buf[i] != rx_buf[i])
                    stats->byte_errors++;
            }
        }
    }

    printf("    Sent: %d, Pass: %d, Fail: %d\n",
           NUM_PACKETS, local_pass, local_fail);
}

/* Pattern fill functions */
static void fill_sequential(uint8_t *buf, size_t len, int seed)
{
    for (size_t i = 0; i < len; i++)
        buf[i] = (uint8_t)((seed + i) & 0xFF);
}

static void fill_random(uint8_t *buf, size_t len, int seed)
{
    (void)seed;
    for (size_t i = 0; i < len; i++)
        buf[i] = (uint8_t)(rand() & 0xFF);
}

static void fill_alternating(uint8_t *buf, size_t len, int seed)
{
    (void)seed;
    for (size_t i = 0; i < len; i++)
        buf[i] = (i % 2) ? 0xAA : 0x55;
}

static void fill_stress(uint8_t *buf, size_t len, int seed)
{
    /* Bit-transition-heavy pattern */
    for (size_t i = 0; i < len; i++)
        buf[i] = (uint8_t)((seed * 179 + i * 37) & 0xFF);
}

int main(int argc, char *argv[])
{
    const char *device = (argc > 1) ? argv[1] : "/dev/spidev0.0";

    crc8_init();
    srand((unsigned)time(NULL));

    printf("==============================================\n");
    printf("   SPI DATA INTEGRITY (CRC-8) DIAGNOSTIC\n");
    printf("==============================================\n");
    printf("Device      : %s\n", device);
    printf("Payload Size: %d bytes\n", PAYLOAD_SIZE);
    printf("Packets/Test: %d\n", NUM_PACKETS);
    printf("CRC Poly    : 0x07 (CRC-8)\n");
    printf("Requires    : MOSI->MISO loopback\n");
    printf("==============================================\n");

    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return EXIT_FAILURE; }

    uint8_t mode = SPI_MODE_0;
    uint8_t bits = 8;
    uint32_t speed = SPI_SPEED;
    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    diag_stats_t stats = {0};

    run_pattern_test(fd, "Sequential",  fill_sequential,  &stats);
    run_pattern_test(fd, "Random",      fill_random,      &stats);
    run_pattern_test(fd, "Alternating", fill_alternating,  &stats);
    run_pattern_test(fd, "Stress",      fill_stress,       &stats);

    /* Summary */
    printf("\n==============================================\n");
    printf("   CRC INTEGRITY SUMMARY\n");
    printf("==============================================\n");
    printf("Total Packets Sent    : %d\n", stats.total_packets);
    printf("CRC Pass              : %d\n", stats.crc_pass);
    printf("CRC Fail              : %d\n", stats.crc_fail);
    printf("Transfer Errors       : %d\n", stats.transfer_errors);
    printf("Byte Errors           : %d\n", stats.byte_errors);
    printf("Total Bytes Checked   : %d\n", stats.total_bytes);
    if (stats.total_bytes > 0) {
        double ber = (double)stats.byte_errors / stats.total_bytes;
        printf("Byte Error Rate       : %.2e\n", ber);
    }
    printf("Overall               : %s\n",
           (stats.crc_fail == 0 && stats.transfer_errors == 0) ? "PASS" : "FAIL");
    printf("==============================================\n");

    close(fd);
    return (stats.crc_fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 5: SPI Slave Device Scanner

Scans multiple chip-select lines and attempts WHO_AM_I / ID register reads to detect devices.
/*
 * spi_device_scanner.c
 * SPI Slave Device Scanner / Detector
 * Compile: gcc -o spi_scanner spi_device_scanner.c
 * Run:     sudo ./spi_scanner
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define MAX_BUS     4
#define MAX_CS      4
#define SPI_SPEED   500000

/* Known device IDs (WHO_AM_I register) */
typedef struct {
    uint8_t     id;
    const char *name;
} known_device_t;

static const known_device_t known_devices[] = {
    { 0x68, "MPU-6050 / MPU-6500"     },
    { 0x71, "MPU-9250"                 },
    { 0x70, "MPU-6500"                 },
    { 0x73, "ICM-20948"               },
    { 0x33, "LIS3DH"                  },
    { 0x3F, "ADXL345"                 },
    { 0xD1, "LSM6DSL"                 },
    { 0x6A, "LSM6DS3"                 },
    { 0xBB, "BMP280"                  },
    { 0x60, "BME280 / BMP280"         },
    { 0x58, "BMP180"                  },
    { 0x55, "BME680"                  },
    { 0x61, "BME680 (variant)"        },
    { 0xFC, "W25Q (Flash)"            },
    { 0xEF, "Winbond Flash"           },
    { 0x20, "Micron Flash"            },
    { 0xBF, "SST Flash"              },
};

#define NUM_KNOWN (sizeof(known_devices) / sizeof(known_devices[0]))

static const char *lookup_device(uint8_t id)
{
    for (size_t i = 0; i < NUM_KNOWN; i++) {
        if (known_devices[i].id == id)
            return known_devices[i].name;
    }
    return NULL;
}

/* Common ID register addresses to probe */
static const uint8_t id_registers[] = {
    0x0F,   /* LIS3DH, LSM6DS, etc.   */
    0x00,   /* ADXL345, some sensors   */
    0x75,   /* MPU-6050, MPU-9250      */
    0xD0,   /* BMP280, BME280          */
    0x9F,   /* SPI Flash JEDEC ID cmd  */
    0x01,   /* Generic                 */
};

#define NUM_ID_REGS (sizeof(id_registers) / sizeof(id_registers[0]))

static int try_read_id(int fd, uint8_t reg_addr, uint8_t *id_out)
{
    /* Send reg address with read bit (bit 7 = 1) + dummy byte */
    uint8_t tx[2] = { reg_addr | 0x80, 0x00 };
    uint8_t rx[2] = { 0 };

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = 2,
        .speed_hz      = SPI_SPEED,
        .bits_per_word = 8,
    };

    int ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 0) return -1;

    *id_out = rx[1];
    return 0;
}

static int probe_device(const char *dev_path)
{
    int fd = open(dev_path, O_RDWR);
    if (fd < 0) return -1;

    uint8_t mode = SPI_MODE_0;
    uint8_t bits = 8;
    uint32_t speed = SPI_SPEED;

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    int found = 0;

    for (size_t r = 0; r < NUM_ID_REGS; r++) {
        uint8_t id = 0;
        if (try_read_id(fd, id_registers[r], &id) == 0) {
            /* Filter out obviously invalid responses */
            if (id != 0x00 && id != 0xFF) {
                const char *name = lookup_device(id);
                printf("  %-24s Reg 0x%02X -> ID 0x%02X  %s%s%s\n",
                       dev_path, id_registers[r], id,
                       name ? "[" : "",
                       name ? name : "(Unknown device)",
                       name ? "]" : "");
                found = 1;
            }
        }
    }

    close(fd);
    return found;
}

int main(void)
{
    char dev_path[64];
    int devices_found = 0;

    printf("==============================================\n");
    printf("   SPI SLAVE DEVICE SCANNER\n");
    printf("==============================================\n");
    printf("Scanning /dev/spidevX.Y ...\n");
    printf("Speed: %d Hz\n\n", SPI_SPEED);

    printf("%-24s %-10s %-10s %s\n", "Device", "Reg Addr", "ID Value", "Match");
    printf("--------------------------------------------------------------\n");

    for (int bus = 0; bus < MAX_BUS; bus++) {
        for (int cs = 0; cs < MAX_CS; cs++) {
            snprintf(dev_path, sizeof(dev_path), "/dev/spidev%d.%d", bus, cs);

            if (access(dev_path, F_OK) != 0)
                continue;

            int found = probe_device(dev_path);
            if (found > 0)
                devices_found++;
            else if (found == 0)
                printf("  %-24s No device responded\n", dev_path);
        }
    }

    printf("\n==============================================\n");
    printf("   SCAN RESULTS\n");
    printf("==============================================\n");
    printf("Devices found: %d\n", devices_found);

    if (devices_found == 0)
        printf("No SPI devices detected.\n"
               "Check: wiring, device power, kernel SPI driver/overlay.\n");

    printf("==============================================\n");

    return (devices_found > 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 6: SPI Bus Stress Test

High-throughput sustained transfer test to find intermittent failures under load.
/*
 * spi_stress_test.c
 * SPI Bus Stress Test - Sustained High-Throughput
 * Compile: gcc -o spi_stress spi_stress_test.c -lrt
 * Run:     sudo ./spi_stress /dev/spidev0.0 [duration_secs]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define BUF_SIZE        4096
#define SPI_SPEED       8000000     /* 8 MHz */
#define PROGRESS_INTERVAL 5         /* Print every 5 seconds */

static volatile int running = 1;

static void sigint_handler(int sig)
{
    (void)sig;
    running = 0;
}

static double get_time_sec(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec + ts.tv_nsec / 1e9;
}

typedef struct {
    uint64_t    total_transfers;
    uint64_t    total_bytes;
    uint64_t    error_transfers;
    uint64_t    byte_mismatches;
    uint64_t    bit_errors;
    double      start_time;
    double      last_report_time;
    uint64_t    last_report_bytes;
} stress_stats_t;

static int count_bit_errors(uint8_t a, uint8_t b)
{
    uint8_t diff = a ^ b;
    int count = 0;
    while (diff) {
        count += diff & 1;
        diff >>= 1;
    }
    return count;
}

static void print_progress(stress_stats_t *s)
{
    double now     = get_time_sec();
    double elapsed = now - s->start_time;
    double dt      = now - s->last_report_time;

    double overall_mbps  = (s->total_bytes * 8.0) / (elapsed * 1e6);
    double current_mbps  = ((s->total_bytes - s->last_report_bytes) * 8.0) / (dt * 1e6);

    printf("[%7.1fs] Transfers: %lu | Bytes: %lu | "
           "Errors: %lu | BitErr: %lu | "
           "Rate: %.2f Mbps (cur: %.2f Mbps)\n",
           elapsed,
           (unsigned long)s->total_transfers,
           (unsigned long)s->total_bytes,
           (unsigned long)s->error_transfers,
           (unsigned long)s->bit_errors,
           overall_mbps, current_mbps);

    s->last_report_time  = now;
    s->last_report_bytes = s->total_bytes;
}

int main(int argc, char *argv[])
{
    const char *device   = (argc > 1) ? argv[1] : "/dev/spidev0.0";
    int duration         = (argc > 2) ? atoi(argv[2]) : 60;
    uint32_t speed       = SPI_SPEED;
    uint8_t mode         = SPI_MODE_0;
    uint8_t bits         = 8;

    printf("==============================================\n");
    printf("   SPI BUS STRESS TEST\n");
    printf("==============================================\n");
    printf("Device    : %s\n", device);
    printf("Speed     : %u Hz\n", speed);
    printf("Buffer    : %d bytes\n", BUF_SIZE);
    printf("Duration  : %d seconds (Ctrl+C to stop)\n", duration);
    printf("Requires  : MOSI->MISO loopback\n");
    printf("==============================================\n\n");

    signal(SIGINT, sigint_handler);

    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return EXIT_FAILURE; }

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    uint8_t *tx_buf = malloc(BUF_SIZE);
    uint8_t *rx_buf = malloc(BUF_SIZE);
    if (!tx_buf || !rx_buf) {
        fprintf(stderr, "Memory allocation failed\n");
        close(fd);
        return EXIT_FAILURE;
    }

    /* Generate pseudo-random TX data */
    srand(0xDEADBEEF);
    for (int i = 0; i < BUF_SIZE; i++)
        tx_buf[i] = (uint8_t)(rand() & 0xFF);

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx_buf,
        .rx_buf        = (unsigned long)rx_buf,
        .len           = BUF_SIZE,
        .speed_hz      = speed,
        .bits_per_word = 8,
    };

    stress_stats_t stats = {0};
    stats.start_time       = get_time_sec();
    stats.last_report_time = stats.start_time;

    printf("Starting stress test...\n\n");

    while (running) {
        double now = get_time_sec();
        if ((now - stats.start_time) >= duration) break;

        /* Rotate TX pattern slightly each iteration */
        uint8_t first = tx_buf[0];
        memmove(tx_buf, tx_buf + 1, BUF_SIZE - 1);
        tx_buf[BUF_SIZE - 1] = first;

        memset(rx_buf, 0, BUF_SIZE);

        int ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        stats.total_transfers++;

        if (ret < 0) {
            stats.error_transfers++;
            continue;
        }

        stats.total_bytes += BUF_SIZE;

        /* Verify */
        if (memcmp(tx_buf, rx_buf, BUF_SIZE) != 0) {
            stats.error_transfers++;
            for (int i = 0; i < BUF_SIZE; i++) {
                if (tx_buf[i] != rx_buf[i]) {
                    stats.byte_mismatches++;
                    stats.bit_errors += count_bit_errors(tx_buf[i], rx_buf[i]);
                }
            }
        }

        /* Periodic progress */
        if ((now - stats.last_report_time) >= PROGRESS_INTERVAL)
            print_progress(&stats);
    }

    double elapsed = get_time_sec() - stats.start_time;

    printf("\n==============================================\n");
    printf("   STRESS TEST SUMMARY\n");
    printf("==============================================\n");
    printf("Duration           : %.1f seconds\n", elapsed);
    printf("Total Transfers    : %lu\n", (unsigned long)stats.total_transfers);
    printf("Total Bytes        : %lu (%.2f MB)\n",
           (unsigned long)stats.total_bytes, stats.total_bytes / 1e6);
    printf("Failed Transfers   : %lu\n", (unsigned long)stats.error_transfers);
    printf("Byte Mismatches    : %lu\n", (unsigned long)stats.byte_mismatches);
    printf("Bit Errors         : %lu\n", (unsigned long)stats.bit_errors);
    printf("Avg Throughput     : %.2f Mbps\n",
           (stats.total_bytes * 8.0) / (elapsed * 1e6));
    printf("Transfers/sec      : %.0f\n", stats.total_transfers / elapsed);

    if (stats.total_bytes > 0) {
        double ber = (double)(stats.bit_errors) / (stats.total_bytes * 8.0);
        printf("Bit Error Rate     : %.2e\n", ber);
    }

    printf("Result             : %s\n",
           (stats.error_transfers == 0) ? "PASS" : "FAIL");
    printf("==============================================\n");

    free(tx_buf);
    free(rx_buf);
    close(fd);

    return (stats.error_transfers == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 7: SPI Timing & Latency Analyzer

Measures transfer latency, jitter, and timing statistics.
/*
 * spi_timing_analyzer.c
 * SPI Timing and Latency Analyzer
 * Compile: gcc -o spi_timing spi_timing_analyzer.c -lm
 * Run:     sudo ./spi_timing /dev/spidev0.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define NUM_SAMPLES     10000
#define SPI_SPEED       1000000

static double get_time_us(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1e6 + ts.tv_nsec / 1000.0;
}

typedef struct {
    double min_us;
    double max_us;
    double avg_us;
    double stddev_us;
    double median_us;
    double p95_us;
    double p99_us;
    double jitter_us;       /* max - min */
} timing_result_t;

static int cmp_double(const void *a, const void *b)
{
    double da = *(const double *)a;
    double db = *(const double *)b;
    return (da > db) - (da < db);
}

static timing_result_t analyze_timings(double *samples, int n)
{
    timing_result_t r = {0};

    qsort(samples, n, sizeof(double), cmp_double);

    r.min_us    = samples[0];
    r.max_us    = samples[n - 1];
    r.median_us = samples[n / 2];
    r.p95_us    = samples[(int)(n * 0.95)];
    r.p99_us    = samples[(int)(n * 0.99)];
    r.jitter_us = r.max_us - r.min_us;

    double sum = 0;
    for (int i = 0; i < n; i++) sum += samples[i];
    r.avg_us = sum / n;

    double var_sum = 0;
    for (int i = 0; i < n; i++) {
        double d = samples[i] - r.avg_us;
        var_sum += d * d;
    }
    r.stddev_us = sqrt(var_sum / n);

    return r;
}

static void print_result(const char *label, timing_result_t *r)
{
    printf("  %-30s\n", label);
    printf("    Min          : %10.2f us\n", r->min_us);
    printf("    Max          : %10.2f us\n", r->max_us);
    printf("    Average      : %10.2f us\n", r->avg_us);
    printf("    Median       : %10.2f us\n", r->median_us);
    printf("    Std Dev      : %10.2f us\n", r->stddev_us);
    printf("    95th %%ile    : %10.2f us\n", r->p95_us);
    printf("    99th %%ile    : %10.2f us\n", r->p99_us);
    printf("    Jitter (p-p) : %10.2f us\n\n", r->jitter_us);
}

static void print_histogram(double *samples, int n, int bins)
{
    double min_val = samples[0];    /* already sorted */
    double max_val = samples[n - 1];
    double range   = max_val - min_val;

    if (range < 1e-6) {
        printf("  [All samples identical: %.2f us]\n\n", min_val);
        return;
    }

    double bin_width = range / bins;
    int *counts = calloc(bins, sizeof(int));

    for (int i = 0; i < n; i++) {
        int b = (int)((samples[i] - min_val) / bin_width);
        if (b >= bins) b = bins - 1;
        counts[b]++;
    }

    int max_count = 0;
    for (int b = 0; b < bins; b++)
        if (counts[b] > max_count) max_count = counts[b];

    printf("  Latency Distribution:\n");
    for (int b = 0; b < bins; b++) {
        double lo = min_val + b * bin_width;
        double hi = lo + bin_width;
        int bar_len = (max_count > 0) ? (counts[b] * 40 / max_count) : 0;

        printf("  %7.1f-%7.1f us |", lo, hi);
        for (int j = 0; j < bar_len; j++) printf("#");
        printf(" %d\n", counts[b]);
    }
    printf("\n");

    free(counts);
}

int main(int argc, char *argv[])
{
    const char *device = (argc > 1) ? argv[1] : "/dev/spidev0.0";
    uint32_t speed = SPI_SPEED;
    uint8_t mode = SPI_MODE_0, bits = 8;

    printf("==============================================\n");
    printf("   SPI TIMING & LATENCY ANALYZER\n");
    printf("==============================================\n");
    printf("Device  : %s\n", device);
    printf("Speed   : %u Hz\n", speed);
    printf("Samples : %d\n", NUM_SAMPLES);
    printf("==============================================\n\n");

    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return EXIT_FAILURE; }

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    double *latencies = malloc(NUM_SAMPLES * sizeof(double));
    if (!latencies) { perror("malloc"); close(fd); return EXIT_FAILURE; }

    /* Test different transfer sizes */
    int test_sizes[] = { 1, 4, 16, 64, 256, 1024, 4096 };
    int num_sizes = sizeof(test_sizes) / sizeof(test_sizes[0]);

    printf("%-10s | %-10s | %-10s | %-10s | %-10s | %-10s\n",
           "Size", "Min(us)", "Avg(us)", "Max(us)", "Jitter", "StdDev");
    printf("----------------------------------------------------------------------\n");

    for (int s = 0; s < num_sizes; s++) {
        int size = test_sizes[s];
        uint8_t *tx = calloc(size, 1);
        uint8_t *rx = calloc(size, 1);

        for (int i = 0; i < size; i++) tx[i] = (uint8_t)(i & 0xFF);

        struct spi_ioc_transfer tr = {
            .tx_buf        = (unsigned long)tx,
            .rx_buf        = (unsigned long)rx,
            .len           = size,
            .speed_hz      = speed,
            .bits_per_word = 8,
        };

        /* Warm up */
        for (int w = 0; w < 100; w++)
            ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

        /* Measure */
        for (int i = 0; i < NUM_SAMPLES; i++) {
            double t0 = get_time_us();
            ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
            double t1 = get_time_us();
            latencies[i] = t1 - t0;
        }

        timing_result_t r = analyze_timings(latencies, NUM_SAMPLES);

        printf("%-10d | %10.2f | %10.2f | %10.2f | %10.2f | %10.2f\n",
               size, r.min_us, r.avg_us, r.max_us, r.jitter_us, r.stddev_us);

        free(tx);
        free(rx);
    }

    /* Detailed analysis for 64-byte transfers */
    printf("\n--- Detailed Analysis (64 bytes) ---\n\n");
    {
        uint8_t tx[64], rx[64];
        memset(tx, 0xA5, 64);

        struct spi_ioc_transfer tr = {
            .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx,
            .len = 64, .speed_hz = speed, .bits_per_word = 8,
        };

        for (int i = 0; i < NUM_SAMPLES; i++) {
            double t0 = get_time_us();
            ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
            double t1 = get_time_us();
            latencies[i] = t1 - t0;
        }

        timing_result_t r = analyze_timings(latencies, NUM_SAMPLES);
        print_result("64-Byte Transfer Latency", &r);

        /* Already sorted by analyze_timings */
        print_histogram(latencies, NUM_SAMPLES, 20);
    }

    /* Inter-transfer gap analysis */
    printf("--- Inter-Transfer Gap Analysis ---\n\n");
    {
        uint8_t tx[1] = {0}, rx[1] = {0};
        struct spi_ioc_transfer tr = {
            .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx,
            .len = 1, .speed_hz = speed, .bits_per_word = 8,
        };

        double timestamps[NUM_SAMPLES];
        for (int i = 0; i < NUM_SAMPLES; i++) {
            timestamps[i] = get_time_us();
            ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        }

        double *gaps = malloc((NUM_SAMPLES - 1) * sizeof(double));
        for (int i = 0; i < NUM_SAMPLES - 1; i++)
            gaps[i] = timestamps[i + 1] - timestamps[i];

        timing_result_t r = analyze_timings(gaps, NUM_SAMPLES - 1);
        print_result("Inter-Transfer Gap", &r);

        free(gaps);
    }

    printf("==============================================\n");
    printf("   TIMING ANALYSIS COMPLETE\n");
    printf("==============================================\n");

    free(latencies);
    close(fd);
    return EXIT_SUCCESS;
}

Program 8: SPI Bit Error Rate Test (BERT)

Comprehensive BERT with statistical confidence calculations.
/*
 * spi_bert.c
 * SPI Bit Error Rate Test (BERT)
 * Compile: gcc -o spi_bert spi_bert.c -lm
 * Run:     sudo ./spi_bert /dev/spidev0.0 [target_ber_exponent]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define BUF_SIZE        4096
#define SPI_SPEED       8000000
#define REPORT_INTERVAL 10      /* seconds */

static volatile int g_running = 1;

void handle_signal(int sig) { (void)sig; g_running = 0; }

static double time_sec(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec + ts.tv_nsec / 1e9;
}

/* Count bits set in a byte (Hamming weight) */
static inline int popcount8(uint8_t v)
{
    int c = 0;
    while (v) { c += v & 1; v >>= 1; }
    return c;
}

/* PRBS-7 generator for deterministic pseudo-random data */
static void prbs7_generate(uint8_t *buf, size_t len, uint8_t *state)
{
    uint8_t s = *state;
    for (size_t i = 0; i < len; i++) {
        uint8_t byte = 0;
        for (int bit = 0; bit < 8; bit++) {
            uint8_t new_bit = ((s >> 6) ^ (s >> 5)) & 1;
            s = ((s << 1) | new_bit) & 0x7F;
            byte = (byte << 1) | new_bit;
        }
        buf[i] = byte;
    }
    *state = s;
}

typedef struct {
    uint64_t total_bits;
    uint64_t error_bits;
    uint64_t total_bytes;
    uint64_t error_bytes;
    uint64_t total_transfers;
    uint64_t error_transfers;
    /* Per-bit-position error count */
    uint64_t bit_position_errors[8];
    /* Error distribution per transfer */
    uint64_t error_free_transfers;
    double   start_time;
} bert_stats_t;

static void bert_print_report(bert_stats_t *s)
{
    double elapsed = time_sec() - s->start_time;
    double ber = (s->total_bits > 0)
                 ? (double)s->error_bits / s->total_bits
                 : 0.0;

    printf("\n--- BERT Report @ %.0fs ---\n", elapsed);
    printf("Total Bits Tested  : %lu\n", (unsigned long)s->total_bits);
    printf("Bit Errors         : %lu\n", (unsigned long)s->error_bits);
    printf("Bit Error Rate     : %.3e\n", ber);
    printf("Byte Errors        : %lu / %lu\n",
           (unsigned long)s->error_bytes, (unsigned long)s->total_bytes);
    printf("Transfer Errors    : %lu / %lu\n",
           (unsigned long)s->error_transfers, (unsigned long)s->total_transfers);
    printf("Error-Free Xfers   : %lu (%.2f%%)\n",
           (unsigned long)s->error_free_transfers,
           s->total_transfers > 0
               ? 100.0 * s->error_free_transfers / s->total_transfers
               : 0.0);

    /* Bit position analysis */
    printf("Bit Position Errors: ");
    for (int b = 7; b >= 0; b--)
        printf("b%d:%lu ", b, (unsigned long)s->bit_position_errors[b]);
    printf("\n");

    /* Confidence level for BER estimate */
    if (s->total_bits > 0 && s->error_bits > 0) {
        /* Simple confidence: need ~20/BER bits for 95% confidence */
        double needed_for_95 = 20.0 / ber;
        double confidence = fmin(100.0, 100.0 * s->total_bits / needed_for_95);
        printf("Est. Confidence    : %.1f%%\n", confidence);
    }
}

int main(int argc, char *argv[])
{
    const char *device      = (argc > 1) ? argv[1] : "/dev/spidev0.0";
    int target_ber_exp      = (argc > 2) ? atoi(argv[2]) : -9;
    double target_ber       = pow(10.0, target_ber_exp);
    /* Need ~20/target_ber bits for 95% confidence */
    double bits_needed      = 20.0 / target_ber;

    uint8_t mode = SPI_MODE_0, bpw = 8;
    uint32_t speed = SPI_SPEED;

    printf("==============================================\n");
    printf("   SPI BIT ERROR RATE TEST (BERT)\n");
    printf("==============================================\n");
    printf("Device          : %s\n", device);
    printf("Speed           : %u Hz\n", speed);
    printf("Buffer Size     : %d bytes\n", BUF_SIZE);
    printf("Target BER      : 1e%d\n", target_ber_exp);
    printf("Bits Needed (95%%): %.2e\n", bits_needed);
    printf("Requires        : MOSI->MISO loopback\n");
    printf("Press Ctrl+C to stop early\n");
    printf("==============================================\n\n");

    signal(SIGINT, handle_signal);

    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return EXIT_FAILURE; }

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bpw);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    uint8_t *tx_buf = malloc(BUF_SIZE);
    uint8_t *rx_buf = malloc(BUF_SIZE);
    if (!tx_buf || !rx_buf) {
        fprintf(stderr, "malloc failed\n");
        close(fd);
        return EXIT_FAILURE;
    }

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx_buf,
        .rx_buf        = (unsigned long)rx_buf,
        .len           = BUF_SIZE,
        .speed_hz      = speed,
        .bits_per_word = 8,
    };

    bert_stats_t stats = {0};
    stats.start_time = time_sec();
    double last_report = stats.start_time;

    uint8_t prbs_state = 0x7F;  /* PRBS seed */

    printf("Running BERT...\n");

    while (g_running) {
        /* Check if we have enough bits */
        if (stats.total_bits >= (uint64_t)bits_needed && stats.total_bits > 0) {
            printf("\nReached target bit count for 95%% confidence.\n");
            break;
        }

        /* Generate PRBS data */
        uint8_t tx_prbs_state = prbs_state;
        prbs7_generate(tx_buf, BUF_SIZE, &tx_prbs_state);

        memset(rx_buf, 0, BUF_SIZE);

        int ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        stats.total_transfers++;

        if (ret < 0) {
            stats.error_transfers++;
            continue;
        }

        /* Reset PRBS for verification (regenerate same sequence) */
        uint8_t verify_state = prbs_state;
        uint8_t verify_buf[BUF_SIZE];
        prbs7_generate(verify_buf, BUF_SIZE, &verify_state);

        /* Advance PRBS state for next iteration */
        prbs_state = tx_prbs_state;

        int xfer_has_error = 0;

        for (int i = 0; i < BUF_SIZE; i++) {
            uint8_t diff = verify_buf[i] ^ rx_buf[i];
            stats.total_bits += 8;
            stats.total_bytes++;

            if (diff) {
                stats.error_bytes++;
                stats.error_bits += popcount8(diff);
                xfer_has_error = 1;

                /* Track per-bit-position errors */
                for (int b = 0; b < 8; b++) {
                    if (diff & (1 << b))
                        stats.bit_position_errors[b]++;
                }
            }
        }

        if (xfer_has_error)
            stats.error_transfers++;
        else
            stats.error_free_transfers++;

        /* Periodic report */
        double now = time_sec();
        if ((now - last_report) >= REPORT_INTERVAL) {
            bert_print_report(&stats);
            last_report = now;
        }
    }

    /* Final report */
    printf("\n==============================================\n");
    printf("   BERT FINAL RESULTS\n");
    printf("==============================================\n");
    bert_print_report(&stats);

    double ber = (stats.total_bits > 0)
                 ? (double)stats.error_bits / stats.total_bits
                 : 0.0;

    printf("\nVerdict: ");
    if (stats.error_bits == 0)
        printf("NO ERRORS DETECTED - BER < %.2e (95%% confidence)\n", target_ber);
    else if (ber <= target_ber)
        printf("BER %.2e <= target 1e%d - PASS\n", ber, target_ber_exp);
    else
        printf("BER %.2e > target 1e%d - FAIL\n", ber, target_ber_exp);

    printf("==============================================\n");

    free(tx_buf);
    free(rx_buf);
    close(fd);

    return (ber <= target_ber) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Program 9: SPI Multi-Slave Communication Diagnostic

Communicates with multiple slaves on different chip-selects and validates each.
/*
 * spi_multi_slave.c
 * SPI Multi-Slave Communication Diagnostic
 * Compile: gcc -o spi_multi spi_multi_slave.c
 * Run:     sudo ./spi_multi
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define MAX_SLAVES  8

typedef struct {
    const char *device;
    uint32_t    speed;
    uint8_t     mode;
    uint8_t     bits;
    int         fd;
    int         present;
    /* Device identification */
    uint8_t     id_reg;
    uint8_t     id_value;
    const char *description;
} spi_slave_t;

/* Configure your slaves here */
static spi_slave_t slaves[MAX_SLAVES] = {
    { "/dev/spidev0.0", 1000000, SPI_MODE_0, 8, -1, 0, 0x75, 0x00, "Slave 0 (CS0)" },
    { "/dev/spidev0.1", 1000000, SPI_MODE_0, 8, -1, 0, 0x75, 0x00, "Slave 1 (CS1)" },
    { "/dev/spidev1.0", 1000000, SPI_MODE_0, 8, -1, 0, 0x0F, 0x00, "Slave 2 (Bus1/CS0)" },
    { "/dev/spidev1.1", 1000000, SPI_MODE_0, 8, -1, 0, 0x0F, 0x00, "Slave 3 (Bus1/CS1)" },
};
static int num_slaves = 4;

static int slave_open(spi_slave_t *s)
{
    s->fd = open(s->device, O_RDWR);
    if (s->fd < 0) return -1;

    if (ioctl(s->fd, SPI_IOC_WR_MODE, &s->mode) < 0) goto fail;
    if (ioctl(s->fd, SPI_IOC_WR_BITS_PER_WORD, &s->bits) < 0) goto fail;
    if (ioctl(s->fd, SPI_IOC_WR_MAX_SPEED_HZ, &s->speed) < 0) goto fail;

    s->present = 1;
    return 0;

fail:
    close(s->fd);
    s->fd = -1;
    return -1;
}

static void slave_close(spi_slave_t *s)
{
    if (s->fd >= 0) close(s->fd);
    s->fd = -1;
}

static int slave_transfer(spi_slave_t *s, uint8_t *tx, uint8_t *rx, size_t len)
{
    if (s->fd < 0) return -1;

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = len,
        .speed_hz      = s->speed,
        .bits_per_word = s->bits,
    };

    return ioctl(s->fd, SPI_IOC_MESSAGE(1), &tr);
}

static int slave_read_reg(spi_slave_t *s, uint8_t reg, uint8_t *val)
{
    uint8_t tx[2] = { reg | 0x80, 0x00 };
    uint8_t rx[2] = { 0 };

    if (slave_transfer(s, tx, rx, 2) < 0) return -1;
    *val = rx[1];
    return 0;
}

/* ---- Tests ---- */

static int test_slave_presence(spi_slave_t *s)
{
    uint8_t tx[1] = {0x00}, rx[1] = {0};
    return slave_transfer(s, tx, rx, 1);
}

static int test_slave_loopback(spi_slave_t *s)
{
    uint8_t tx[32], rx[32];
    for (int i = 0; i < 32; i++) tx[i] = (uint8_t)(i * 3 + 7);
    memset(rx, 0, 32);

    if (slave_transfer(s, tx, rx, 32) < 0) return -1;

    /* In loopback mode, rx should match tx */
    return memcmp(tx, rx, 32) == 0 ? 0 : -1;
}

static int test_cross_talk(spi_slave_t *slaves_list, int n)
{
    /*
     * Send data to one slave while verifying others
     * don't receive stray data (basic cross-talk check)
     */
    int issues = 0;

    for (int active = 0; active < n; active++) {
        if (!slaves_list[active].present) continue;

        /* Send known data to active slave */
        uint8_t tx[16];
        uint8_t rx_active[16], rx_other[16];
        memset(tx, 0xA5, 16);

        slave_transfer(&slaves_list[active], tx, rx_active, 16);

        /* Read from other slaves - they should not have received our data */
        for (int other = 0; other < n; other++) {
            if (other == active || !slaves_list[other].present) continue;

            uint8_t probe_tx[16] = {0};
            memset(rx_other, 0, 16);
            slave_transfer(&slaves_list[other], probe_tx, rx_other, 16);

            /* Check if other slave somehow got our 0xA5 pattern */
            int match_count = 0;
            for (int i = 0; i < 16; i++) {
                if (rx_other[i] == 0xA5) match_count++;
            }

            if (match_count > 8) {  /* Threshold for cross-talk detection */
                printf("  WARNING: Possible cross-talk from %s to %s\n",
                       slaves_list[active].device, slaves_list[other].device);
                issues++;
            }
        }
    }

    return issues;
}

static int test_round_robin(spi_slave_t *slaves_list, int n, int rounds)
{
    int errors = 0;

    for (int r = 0; r < rounds; r++) {
        for (int s = 0; s < n; s++) {
            if (!slaves_list[s].present) continue;

            uint8_t tx[8], rx[8];
            for (int i = 0; i < 8; i++)
                tx[i] = (uint8_t)(r * n + s + i);
            memset(rx, 0, 8);

            if (slave_transfer(&slaves_list[s], tx, rx, 8) < 0)
                errors++;
        }
    }

    return errors;
}

int main(void)
{
    int pass = 0, fail = 0, skip = 0;
    int active_slaves = 0;

    printf("==============================================\n");
    printf("   SPI MULTI-SLAVE DIAGNOSTIC\n");
    printf("==============================================\n\n");

    /* Open all slaves */
    printf("[Phase 1] Slave Enumeration\n");
    printf("%-30s %-12s %-10s\n", "Device", "Status", "FD");
    printf("----------------------------------------------------\n");

    for (int i = 0; i < num_slaves; i++) {
        int ret = slave_open(&slaves[i]);
        printf("%-30s %-12s %-10d\n",
               slaves[i].device,
               (ret == 0) ? "OPENED" : "NOT FOUND",
               slaves[i].fd);
        if (ret == 0) active_slaves++;
    }

    if (active_slaves == 0) {
        printf("\nNo SPI slaves found. Exiting.\n");
        return EXIT_FAILURE;
    }

    printf("\nActive slaves: %d / %d\n\n", active_slaves, num_slaves);

    /* Test 1: Presence check */
    printf("[Phase 2] Presence Check\n");
    for (int i = 0; i < num_slaves; i++) {
        if (!slaves[i].present) { skip++; continue; }

        int ret = test_slave_presence(&slaves[i]);
        printf("  %-30s: %s\n", slaves[i].description,
               (ret >= 0) ? "PRESENT" : "NO RESPONSE");
        if (ret >= 0) pass++; else fail++;
    }

    /* Test 2: ID Register Read */
    printf("\n[Phase 3] ID Register Read\n");
    for (int i = 0; i < num_slaves; i++) {
        if (!slaves[i].present) continue;

        uint8_t id = 0;
        int ret = slave_read_reg(&slaves[i], slaves[i].id_reg, &id);

        slaves[i].id_value = id;
        printf("  %-30s: Reg[0x%02X] = 0x%02X %s\n",
               slaves[i].description, slaves[i].id_reg, id,
               (ret >= 0 && id != 0x00 && id != 0xFF) ? "(valid)" : "(check)");
    }

    /* Test 3: Loopback */
    printf("\n[Phase 4] Loopback Test (if wired)\n");
    for (int i = 0; i < num_slaves; i++) {
        if (!slaves[i].present) continue;

        int ret = test_slave_loopback(&slaves[i]);
        printf("  %-30s: %s\n", slaves[i].description,
               (ret == 0) ? "PASS" : "FAIL/NO LOOPBACK");
    }

    /* Test 4: Cross-talk */
    printf("\n[Phase 5] Cross-Talk Detection\n");
    int xtalk = test_cross_talk(slaves, num_slaves);
    printf("  Cross-talk issues: %d\n", xtalk);

    /* Test 5: Round-robin */
    printf("\n[Phase 6] Round-Robin Communication (100 rounds)\n");
    int rr_errors = test_round_robin(slaves, num_slaves, 100);
    printf("  Round-robin errors: %d\n", rr_errors);

    /* Test 6: Rapid switching */
    printf("\n[Phase 7] Rapid Slave Switching (1000 switches)\n");
    {
        int switch_err = 0;
        uint8_t tx[4] = {0xDE, 0xAD, 0xBE, 0xEF};
        uint8_t rx[4];

        for (int iter = 0; iter < 1000; iter++) {
            int s = iter % num_slaves;
            if (!slaves[s].present) continue;
            if (slave_transfer(&slaves[s], tx, rx, 4) < 0)
                switch_err++;
        }
        printf("  Switching errors: %d / 1000\n", switch_err);
    }

    /* Cleanup */
    for (int i = 0; i < num_slaves; i++)
        slave_close(&slaves[i]);

    /* Summary */
    printf("\n==============================================\n");
    printf("   MULTI-SLAVE DIAGNOSTIC SUMMARY\n");
    printf("==============================================\n");
    printf("Slaves Found  : %d / %d\n", active_slaves, num_slaves);
    printf("Cross-talk    : %d issues\n", xtalk);
    printf("Round-Robin   : %d errors\n", rr_errors);
    printf("Overall       : %s\n",
           (xtalk == 0 && rr_errors == 0) ? "PASS" : "ISSUES DETECTED");
    printf("==============================================\n");

    return EXIT_SUCCESS;
}

Program 10: SPI Bus Protocol Logger

Captures, timestamps, and logs all SPI transactions for debugging.
/*
 * spi_protocol_logger.c
 * SPI Bus Transaction Logger / Protocol Analyzer
 * Compile: gcc -o spi_logger spi_protocol_logger.c
 * Run:     sudo ./spi_logger /dev/spidev0.0 [logfile.csv]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define MAX_LOG_SIZE        4096
#define DEFAULT_LOG_FILE    "spi_log.csv"
#define SPI_SPEED           1000000

static volatile int g_running = 1;
void sig_handler(int s) { (void)s; g_running = 0; }

/* Transaction record */
typedef struct {
    uint64_t    seq_num;
    double      timestamp_us;
    double      duration_us;
    uint8_t     direction;      /* 0=TX+RX, 1=TX-only, 2=RX-only */
    uint16_t    length;
    uint8_t     tx_data[MAX_LOG_SIZE];
    uint8_t     rx_data[MAX_LOG_SIZE];
    int         status;         /* 0=OK, -1=ERROR */
    int         data_match;     /* 1 if tx==rx (loopback) */
    uint32_t    speed;
    uint8_t     mode;
} spi_log_entry_t;

/* Circular log buffer */
#define LOG_CAPACITY    10000
static spi_log_entry_t log_buffer[LOG_CAPACITY];
static int log_head = 0;
static uint64_t log_seq = 0;

static double get_time_us(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1e6 + ts.tv_nsec / 1000.0;
}

static void get_timestamp_str(char *buf, size_t size)
{
    struct timespec ts;
    struct tm tm_info;
    clock_gettime(CLOCK_REALTIME, &ts);
    localtime_r(&ts.tv_sec, &tm_info);
    int len = strftime(buf, size, "%Y-%m-%d %H:%M:%S", &tm_info);
    snprintf(buf + len, size - len, ".%06ld", ts.tv_nsec / 1000);
}

/* Logged SPI transfer */
static int logged_spi_transfer(int fd, uint8_t *tx, uint8_t *rx,
                                size_t len, uint32_t speed, uint8_t mode)
{
    spi_log_entry_t *entry = &log_buffer[log_head % LOG_CAPACITY];

    entry->seq_num   = log_seq++;
    entry->length    = (uint16_t)len;
    entry->speed     = speed;
    entry->mode      = mode;
    entry->direction = 0;

    if (len <= MAX_LOG_SIZE) {
        memcpy(entry->tx_data, tx, len);
    }

    struct spi_ioc_transfer tr = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = len,
        .speed_hz      = speed,
        .bits_per_word = 8,
    };

    double t0 = get_time_us();
    int ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    double t1 = get_time_us();

    entry->timestamp_us = t0;
    entry->duration_us  = t1 - t0;
    entry->status       = (ret < 0) ? -1 : 0;

    if (ret >= 0 && len <= MAX_LOG_SIZE) {
        memcpy(entry->rx_data, rx, len);
        entry->data_match = (memcmp(tx, rx, len) == 0) ? 1 : 0;
    } else {
        entry->data_match = 0;
    }

    log_head++;
    return ret;
}

/* Export log to CSV */
static int export_log_csv(const char *filename)
{
    FILE *fp = fopen(filename, "w");
    if (!fp) { perror("fopen"); return -1; }

    fprintf(fp, "Seq,Timestamp_us,Duration_us,Length,Speed_Hz,Mode,"
                "Status,Match,TX_Data,RX_Data\n");

    int count = (log_head < LOG_CAPACITY) ? log_head : LOG_CAPACITY;
    int start = (log_head < LOG_CAPACITY) ? 0 : (log_head % LOG_CAPACITY);

    for (int i = 0; i < count; i++) {
        int idx = (start + i) % LOG_CAPACITY;
        spi_log_entry_t *e = &log_buffer[idx];

        fprintf(fp, "%lu,%.1f,%.1f,%u,%u,%u,%s,%s,",
                (unsigned long)e->seq_num,
                e->timestamp_us, e->duration_us,
                e->length, e->speed, e->mode,
                e->status == 0 ? "OK" : "ERROR",
                e->data_match ? "MATCH" : "MISMATCH");

        /* TX data (hex string) */
        int print_len = (e->length > 32) ? 32 : e->length;
        for (int b = 0; b < print_len; b++)
            fprintf(fp, "%02X", e->tx_data[b]);
        if (e->length > 32) fprintf(fp, "...");

        fprintf(fp, ",");

        /* RX data (hex string) */
        for (int b = 0; b < print_len; b++)
            fprintf(fp, "%02X", e->rx_data[b]);
        if (e->length > 32) fprintf(fp, "...");

        fprintf(fp, "\n");
    }

    fclose(fp);
    return count;
}

/* Print live transaction */
static void print_transaction(spi_log_entry_t *e, int verbose)
{
    printf("[%8lu] t=%.1f us  dur=%.1f us  len=%u  %s  %s",
           (unsigned long)e->seq_num,
           e->timestamp_us, e->duration_us,
           e->length,
           e->status == 0 ? "OK  " : "ERR ",
           e->data_match ? "MATCH" : "DIFF ");

    if (verbose && e->length <= 16) {
        printf("  TX:");
        for (int i = 0; i < e->length; i++)
            printf("%02X", e->tx_data[i]);
        printf("  RX:");
        for (int i = 0; i < e->length; i++)
            printf("%02X", e->rx_data[i]);
    }
    printf("\n");
}

/* --- Diagnostic sequence with logging --- */
static void run_diagnostic_sequence(int fd, uint32_t speed, uint8_t mode)
{
    uint8_t tx[256], rx[256];

    printf("\n--- Running Diagnostic Sequence (all logged) ---\n\n");

    /* Test 1: Single byte */
    printf("  [1] Single byte transfer\n");
    tx[0] = 0xAA;
    logged_spi_transfer(fd, tx, rx, 1, speed, mode);
    print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 1);

    /* Test 2: Register read pattern */
    printf("  [2] Register read pattern (2 bytes)\n");
    tx[0] = 0x80 | 0x75; tx[1] = 0x00;  /* Read reg 0x75 */
    logged_spi_transfer(fd, tx, rx, 2, speed, mode);
    print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 1);

    /* Test 3: Burst read */
    printf("  [3] Burst read (16 bytes)\n");
    memset(tx, 0, 16);
    tx[0] = 0x80 | 0x00;  /* Read from reg 0x00 */
    logged_spi_transfer(fd, tx, rx, 16, speed, mode);
    print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 1);

    /* Test 4: Walking ones */
    printf("  [4] Walking ones pattern\n");
    for (int i = 0; i < 8; i++) tx[i] = (1 << i);
    logged_spi_transfer(fd, tx, rx, 8, speed, mode);
    print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 1);

    /* Test 5: Incrementing data */
    printf("  [5] Incrementing data (256 bytes)\n");
    for (int i = 0; i < 256; i++) tx[i] = (uint8_t)i;
    logged_spi_transfer(fd, tx, rx, 256, speed, mode);
    print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 0);

    /* Test 6: Repeated transfers (for timing analysis) */
    printf("  [6] Repeated 1-byte transfers (1000x)\n");
    tx[0] = 0x55;
    for (int i = 0; i < 1000; i++)
        logged_spi_transfer(fd, tx, rx, 1, speed, mode);
    printf("       ... 1000 transfers logged\n");

    /* Test 7: Different modes */
    printf("  [7] Mode sweep\n");
    for (uint8_t m = 0; m <= 3; m++) {
        ioctl(fd, SPI_IOC_WR_MODE, &m);
        tx[0] = 0xA5;
        logged_spi_transfer(fd, tx, rx, 1, speed, m);
        printf("       Mode %d: ", m);
        print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 1);
    }
    ioctl(fd, SPI_IOC_WR_MODE, &mode);  /* Restore */

    /* Test 8: Speed sweep */
    printf("  [8] Speed sweep\n");
    uint32_t speeds[] = {100000, 500000, 1000000, 4000000, 8000000};
    for (int i = 0; i < 5; i++) {
        ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speeds[i]);
        tx[0] = 0xCC;
        logged_spi_transfer(fd, tx, rx, 1, speeds[i], mode);
        printf("       %u Hz: ", speeds[i]);
        print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 1);
    }
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    /* Test 9: Max-size transfer */
    printf("  [9] Max-size transfer (4096 bytes)\n");
    uint8_t *big_tx = calloc(4096, 1);
    uint8_t *big_rx = calloc(4096, 1);
    if (big_tx && big_rx) {
        for (int i = 0; i < 4096; i++) big_tx[i] = (uint8_t)(i & 0xFF);
        logged_spi_transfer(fd, big_tx, big_rx, 4096, speed, mode);
        print_transaction(&log_buffer[(log_head - 1) % LOG_CAPACITY], 0);
    }
    free(big_tx);
    free(big_rx);

    /* Test 10: Continuous monitoring */
    printf("  [10] Continuous monitoring (5 seconds)...\n");
    {
        double end_time = get_time_us() + 5e6;
        int count = 0;
        tx[0] = 0xFF;

        while (get_time_us() < end_time && g_running) {
            logged_spi_transfer(fd, tx, rx, 1, speed, mode);
            count++;
            usleep(1000);  /* 1ms interval */
        }
        printf("       Captured %d transactions in 5s\n", count);
    }
}

int main(int argc, char *argv[])
{
    const char *device   = (argc > 1) ? argv[1] : "/dev/spidev0.0";
    const char *logfile  = (argc > 2) ? argv[2] : DEFAULT_LOG_FILE;
    uint32_t speed       = SPI_SPEED;
    uint8_t mode         = SPI_MODE_0;
    uint8_t bits         = 8;

    char ts_buf[64];
    get_timestamp_str(ts_buf, sizeof(ts_buf));

    printf("==============================================\n");
    printf("   SPI PROTOCOL LOGGER / ANALYZER\n");
    printf("==============================================\n");
    printf("Device   : %s\n", device);
    printf("Log File : %s\n", logfile);
    printf("Speed    : %u Hz\n", speed);
    printf("Started  : %s\n", ts_buf);
    printf("Buffer   : %d entries\n", LOG_CAPACITY);
    printf("==============================================\n");

    signal(SIGINT, sig_handler);

    int fd = open(device, O_RDWR);
    if (fd < 0) { perror("open"); return EXIT_FAILURE; }

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    /* Run full diagnostic sequence with logging */
    run_diagnostic_sequence(fd, speed, mode);

    /* Export log */
    printf("\n--- Exporting Log ---\n");
    int entries = export_log_csv(logfile);
    printf("Exported %d entries to %s\n", entries, logfile);

    /* Log analysis */
    printf("\n--- Log Analysis ---\n");
    {
        int total = (log_head < LOG_CAPACITY) ? log_head : LOG_CAPACITY;
        int errors = 0, matches = 0;
        double min_dur = 1e12, max_dur = 0, sum_dur = 0;

        int start = (log_head < LOG_CAPACITY) ? 0 : (log_head % LOG_CAPACITY);
        for (int i = 0; i < total; i++) {
            int idx = (start + i) % LOG_CAPACITY;
            spi_log_entry_t *e = &log_buffer[idx];

            if (e->status != 0) errors++;
            if (e->data_match) matches++;
            if (e->duration_us < min_dur) min_dur = e->duration_us;
            if (e->duration_us > max_dur) max_dur = e->duration_us;
            sum_dur += e->duration_us;
        }

        printf("  Total Logged     : %d\n", total);
        printf("  Transfer Errors  : %d\n", errors);
        printf("  Data Matches     : %d (loopback)\n", matches);
        printf("  Min Duration     : %.1f us\n", min_dur);
        printf("  Max Duration     : %.1f us\n", max_dur);
        printf("  Avg Duration     : %.1f us\n",
               total > 0 ? sum_dur / total : 0);

        printf("\n  Error Rate       : %.4f%%\n",
               total > 0 ? 100.0 * errors / total : 0);
        printf("  Match Rate       : %.4f%%\n",
               total > 0 ? 100.0 * matches / total : 0);
    }

    printf("\n==============================================\n");
    printf("   LOGGING COMPLETE\n");
    printf("==============================================\n");
    printf("Review %s for detailed transaction log.\n", logfile);

    close(fd);
    return EXIT_SUCCESS;
}

Compilation & Usage Summary

# Compile all programs
gcc -o spi_loopback   spi_loopback_test.c
gcc -o spi_reg_rw     spi_register_rw.c
gcc -o spi_clk_sweep  spi_clock_sweep.c
gcc -o spi_crc        spi_crc_integrity.c
gcc -o spi_scanner    spi_device_scanner.c
gcc -o spi_stress     spi_stress_test.c    -lrt
gcc -o spi_timing     spi_timing_analyzer.c -lm
gcc -o spi_bert       spi_bert.c           -lm
gcc -o spi_multi      spi_multi_slave.c
gcc -o spi_logger     spi_protocol_logger.c

# Run (requires root for /dev/spidevX.Y access)
sudo ./spi_loopback   /dev/spidev0.0
sudo ./spi_reg_rw     /dev/spidev0.0
sudo ./spi_clk_sweep  /dev/spidev0.0
sudo ./spi_crc        /dev/spidev0.0
sudo ./spi_scanner
sudo ./spi_stress     /dev/spidev0.0 60
sudo ./spi_timing     /dev/spidev0.0
sudo ./spi_bert       /dev/spidev0.0 -9
sudo ./spi_multi
sudo ./spi_logger     /dev/spidev0.0 log.csv

Prerequisites & Notes

  • Kernel: Linux with SPI driver enabled (CONFIG_SPI_SPIDEV=y)
  • Device Tree: SPI overlay must be loaded (e.g., on Raspberry Pi: dtparam=spi=on)
  • Loopback: Programs 1, 3, 4, 6, 7, 8 require MOSI→MISO physical loopback wire
  • Permissions: Requires root or spi group membership
  • Hardware: Adjust device paths, register maps, and speeds for your target platform
  • Safety: Programs that write registers (Program 2) should be customized for your specific device to avoid unintended writes
Linux Rocks Every Day