The Embedded New Testament

The "Holy Bible" for embedded engineers


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

Protocol Analysis and Debugging

Effective protocol analysis accelerates bring-up, reveals timing bugs, and derisks field failures. This guide covers instrumentation, capture strategy, timing analysis, and a practical checklist for UART, SPI, I2C, CAN, and Ethernet-based protocols.


🧠 Concept First

Analysis vs Debugging

Concept: Protocol analysis is systematic observation, debugging is targeted problem-solving. Why it matters: Understanding this distinction helps you choose the right tools and approach for your situation. Minimal example: Use a logic analyzer to observe normal UART communication vs. use it to find a specific timing bug. Try it: First analyze a working protocol, then use the same tools to debug a broken one. Takeaways: Analysis builds understanding, debugging solves specific problems.

Tool Selection Strategy

Concept: Different tools reveal different aspects of protocol behavior. Why it matters: Using the wrong tool can miss critical information or waste time. Minimal example: Compare logic analyzer vs. oscilloscope for SPI signal analysis. Try it: Analyze the same signal with different tools and compare what you learn. Takeaways: Choose tools based on what you need to observe, not what’s convenient.


Instruments and Measurement Fundamentals

Logic Analyzer Selection and Configuration

Digital Sampling Theory Logic analyzers capture digital signals at discrete time intervals. The choice of sample rate and memory depth fundamentally affects what you can observe and analyze.

Why Sample Rate Matters

Memory Depth Considerations Memory depth determines how long you can capture at a given sample rate:

Protocol Decoder Capabilities Modern logic analyzers include built-in decoders for common protocols:

// Calculate minimum sample rate for reliable edge detection
uint32_t calculate_min_sample_rate(uint32_t signal_frequency, uint32_t edge_accuracy_ns) {
    // Nyquist: 2x signal frequency minimum
    uint32_t nyquist_rate = signal_frequency * 2;
    
    // Edge accuracy: higher sample rate = better edge precision
    uint32_t accuracy_rate = 1000000000 / edge_accuracy_ns;
    
    // Use the higher of the two rates, with 10x margin for noisy signals
    uint32_t min_rate = MAX(nyquist_rate, accuracy_rate) * 10;
    
    return min_rate;
}

// Example: 1MHz SPI clock with 10ns edge accuracy
// Min sample rate = MAX(2MHz, 100MHz) * 10 = 1GHz

Oscilloscope Measurements and Analysis

Analog vs Digital Analysis While logic analyzers excel at digital signal analysis, oscilloscopes provide crucial analog information that digital tools cannot capture.

Signal Integrity Fundamentals

Bandwidth Requirements Oscilloscope bandwidth should be 3-5x the highest frequency component:

Probe Selection Considerations

Protocol Analyzers and Specialized Tools

When to Use Specialized Tools

Tool Selection Criteria


Advanced Capture Strategy

Trigger Configuration for Complex Scenarios

Trigger Philosophy Effective triggering reduces capture time and focuses analysis on relevant events. The goal is to capture the right data at the right time.

Multi-Condition Triggers Complex systems often require sophisticated trigger conditions:

Trigger Optimization

// Multi-condition trigger setup
typedef struct {
    uint8_t trigger_type;      // EDGE, PATTERN, STATE, PROTOCOL
    uint8_t trigger_source;    // Channel number
    uint8_t trigger_condition; // RISING, FALLING, HIGH, LOW
    uint32_t trigger_value;    // Pattern or threshold value
    uint32_t pre_trigger;      // Pre-trigger samples
    uint32_t post_trigger;     // Post-trigger samples
} trigger_config_t;

// Configure complex trigger for UART frame error
err_t configure_uart_error_trigger(trigger_config_t *config) {
    config->trigger_type = TRIGGER_PROTOCOL;
    config->trigger_source = UART_RX_CHANNEL;
    config->trigger_condition = UART_FRAME_ERROR;
    config->pre_trigger = 1000;   // 1ms pre-trigger
    config->post_trigger = 5000;  // 5ms post-trigger
    
    return configure_logic_analyzer_trigger(config);
}

Correlated Multi-Instrument Capture

Why Multi-Instrument Correlation Matters Modern embedded systems have multiple communication interfaces and subsystems. Correlating data from multiple instruments provides a complete picture of system behavior.

Correlation Strategies

Correlation Challenges

// Synchronize multiple instruments for comprehensive analysis
typedef struct {
    uint32_t timestamp_ns;
    uint8_t instrument_id;
    uint8_t event_type;
    uint32_t event_data;
} correlated_event_t;

// Event correlation buffer
#define MAX_CORRELATED_EVENTS 1000
static correlated_event_t event_buffer[MAX_CORRELATED_EVENTS];
static uint32_t event_count = 0;

// Add event from any instrument
void add_correlated_event(uint8_t instrument_id, uint8_t event_type, uint32_t event_data) {
    if (event_count < MAX_CORRELATED_EVENTS) {
        event_buffer[event_count].timestamp_ns = get_high_resolution_time();
        event_buffer[event_count].instrument_id = instrument_id;
        event_buffer[event_count].event_type = event_type;
        event_buffer[event_count].event_data = event_data;
        event_count++;
    }
}

UART Protocol Analysis

Advanced UART Timing Analysis

UART Timing Fundamentals UART communication relies on precise timing relationships between the transmitter and receiver. Understanding these relationships is crucial for reliable communication.

Bit Timing Analysis

Timing Budget Philosophy UART timing budgets must account for:

Why Timing Budgets Matter

// UART timing budget analysis
typedef struct {
    uint32_t baud_rate;
    uint32_t bit_time_ns;
    uint32_t inter_byte_time_ns;
    uint32_t isr_latency_ns;
    uint32_t buffer_processing_time_ns;
    uint32_t margin_ns;
} uart_timing_budget_t;

uart_timing_budget_t calculate_uart_timing(uint32_t baud_rate, uint8_t data_bits, 
                                          uint8_t stop_bits, uint8_t parity) {
    uart_timing_budget_t budget = {0};
    
    budget.baud_rate = baud_rate;
    budget.bit_time_ns = 1000000000 / baud_rate;
    
    // Calculate frame time (start + data + parity + stop)
    uint8_t frame_bits = 1 + data_bits + (parity ? 1 : 0) + stop_bits;
    uint32_t frame_time_ns = frame_bits * budget.bit_time_ns;
    
    // Inter-byte time includes frame time plus any idle time
    budget.inter_byte_time_ns = frame_time_ns;
    
    // Calculate required ISR latency
    budget.isr_latency_ns = budget.bit_time_ns / 2;  // Must sample in middle of bit
    
    // Buffer processing time (copy, parse, queue)
    budget.buffer_processing_time_ns = 1000;  // 1µs typical
    
    // Required margin
    budget.margin_ns = budget.inter_byte_time_ns - budget.isr_latency_ns - 
                      budget.buffer_processing_time_ns;
    
    return budget;
}

// Example: 115200 baud, 8N1
// Bit time = 8.68 µs
// Frame time = 10 bits × 8.68 µs = 86.8 µs
// ISR latency must be < 4.34 µs (half bit time)
// Buffer processing: 1 µs
// Margin: 86.8 - 4.34 - 1 = 81.46 µs

UART Error Detection and Analysis

Error Types and Their Causes UART communication can fail in several ways, each with different causes and implications:

Frame Errors

Parity Errors

Overrun Errors

Noise Errors

Error Statistics and Analysis Understanding error patterns helps identify root causes:

// UART error statistics and analysis
typedef struct {
    uint32_t frame_errors;
    uint32_t parity_errors;
    uint32_t overrun_errors;
    uint32_t noise_errors;
    uint32_t total_errors;
    uint32_t total_frames;
    float error_rate;
} uart_error_stats_t;

// Analyze UART errors from logic analyzer capture
uart_error_stats_t analyze_uart_errors(uint8_t *capture_data, uint32_t capture_length) {
    uart_error_stats_t stats = {0};
    
    for (uint32_t i = 0; i < capture_length - 10; i++) {
        // Look for UART frame patterns
        if (is_uart_start_bit(capture_data, i)) {
            stats.total_frames++;
            
            // Check for frame errors
            if (has_frame_error(capture_data, i)) {
                stats.frame_errors++;
            }
            
            // Check for parity errors
            if (has_parity_error(capture_data, i)) {
                stats.parity_errors++;
            }
            
            // Check for overrun
            if (has_overrun_error(capture_data, i)) {
                stats.overrun_errors++;
            }
        }
    }
    
    stats.total_errors = stats.frame_errors + stats.parity_errors + 
                        stats.overrun_errors + stats.noise_errors;
    
    if (stats.total_frames > 0) {
        stats.error_rate = (float)stats.total_errors / stats.total_frames * 100.0f;
    }
    
    return stats;
}

UART Signal Quality Analysis

Signal Quality Metrics Signal quality directly affects communication reliability and performance:

Rise and Fall Times

Overshoot and Undershoot

Jitter Analysis

Noise Analysis

// UART signal quality analysis
typedef struct {
    float scl_rise_time_ns;
    float scl_fall_time_ns;
    float sda_rise_time_ns;
    float sda_fall_time_ns;
    float pull_up_resistance_ohms;
    float bus_capacitance_pf;
    float noise_margin_mv;
} uart_signal_quality_t;

uart_signal_quality_t analyze_uart_signal_quality(float *analog_waveform, 
                                                 uint32_t samples, 
                                                 float sample_period_ns) {
    uart_signal_quality_t quality = {0};
    
    // Calculate rise and fall times
    quality.scl_rise_time_ns = calculate_rise_time(analog_waveform, samples, sample_period_ns);
    quality.scl_fall_time_ns = calculate_fall_time(analog_waveform, samples, sample_period_ns);
    quality.sda_rise_time_ns = calculate_rise_time(analog_waveform, samples, sample_period_ns);
    quality.sda_fall_time_ns = calculate_fall_time(analog_waveform, samples, sample_period_ns);
    
    // Calculate pull-up resistance from rise time
    // τ = RC, where τ is rise time, R is pull-up resistance, C is bus capacitance
    float avg_rise_time_ns = (quality.scl_rise_time_ns + quality.sda_rise_time_ns) / 2.0f;
    quality.bus_capacitance_pf = estimate_bus_capacitance();  // From PCB design
    quality.pull_up_resistance_ohms = (avg_rise_time_ns * 1e-9) / 
                                     (quality.bus_capacitance_pf * 1e-12);
    
    // Calculate noise margin
    float v_ih_min = 0.7 * V_DD;  // Input high minimum
    float v_il_max = 0.3 * V_DD;  // Input low maximum
    float v_oh_min = 0.9 * V_DD;  // Output high minimum
    float v_ol_max = 0.1 * V_DD;  // Output low maximum
    
    quality.noise_margin_mv = MIN(v_oh_min - v_ih_min, v_il_max - v_ol_max) * 1000.0f;
    
    return quality;
}

SPI Protocol Analysis

SPI Timing Analysis and Validation

SPI Timing Fundamentals SPI communication relies on precise timing relationships between clock and data signals. Understanding these relationships is essential for reliable communication.

Clock Polarity and Phase SPI supports four timing modes (CPOL/CPHA combinations):

Timing Parameters

Why Timing Validation Matters

// SPI timing parameters and validation
typedef struct {
    uint32_t clock_frequency_hz;
    uint32_t clock_period_ns;
    uint32_t setup_time_ns;
    uint32_t hold_time_ns;
    uint32_t clock_to_output_ns;
    uint32_t chip_select_delay_ns;
    uint8_t clock_polarity;    // CPOL: 0 or 1
    uint8_t clock_phase;       // CPHA: 0 or 1
} spi_timing_params_t;

// Validate SPI timing against device specifications
err_t validate_spi_timing(spi_timing_params_t *measured, spi_timing_params_t *required) {
    err_t result = ERR_OK;
    
    // Check setup time
    if (measured->setup_time_ns < required->setup_time_ns) {
        printf("Setup time violation: %lu ns < %lu ns required\n", 
               measured->setup_time_ns, required->setup_time_ns);
        result = ERR_TIMEOUT;
    }
    
    // Check hold time
    if (measured->hold_time_ns < required->hold_time_ns) {
        printf("Hold time violation: %lu ns < %lu ns required\n", 
               measured->hold_time_ns, required->hold_time_ns);
        result = ERR_TIMEOUT;
    }
    
    // Check clock frequency
    if (measured->clock_frequency_hz > required->clock_frequency_hz) {
        printf("Clock frequency violation: %lu Hz > %lu Hz max\n", 
               measured->clock_frequency_hz, required->clock_frequency_hz);
        result = ERR_TIMEOUT;
    }
    
    return result;
}

SPI Protocol Decoding and Analysis

SPI Frame Structure Understanding SPI frame structure is essential for protocol analysis:

Common SPI Patterns

Protocol Analysis Techniques

// SPI frame decoder
typedef struct {
    uint8_t *data;
    uint32_t data_length;
    uint8_t chip_select;
    uint32_t timestamp_ns;
    uint8_t frame_type;  // READ, WRITE, READ_WRITE
    uint8_t address;
    uint16_t payload_length;
} spi_frame_t;

// Decode SPI frames from logic analyzer capture
spi_frame_t* decode_spi_frames(uint8_t *capture_data, uint32_t capture_length,
                               spi_timing_params_t *timing, uint32_t *frame_count) {
    // Allocate frame buffer
    spi_frame_t *frames = malloc(MAX_SPI_FRAMES * sizeof(spi_frame_t));
    *frame_count = 0;
    
    uint32_t bit_index = 0;
    uint32_t frame_start = 0;
    
    for (uint32_t i = 0; i < capture_length && *frame_count < MAX_SPI_FRAMES; i++) {
        // Detect chip select assertion
        if (is_chip_select_asserted(capture_data, i)) {
            frame_start = i;
            frames[*frame_count].timestamp_ns = i * timing->clock_period_ns;
            frames[*frame_count].chip_select = get_chip_select_number(capture_data, i);
        }
        
        // Detect chip select deassertion
        if (is_chip_select_deasserted(capture_data, i) && frame_start > 0) {
            // Frame complete, decode it
            uint32_t frame_length = i - frame_start;
            frames[*frame_count].data_length = frame_length / 8;  // 8 bits per byte
            
            // Allocate data buffer
            frames[*frame_count].data = malloc(frames[*frame_count].data_length);
            
            // Decode data bits
            decode_spi_data_bits(capture_data, frame_start, frame_length, 
                                frames[*frame_count].data, timing);
            
            // Determine frame type and address
            analyze_spi_frame_content(&frames[*frame_count]);
            
            (*frame_count)++;
            frame_start = 0;
        }
    }
    
    return frames;
}

I2C Protocol Analysis

I2C Timing and Signal Analysis

I2C Timing Fundamentals I2C communication uses open-drain signaling with pull-up resistors. Understanding the timing relationships is crucial for reliable communication.

Clock and Data Relationships

Timing Parameters

Signal Quality Considerations

// I2C timing parameters
typedef struct {
    uint32_t clock_frequency_hz;
    uint32_t clock_period_ns;
    uint32_t setup_time_ns;
    uint32_t hold_time_ns;
    uint32_t data_setup_time_ns;
    uint32_t data_hold_time_ns;
    uint32_t clock_low_time_ns;
    uint32_t clock_high_time_ns;
    uint32_t start_hold_time_ns;
    uint32_t stop_setup_time_ns;
} i2c_timing_params_t;

// I2C signal quality analysis
typedef struct {
    float scl_rise_time_ns;
    float scl_fall_time_ns;
    float sda_rise_time_ns;
    float sda_fall_time_ns;
    float pull_up_resistance_ohms;
    float bus_capacitance_pf;
    float noise_margin_mv;
} i2c_signal_quality_t;

I2C Protocol Decoding and Error Analysis

I2C Frame Structure Understanding I2C frame structure is essential for protocol analysis:

Common I2C Patterns

Error Detection and Analysis


CAN Protocol Analysis

CAN Bit Timing and Signal Analysis

CAN Bit Timing Fundamentals CAN communication uses sophisticated bit timing to ensure reliable communication in noisy environments.

Bit Timing Components

Sample Point Optimization

Why Bit Timing Matters

// CAN bit timing parameters
typedef struct {
    uint32_t nominal_bit_rate;
    uint32_t data_bit_rate;  // For CAN-FD
    uint32_t prescaler;
    uint32_t time_quanta;
    uint32_t sync_seg;
    uint32_t tseg1;
    uint32_t tseg2;
    uint32_t sjw;  // Synchronization jump width
    float sample_point_percent;
} can_bit_timing_t;

// Calculate CAN bit timing from oscilloscope measurements
can_bit_timing_t calculate_can_bit_timing(float *can_h_waveform, float *can_l_waveform,
                                         uint32_t samples, float sample_period_ns) {
    can_bit_timing_t timing = {0};
    
    // Find bit boundaries
    uint32_t *bit_boundaries = find_can_bit_boundaries(can_h_waveform, can_l_waveform, 
                                                      samples);
    uint32_t bit_count = count_can_bits(bit_boundaries);
    
    if (bit_count >= 2) {
        // Calculate nominal bit rate
        uint32_t bit_period_samples = bit_boundaries[1] - bit_boundaries[0];
        uint32_t bit_period_ns = bit_period_samples * sample_period_ns;
        timing.nominal_bit_rate = 1000000000 / bit_period_ns;
        
        // Calculate time quanta (typically 1/16 of bit time)
        timing.time_quanta = bit_period_ns / 16;
        
        // Calculate sample point (typically 87.5% of bit time)
        timing.sample_point_percent = 87.5f;
        
        // Calculate time segments
        timing.sync_seg = 1;  // Always 1 time quantum
        timing.tseg1 = 13;    // 13 time quanta (typical)
        timing.tseg2 = 2;     // 2 time quanta (typical)
        timing.sjw = 1;       // 1 time quantum (typical)
    }
    
    return timing;
}

CAN Protocol Decoding and Error Analysis

CAN Frame Structure Understanding CAN frame structure is essential for protocol analysis:

Error Types and Analysis

Bus Analysis Techniques


Advanced Timing and Jitter Analysis

High-Resolution Timing Measurements

Timing Measurement Philosophy High-resolution timing measurements provide insights into system performance that lower-resolution measurements cannot capture.

Measurement Techniques

Why High Resolution Matters

// High-resolution timer for embedded systems
typedef struct {
    uint32_t timer_frequency_hz;
    uint32_t timer_resolution_ns;
    uint32_t overflow_count;
    uint32_t last_timestamp;
} high_res_timer_t;

// Initialize high-resolution timer
err_t init_high_res_timer(high_res_timer_t *timer) {
    // Configure DWT cycle counter (ARM Cortex-M)
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
    
    timer->timer_frequency_hz = SystemCoreClock;
    timer->timer_resolution_ns = 1000000000 / timer->timer_frequency_hz;
    timer->overflow_count = 0;
    timer->last_timestamp = DWT->CYCCNT;
    
    return ERR_OK;
}

Jitter Analysis and Statistics

Jitter Fundamentals Jitter is the variation in timing of signal edges. Understanding jitter is crucial for high-performance systems.

Jitter Types

Jitter Analysis Techniques

Jitter Impact on Systems

// Jitter analysis structure
typedef struct {
    uint32_t min_latency_ns;
    uint32_t max_latency_ns;
    uint32_t avg_latency_ns;
    uint32_t jitter_rms_ns;
    uint32_t jitter_peak_peak_ns;
    uint32_t samples_50th_percentile_ns;
    uint32_t samples_95th_percentile_ns;
    uint32_t samples_99th_percentile_ns;
    uint32_t samples_99_9th_percentile_ns;
} jitter_analysis_t;

// Analyze jitter from timing measurements
jitter_analysis_t analyze_jitter(uint32_t *latency_samples, uint32_t sample_count) {
    jitter_analysis_t analysis = {0};
    
    if (sample_count == 0) return analysis;
    
    // Calculate basic statistics
    analysis.min_latency_ns = latency_samples[0];
    analysis.max_latency_ns = latency_samples[0];
    uint64_t sum = 0;
    
    for (uint32_t i = 0; i < sample_count; i++) {
        if (latency_samples[i] < analysis.min_latency_ns) {
            analysis.min_latency_ns = latency_samples[i];
        }
        if (latency_samples[i] > analysis.max_latency_ns) {
            analysis.max_latency_ns = latency_samples[i];
        }
        sum += latency_samples[i];
    }
    
    analysis.avg_latency_ns = (uint32_t)(sum / sample_count);
    analysis.jitter_peak_peak_ns = analysis.max_latency_ns - analysis.min_latency_ns;
    
    // Calculate RMS jitter
    uint64_t variance_sum = 0;
    for (uint32_t i = 0; i < sample_count; i++) {
        int32_t diff = (int32_t)latency_samples[i] - (int32_t)analysis.avg_latency_ns;
        variance_sum += (uint64_t)(diff * diff);
    }
    float variance = (float)variance_sum / sample_count;
    analysis.jitter_rms_ns = (uint32_t)sqrtf(variance);
    
    // Calculate percentiles
    uint32_t *sorted_samples = malloc(sample_count * sizeof(uint32_t));
    memcpy(sorted_samples, latency_samples, sample_count * sizeof(uint32_t));
    qsort(sorted_samples, sample_count, sizeof(uint32_t), compare_uint32);
    
    analysis.samples_50th_percentile_ns = sorted_samples[sample_count / 2];
    analysis.samples_95th_percentile_ns = sorted_samples[(sample_count * 95) / 100];
    analysis.samples_99th_percentile_ns = sorted_samples[(sample_count * 99) / 100];
    analysis.samples_99_9th_percentile_ns = sorted_samples[(sample_count * 999) / 1000];
    
    free(sorted_samples);
    return analysis;
}

Comprehensive Debugging Methodology

Structured Debug Checklist Implementation

Debug Methodology Philosophy Structured debugging provides a systematic approach to problem solving that increases the likelihood of finding and fixing issues quickly.

Debug Process Benefits

Debug Checklist Structure The debug checklist provides a framework for:

// Debug session management
typedef struct {
    char description[256];
    uint32_t start_timestamp;
    uint32_t end_timestamp;
    uint8_t severity;  // 1=Low, 2=Medium, 3=High, 4=Critical
    uint8_t status;    // 0=Open, 1=Investigating, 2=Resolved, 3=Closed
    char root_cause[512];
    char solution[512];
    char notes[1024];
} debug_session_t;

// Debug checklist implementation
typedef struct {
    uint8_t step_completed;
    char step_description[256];
    uint8_t result;  // 0=Pass, 1=Pass with issues, 2=Fail
    char findings[512];
    char next_actions[512];
} debug_checklist_step_t;

#define DEBUG_STEPS_COUNT 7
static debug_checklist_step_t debug_checklist[DEBUG_STEPS_COUNT] = {
    {0, "Reproduce and bound the problem", 0, "", ""},
    {0, "Validate physical layer", 0, "", ""},
    {0, "Verify timing", 0, "", ""},
    {0, "Confirm configuration", 0, "", ""},
    {0, "Inspect protocol semantics", 0, "", ""},
    {0, "Introduce instrumentation", 0, "", ""},
    {0, "Mitigate, then fix", 0, "", ""}
};

Automated Problem Detection

Automation Philosophy Automated problem detection provides early warning of issues before they become critical problems.

Detection Strategy

Detection Benefits

```c // Automated problem detection system typedef struct { uint32_t check_interval_ms; uint32_t last_check_time; uint8_t enabled; uint32_t problem_count; char last_problem[256]; } problem_detector_t;

// Problem detection rules typedef struct { char rule_name[64]; uint8_t (*check_function)(void); uint8_t severity; uint32_t threshold; uint32_t current_count; } detection_rule_t;

// Example detection rules static detection_rule_t detection_rules[] = { {“UART_Frame_Errors”, check_uart_frame_errors, 2, 5, 0}, {“SPI_Timing_Violations”, check_spi_timing_violations, 3, 3, 0}, {“I2C_Bus_Errors”, check_i2c_bus_errors, 2, 10, 0}, {“CAN_CRC_Errors”, check_can_crc_errors, 3, 2, 0}, {“Network_Timeout”, check_network_timeout, 4, 1, 0} };

// Run automated problem detection void run_problem_detection(void) { uint32_t current_time = sys_now();

for (int i = 0; i < sizeof(detection_rules) / sizeof(detection_rules[0]); i++) {
    if (detection_rules[i].check_function()) {
        detection_rules[i].current_count++;
        
        if (detection_rules[i].current_count >= detection_rules[i].threshold) {
            // Problem detected
            printf("PROBLEM DETECTED: %s (Severity: %d)\n", 
                   detection_rules[i].rule_name, detection_rules[i].severity);
            
            // Take automatic action based on severity
            take_automatic_action(detection_rules[i].severity);
            
            // Reset counter
            detection_rules[i].current_count = 0;
        }
    } else {
        // Reset counter if no problem
        detection_rules[i].current_count = 0;
    }
} }

🧪 Guided Labs

Lab 1: Logic Analyzer Setup and Basic Capture

Objective: Set up a logic analyzer and capture basic protocol data. Setup: Logic analyzer connected to UART or SPI signals. Steps:

  1. Connect probes to signal lines
  2. Configure sample rate and memory depth
  3. Set up basic triggers
  4. Capture normal communication
  5. Analyze captured data Expected Outcome: Understanding of basic logic analyzer operation and data interpretation.

Lab 2: Protocol Decoding and Analysis

Objective: Use protocol decoders to analyze captured data. Setup: Logic analyzer with protocol decoding capabilities. Steps:

  1. Capture protocol data
  2. Configure protocol decoder parameters
  3. Analyze decoded messages
  4. Identify timing issues
  5. Document findings Expected Outcome: Ability to use protocol decoders effectively for analysis.

Lab 3: Timing Analysis and Debugging

Objective: Use timing analysis to find protocol problems. Setup: System with known or suspected timing issues. Steps:

  1. Establish timing requirements
  2. Measure actual timing
  3. Compare requirements vs. actual
  4. Identify violations
  5. Implement fixes Expected Outcome: Understanding of timing analysis and debugging techniques.

Check Yourself

Understanding Questions

  1. Tool Selection: When would you choose a logic analyzer over an oscilloscope?
  2. Sample Rate: How do you determine the minimum sample rate for your analysis?
  3. Trigger Strategy: What makes an effective trigger configuration?
  4. Protocol Decoding: How do protocol decoders help with analysis?

Application Questions

  1. Analysis Planning: How do you plan a protocol analysis session?
  2. Problem Isolation: How do you isolate protocol problems systematically?
  3. Tool Configuration: What are the key parameters to configure for your analysis?
  4. Data Interpretation: How do you interpret the data you capture?

Troubleshooting Questions

  1. Capture Issues: What are the most common problems with protocol capture?
  2. Timing Problems: How do you identify and fix timing-related protocol issues?
  3. Tool Limitations: What are the limitations of different analysis tools?
  4. Analysis Efficiency: How do you make protocol analysis more efficient?

Advanced Concepts

Practical Applications

This enhanced Protocol Analysis document now provides a better balance of conceptual explanations, practical insights, and technical implementation details that embedded engineers can use to understand and implement effective protocol analysis and debugging strategies.