The Embedded New Testament

The "Holy Bible" for embedded engineers


Project maintained by theEmbeddedGeorge Hosted on GitHub Pages — Theme by mattgraham

Oscilloscope Measurements

Table of Contents

Overview

Oscilloscopes are fundamental tools for embedded system debugging, providing real-time visualization of analog and digital signals. They excel at measuring voltage levels, timing relationships, and signal quality, making them essential for hardware validation, power analysis, and signal integrity verification.

Key Benefits:

Key Concepts

Analog Signal Fundamentals

Oscilloscopes capture continuous voltage variations over time, providing detailed insight into signal characteristics that digital tools cannot provide.

Signal Parameters:

Sampling and Digitization

Modern oscilloscopes convert analog signals to digital form for storage, analysis, and display. The quality of this conversion directly affects measurement accuracy.

Sampling Considerations:

Bandwidth and Rise Time

Oscilloscope bandwidth determines the fastest signals that can be accurately measured, while rise time affects timing measurement precision.

Bandwidth Relationship:

Bandwidth = 0.35 / Rise Time

Practical Bandwidth Requirements:

Core Concepts

Triggering Systems

Oscilloscope triggers synchronize capture to specific signal events, enabling stable display and focused analysis of complex signals.

Trigger Types:

Trigger Modes:

Measurement Systems

Modern oscilloscopes provide automated measurements for common signal parameters, improving accuracy and reducing measurement time.

Basic Measurements:

Advanced Measurements:

Display and Analysis

Oscilloscope displays provide multiple views of captured data, enabling comprehensive signal analysis and comparison.

Display Modes:

Implementation

Basic Oscilloscope Setup

// Oscilloscope configuration structure
typedef struct {
    uint32_t sample_rate;
    uint32_t memory_depth;
    uint32_t channel_count;
    float time_base;
    float voltage_scale;
    uint32_t trigger_source;
    float trigger_level;
    bool trigger_enabled;
} oscilloscope_config_t;

// Channel configuration
typedef struct {
    uint32_t channel_id;
    float voltage_scale;
    float offset;
    bool enabled;
    bool inverted;
    uint32_t coupling; // AC, DC, GND
    char label[32];
} channel_config_t;

// Initialize oscilloscope
bool oscilloscope_init(oscilloscope_config_t *config) {
    // Set time base
    if (!set_time_base(config->time_base)) {
        return false;
    }
    
    // Configure channels
    for (uint32_t i = 0; i < config->channel_count; i++) {
        if (!configure_channel(i, &config->channels[i])) {
            return false;
        }
    }
    
    // Set up trigger
    if (config->trigger_enabled) {
        if (!configure_trigger(config->trigger_source, config->trigger_level)) {
            return false;
        }
    }
    
    return true;
}

Trigger Configuration

// Trigger configuration structure
typedef struct {
    uint32_t trigger_type;
    uint32_t trigger_source;
    float trigger_level;
    uint32_t trigger_mode;
    float trigger_delay;
    bool trigger_enabled;
} trigger_config_t;

// Trigger types
typedef enum {
    TRIGGER_EDGE_RISING,
    TRIGGER_EDGE_FALLING,
    TRIGGER_PULSE_WIDTH,
    TRIGGER_PATTERN,
    TRIGGER_VIDEO,
    TRIGGER_SERIAL
} trigger_type_t;

// Configure edge trigger
bool configure_edge_trigger(uint32_t source, float level, bool rising) {
    // Set trigger source
    if (!set_trigger_source(source)) {
        return false;
    }
    
    // Set trigger level
    if (!set_trigger_level(level)) {
        return false;
    }
    
    // Set edge direction
    if (!set_trigger_edge(rising)) {
        return false;
    }
    
    // Enable trigger
    return enable_trigger();
}

// Configure pulse width trigger
bool configure_pulse_trigger(uint32_t source, float level, 
                           float min_width, float max_width) {
    if (!configure_edge_trigger(source, level, true)) {
        return false;
    }
    
    // Set pulse width conditions
    if (!set_pulse_width_trigger(min_width, max_width)) {
        return false;
    }
    
    return true;
}

Data Acquisition


// Acquisition buffer structure
typedef struct {
    float *voltage_data;
    uint64_t *timestamp_data;
    uint32_t sample_count;
    uint32_t channel_count;
    uint64_t trigger_time;
    bool trigger_found;
} acquisition_buffer_t;

// Start acquisition
bool start_acquisition(oscilloscope_config_t *config) {
    // Arm trigger
    if (config->trigger_enabled) {
        if (!arm_trigger()) {
            return false;
        }
    }
    
    // Start sampling
    if (!start_sampling(config->sample_rate)) {
        return false;
    }
    
    // Wait for trigger or timeout
    uint32_t timeout_ms = 10000; // 10 second timeout
    uint32_t elapsed_ms = 0;
    
    while (!is_triggered() && elapsed_ms < timeout_ms) {
        delay_ms(10);
        elapsed_ms += 10;
    }
    
    if (elapsed_ms >= timeout_ms) {
        stop_sampling();
        return false; // Timeout
    }
    
    // Continue sampling for post-trigger data
    uint32_t post_trigger_samples = config->memory_depth * 0.8f; // 80% post-trigger
    
    delay_ms(post_trigger_samples * 1000 / config->sample_rate);
    
    stop_sampling();
    return true;
}

// Read acquired data
bool read_acquired_data(acquisition_buffer_t *buffer) {
    // Allocate buffers
    size_t data_size = buffer->sample_count * buffer->channel_count * sizeof(float);
    buffer->voltage_data = malloc(data_size);
    if (!buffer->voltage_data) {
        return false;
    }
    
    size_t timestamp_size = buffer->sample_count * sizeof(uint64_t);
    buffer->timestamp_data = malloc(timestamp_size);
    if (!buffer->timestamp_data) {
        free(buffer->voltage_data);
        return false;
    }
    
    // Read voltage data
    if (!read_voltage_data(buffer->voltage_data, data_size)) {
        free(buffer->voltage_data);
        free(buffer->timestamp_data);
        return false;
    }
    
    // Read timestamp data
    if (!read_timestamp_data(buffer->timestamp_data, timestamp_size)) {
        free(buffer->voltage_data);
        free(buffer->timestamp_data);
        return false;
    }
    
    // Get trigger information
    buffer->trigger_time = get_trigger_time();
    buffer->trigger_found = is_trigger_found();
    
    return true;
}

Measurement Calculations

// Measurement result structure
typedef struct {
    float peak_to_peak;
    float rms;
    float average;
    float frequency;
    float period;
    float rise_time;
    float fall_time;
    float duty_cycle;
} measurement_result_t;

// Calculate voltage measurements
measurement_result_t calculate_voltage_measurements(float *voltage_data, 
                                                  uint32_t sample_count) {
    measurement_result_t result = {0};
    
    if (sample_count == 0) {
        return result;
    }
    
    float min_val = voltage_data[0];
    float max_val = voltage_data[0];
    float sum = 0;
    float sum_squares = 0;
    
    // Find min, max, and calculate sum
    for (uint32_t i = 0; i < sample_count; i++) {
        float voltage = voltage_data[i];
        
        if (voltage < min_val) min_val = voltage;
        if (voltage > max_val) max_val = voltage;
        
        sum += voltage;
        sum_squares += voltage * voltage;
    }
    
    // Calculate measurements
    result.peak_to_peak = max_val - min_val;
    result.average = sum / sample_count;
    result.rms = sqrt(sum_squares / sample_count);
    
    return result;
}

// Calculate timing measurements
measurement_result_t calculate_timing_measurements(float *voltage_data, 
                                                 uint64_t *timestamp_data,
                                                 uint32_t sample_count,
                                                 float threshold) {
    measurement_result_t result = {0};
    
    if (sample_count < 2) {
        return result;
    }
    
    // Find zero crossings
    uint32_t crossings[1000];
    uint32_t crossing_count = 0;
    
    for (uint32_t i = 1; i < sample_count; i++) {
        float current = voltage_data[i];
        float previous = voltage_data[i-1];
        
        // Check for crossing threshold
        if ((previous < threshold && current >= threshold) ||
            (previous >= threshold && current < threshold)) {
            
            if (crossing_count < 1000) {
                crossings[crossing_count++] = i;
            }
        }
    }
    
    // Calculate period and frequency
    if (crossing_count >= 2) {
        uint64_t time_diff = timestamp_data[crossings[1]] - timestamp_data[crossings[0]];
        result.period = (float)time_diff / 1000000.0f; // Convert to ms
        result.frequency = 1000.0f / result.period; // Convert to Hz
    }
    
    // Calculate rise and fall times
    if (crossing_count >= 4) {
        // Rise time: 10% to 90% of peak-to-peak
        float v_pp = 0;
        for (uint32_t i = 0; i < sample_count; i++) {
            if (voltage_data[i] > v_pp) v_pp = voltage_data[i];
        }
        
        float v_10 = v_pp * 0.1f;
        float v_90 = v_pp * 0.9f;
        
        // Find 10% and 90% crossing points
        uint32_t t_10 = 0, t_90 = 0;
        for (uint32_t i = 0; i < sample_count; i++) {
            if (voltage_data[i] >= v_10 && t_10 == 0) t_10 = i;
            if (voltage_data[i] >= v_90 && t_90 == 0) t_90 = i;
        }
        
        if (t_10 > 0 && t_90 > 0 && t_90 > t_10) {
            result.rise_time = (float)(timestamp_data[t_90] - timestamp_data[t_10]) / 1000000.0f;
        }
    }
    
    return result;
}

Advanced Techniques

FFT Analysis

// FFT configuration
typedef struct {
    uint32_t fft_size;
    uint32_t window_type;
    float frequency_resolution;
    bool db_scale;
} fft_config_t;

// Window types
typedef enum {
    WINDOW_RECTANGULAR,
    WINDOW_HANNING,
    WINDOW_HAMMING,
    WINDOW_BLACKMAN,
    WINDOW_FLAT_TOP
} window_type_t;

// Perform FFT analysis
bool perform_fft_analysis(float *voltage_data, 
                         uint32_t sample_count,
                         fft_config_t *config,
                         float *magnitude_data,
                         float *phase_data) {
    // Apply window function
    float *windowed_data = malloc(sample_count * sizeof(float));
    if (!windowed_data) {
        return false;
    }
    
    apply_window_function(voltage_data, windowed_data, sample_count, config->window_type);
    
    // Perform FFT
    if (!fft_compute(windowed_data, magnitude_data, phase_data, config->fft_size)) {
        free(windowed_data);
        return false;
    }
    
    // Convert to dB scale if requested
    if (config->db_scale) {
        for (uint32_t i = 0; i < config->fft_size / 2; i++) {
            if (magnitude_data[i] > 0) {
                magnitude_data[i] = 20 * log10(magnitude_data[i]);
            } else {
                magnitude_data[i] = -100; // -100 dB floor
            }
        }
    }
    
    free(windowed_data);
    return true;
}

Statistical Analysis

// Statistical measurement structure
typedef struct {
    float mean;
    float standard_deviation;
    float min_value;
    float max_value;
    uint32_t sample_count;
    float confidence_interval;
} statistical_measurement_t;

// Calculate statistical measurements
statistical_measurement_t calculate_statistics(float *voltage_data, 
                                             uint32_t sample_count) {
    statistical_measurement_t stats = {0};
    
    if (sample_count == 0) {
        return stats;
    }
    
    // Calculate mean
    float sum = 0;
    for (uint32_t i = 0; i < sample_count; i++) {
        sum += voltage_data[i];
    }
    stats.mean = sum / sample_count;
    
    // Calculate standard deviation
    float sum_squares = 0;
    for (uint32_t i = 0; i < sample_count; i++) {
        float diff = voltage_data[i] - stats.mean;
        sum_squares += diff * diff;
    }
    stats.standard_deviation = sqrt(sum_squares / (sample_count - 1));
    
    // Find min and max
    stats.min_value = voltage_data[0];
    stats.max_value = voltage_data[0];
    for (uint32_t i = 1; i < sample_count; i++) {
        if (voltage_data[i] < stats.min_value) stats.min_value = voltage_data[i];
        if (voltage_data[i] > stats.max_value) stats.max_value = voltage_data[i];
    }
    
    stats.sample_count = sample_count;
    
    // Calculate 95% confidence interval
    stats.confidence_interval = 1.96f * stats.standard_deviation / sqrt(sample_count);
    
    return stats;
}

Power Analysis

// Power measurement structure
typedef struct {
    float rms_voltage;
    float rms_current;
    float apparent_power;
    float real_power;
    float power_factor;
    float efficiency;
} power_measurement_t;

// Calculate power measurements
power_measurement_t calculate_power_measurements(float *voltage_data,
                                               float *current_data,
                                               uint32_t sample_count) {
    power_measurement_t power = {0};
    
    if (sample_count == 0) {
        return power;
    }
    
    // Calculate RMS voltage and current
    float v_sum_squares = 0;
    float i_sum_squares = 0;
    float power_sum = 0;
    
    for (uint32_t i = 0; i < sample_count; i++) {
        v_sum_squares += voltage_data[i] * voltage_data[i];
        i_sum_squares += current_data[i] * current_data[i];
        power_sum += voltage_data[i] * current_data[i];
    }
    
    power.rms_voltage = sqrt(v_sum_squares / sample_count);
    power.rms_current = sqrt(i_sum_squares / sample_count);
    
    // Calculate power values
    power.apparent_power = power.rms_voltage * power.rms_current;
    power.real_power = power_sum / sample_count;
    
    if (power.apparent_power > 0) {
        power.power_factor = power.real_power / power.apparent_power;
    }
    
    return power;
}

Common Pitfalls

Probe Issues

Trigger Problems

Measurement Errors

Best Practices

Setup and Calibration

  1. Calibrate probes before making critical measurements
  2. Verify probe bandwidth exceeds signal requirements
  3. Use appropriate probe attenuation for voltage range
  4. Check ground connections for noise and safety

Trigger Configuration

  1. Set trigger level at signal midpoint for stability
  2. Use appropriate trigger mode for signal characteristics
  3. Enable trigger coupling for AC signals
  4. Test trigger setup with known signal patterns

Measurement Accuracy

  1. Use appropriate time base for signal analysis
  2. Enable averaging for noisy signals
  3. Verify sampling rate is sufficient for signal bandwidth
  4. Use cursor measurements for precise values

Interview Questions

Basic Level

  1. What is the difference between analog and digital oscilloscopes?
    • Analog oscilloscopes use CRT displays and continuous signals
    • Digital oscilloscopes digitize signals for storage and analysis
    • Digital scopes offer more measurement and analysis features
  2. Why is oscilloscope bandwidth important?
    • Determines fastest signals that can be accurately measured
    • Insufficient bandwidth causes signal distortion and measurement errors
    • Should be 3-5x signal frequency for digital signals

Intermediate Level

  1. How would you measure the rise time of a digital signal?
    • Use edge trigger to capture signal transition
    • Set appropriate time base for detailed view
    • Use cursor measurements for 10%-90% timing
    • Consider probe bandwidth limitations
  2. What considerations are important for power measurements?
    • Use appropriate voltage and current probes
    • Consider probe bandwidth and accuracy
    • Account for power factor and efficiency calculations
    • Use averaging for stable measurements

Advanced Level

  1. How would you analyze signal integrity issues using an oscilloscope?
    • Measure rise/fall times and overshoot
    • Analyze signal ringing and settling behavior
    • Use FFT for frequency domain analysis
    • Check for jitter and timing variations
  2. What are the challenges of measuring high-frequency signals?
    • Probe bandwidth must exceed signal frequency
    • Signal integrity becomes critical at high frequencies
    • Ground plane and shielding requirements increase
    • Sampling rate must be sufficient to avoid aliasing