Usage Examples

Complete Application Examples

This document provides practical, ready-to-use code examples for common TLE92466ED applications.

Example 1: Basic Current Control

Single Channel ICC Mode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "TLE92466ED.hpp"
#include "my_platform_hal.hpp"

using namespace TLE92466ED;

int main() {
    // 1. Create HAL and driver
    MyPlatformHAL hal;
    Driver driver(hal);
    
    // 2. Initialize
    if (auto result = driver.init(); !result) {
        error("Init failed");
        return -1;
    }
    
    // 3. Configure channel (in Config Mode by default)
    driver.set_channel_mode(Channel::CH0, ChannelMode::ICC);
    driver.set_current_setpoint(Channel::CH0, 1500); // 1.5A
    
    // 4. Enter Mission Mode
    driver.enter_mission_mode();
    
    // 5. Enable channel
    driver.enable_channel(Channel::CH0, true);
    
    // 6. Monitor
    while (running) {
        auto current = driver.get_average_current(Channel::CH0);
        if (current) {
            log("Current: %d mA", *current);
        }
        
        delay(100ms);
    }
    
    // 7. Cleanup
    driver.disable_all_channels();
    
    return 0;
}

Example 2: Parallel Operation (High Current)

4A Solenoid Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void configure_high_current_solenoid() {
    Driver driver(hal);
    driver.init();
    
    // Enter Config Mode
    driver.enter_config_mode();
    
    // Enable parallel operation CH0+CH3
    driver.set_parallel_operation(ParallelPair::CH0_CH3, true);
    
    // Configure both channels identically
    ChannelConfig config{
        .mode = ChannelMode::ICC,
        .current_setpoint_ma = 3500,  // 3.5A total
        .slew_rate = SlewRate::MEDIUM_2V5_US,
        .open_load_threshold = 4  // 4/8 = 50%
    };
    
    driver.configure_channel(Channel::CH0, config);
    driver.configure_channel(Channel::CH3, config);
    
    // Enter Mission Mode and enable
    driver.enter_mission_mode();
    driver.enable_channel(Channel::CH0, true);
    driver.enable_channel(Channel::CH3, true);
    
    log("High-current mode active: 3.5A");
}

Example 3: Proportional Valve with Dither

Precision Position Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class ProportionalValveController {
public:
    ProportionalValveController(Driver& driver, Channel channel)
        : driver_(driver), channel_(channel) {}
    
    void init() {
        driver_.enter_config_mode();
        
        // Configure ICC mode
        driver_.set_channel_mode(channel_, ChannelMode::ICC);
        
        // Configure dither for smooth positioning
        // 100mA amplitude, 50Hz frequency
        driver_.configure_dither(channel_,
            200,   // step_size (gives ~100mA amplitude)
            25,    // num_steps
            10);   // flat_steps
        
        // Set initial position (50% = 1000mA)
        driver_.set_current_setpoint(channel_, 1000);
        
        driver_.enter_mission_mode();
        driver_.enable_channel(channel_, true);
    }
    
    void set_position(float percent) {
        // Convert 0-100% to 0-2000mA
        uint16_t current = static_cast<uint16_t>(percent * 20.0f);
        driver_.set_current_setpoint(channel_, current);
    }
    
    float get_actual_position() {
        auto current = driver_.get_average_current(channel_);
        if (!current) return -1.0f;
        return *current / 20.0f;  // Convert to %
    }
    
private:
    Driver& driver_;
    Channel channel_;
};

// Usage
int main() {
    MyPlatformHAL hal;
    Driver driver(hal);
    driver.init();
    
    ProportionalValveController valve(driver, Channel::CH0);
    valve.init();
    
    // Ramp to 75% position
    for (float pos = 0.0f; pos <= 75.0f; pos += 1.0f) {
        valve.set_position(pos);
        delay(10ms);
    }
    
    // Monitor position
    float actual = valve.get_actual_position();
    log("Position: %.1f%%", actual);
}

Example 4: Multi-Channel Solenoid Array

Valve Bank Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class SolenoidArray {
public:
    SolenoidArray(Driver& driver) : driver_(driver) {}
    
    void init() {
        driver_.enter_config_mode();
        
        // Configure all 6 channels
        for (int i = 0; i < 6; i++) {
            Channel ch = static_cast<Channel>(i);
            
            driver_.set_channel_mode(ch, ChannelMode::ICC);
            
            ChannelConfig config{
                .mode = ChannelMode::ICC,
                .current_setpoint_ma = 1200,  // 1.2A per solenoid
                .slew_rate = SlewRate::FAST_5V0_US,
                .open_load_threshold = 3
            };
            driver_.configure_channel(ch, config);
        }
        
        driver_.enter_mission_mode();
    }
    
    void activate_pattern(uint8_t pattern) {
        // Pattern is 6-bit mask for channels
        for (int i = 0; i < 6; i++) {
            bool enable = (pattern & (1 << i)) != 0;
            driver_.enable_channel(static_cast<Channel>(i), enable);
        }
    }
    
    void sequence_test() {
        // Test each channel sequentially
        for (int i = 0; i < 6; i++) {
            log("Testing channel %d", i);
            activate_pattern(1 << i);
            delay(500ms);
            
            auto diag = driver_.get_channel_diagnostics(static_cast<Channel>(i));
            if (diag) {
                if (diag->overcurrent) log("  OC fault!");
                if (diag->open_load) log("  Open load!");
                log("  Current: %d mA", diag->average_current);
            }
        }
        activate_pattern(0);  // All off
    }
    
private:
    Driver& driver_;
};

// Usage
int main() {
    MyPlatformHAL hal;
    Driver driver(hal);
    driver.init();
    
    SolenoidArray array(driver);
    array.init();
    
    // Run sequence test
    array.sequence_test();
    
    // Activate specific pattern
    array.activate_pattern(0b00101010);  // CH1, CH3, CH5
}

Example 5: Fault-Tolerant Operation

Robust Error Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
class FaultTolerantDriver {
public:
    FaultTolerantDriver(Driver& driver) : driver_(driver), fault_count_(0) {}
    
    bool init_with_retry(int max_retries = 3) {
        for (int attempt = 1; attempt <= max_retries; attempt++) {
            log("Init attempt %d/%d", attempt, max_retries);
            
            if (auto result = driver_.init(); result) {
                log("Init successful");
                start_monitoring();
                return true;
            } else {
                log("Init failed: %d", static_cast<int>(result.error()));
                delay(1s);
            }
        }
        log("Init failed after %d attempts", max_retries);
        return false;
    }
    
    void monitor_and_recover() {
        auto status = driver_.get_device_status();
        if (!status) return;
        
        // Check for critical faults
        if (status->ot_error) {
            handle_overtemperature();
        }
        
        if (status->vbat_uv || status->vbat_ov) {
            handle_supply_fault();
        }
        
        // Check each channel
        for (int i = 0; i < 6; i++) {
            Channel ch = static_cast<Channel>(i);
            auto diag = driver_.get_channel_diagnostics(ch);
            
            if (diag) {
                if (diag->overcurrent) {
                    handle_overcurrent(ch);
                }
                if (diag->short_to_ground) {
                    handle_short_circuit(ch);
                }
                if (diag->open_load) {
                    handle_open_load(ch);
                }
            }
        }
    }
    
private:
    Driver& driver_;
    int fault_count_;
    
    void handle_overtemperature() {
        log("Over-temperature detected");
        
        // Reduce all currents by 50%
        for (int i = 0; i < 6; i++) {
            Channel ch = static_cast<Channel>(i);
            auto current = driver_.get_current_setpoint(ch);
            if (current) {
                driver_.set_current_setpoint(ch, *current / 2);
            }
        }
        
        // Clear fault after cooling
        delay(10s);
        driver_.clear_faults();
    }
    
    void handle_overcurrent(Channel ch) {
        log("Over-current on channel %d", static_cast<int>(ch));
        
        // Disable channel
        driver_.enable_channel(ch, false);
        
        // Reduce setpoint
        auto current = driver_.get_current_setpoint(ch);
        if (current && *current > 500) {
            uint16_t new_current = *current - 200;  // Reduce by 200mA
            driver_.set_current_setpoint(ch, new_current);
        }
        
        // Clear fault and retry
        delay(100ms);
        driver_.clear_faults();
        driver_.enable_channel(ch, true);
    }
    
    void handle_short_circuit(Channel ch) {
        log("Short circuit on channel %d", static_cast<int>(ch));
        
        // Permanent disable
        driver_.enable_channel(ch, false);
        
        fault_count_++;
        if (fault_count_ > 3) {
            log("Too many faults, entering safe mode");
            driver_.disable_all_channels();
        }
    }
    
    void handle_open_load(Channel ch) {
        log("Open load on channel %d", static_cast<int>(ch));
        
        // Check if transient
        delay(50ms);
        auto diag = driver_.get_channel_diagnostics(ch);
        if (diag && !diag->open_load) {
            log("Transient open load, continuing");
            return;
        }
        
        // Persistent open load - disable
        driver_.enable_channel(ch, false);
    }
    
    void handle_supply_fault() {
        log("Supply voltage fault");
        
        // Enter safe state
        driver_.disable_all_channels();
        
        // Wait for supply to stabilize
        delay(1s);
        
        // Re-enable if supply recovered
        auto status = driver_.get_device_status();
        if (status && !status->vbat_uv && !status->vbat_ov) {
            log("Supply recovered");
            driver_.clear_faults();
        }
    }
    
    void start_monitoring() {
        // Start periodic monitoring task
        // (platform-specific implementation)
    }
};

Example 6: Automotive Application

Engine Management Actuators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class EngineActuatorControl {
public:
    enum class Actuator {
        VVT_INTAKE,      // Variable valve timing (CH0)
        VVT_EXHAUST,     // Variable valve timing (CH1)
        EGR_VALVE,       // EGR control (CH2)
        WASTEGATE,       // Turbo wastegate (CH3)
        FUEL_PUMP,       // High-pressure fuel pump (CH4+CH5 parallel)
    };
    
    EngineActuatorControl(Driver& driver) : driver_(driver) {}
    
    void init() {
        driver_.enter_config_mode();
        
        // Configure VVT actuators (proportional, with dither)
        configure_vvt(Channel::CH0);
        configure_vvt(Channel::CH1);
        
        // Configure EGR valve (proportional)
        ChannelConfig egr_config{
            .mode = ChannelMode::ICC,
            .current_setpoint_ma = 0,  // Start closed
            .slew_rate = SlewRate::MEDIUM_2V5_US,
            .open_load_threshold = 4
        };
        driver_.configure_channel(Channel::CH2, egr_config);
        
        // Configure wastegate (fast response)
        ChannelConfig wg_config{
            .mode = ChannelMode::ICC,
            .current_setpoint_ma = 0,
            .slew_rate = SlewRate::FASTEST_10V0_US,
            .open_load_threshold = 3
        };
        driver_.configure_channel(Channel::CH3, wg_config);
        
        // Configure fuel pump (parallel, high current)
        driver_.set_parallel_operation(ParallelPair::CH4_CH5, true);
        ChannelConfig pump_config{
            .mode = ChannelMode::ICC,
            .current_setpoint_ma = 3000,  // 3A
            .slew_rate = SlewRate::MEDIUM_2V5_US,
            .open_load_threshold = 5
        };
        driver_.configure_channel(Channel::CH4, pump_config);
        driver_.configure_channel(Channel::CH5, pump_config);
        
        driver_.enter_mission_mode();
        
        // Enable critical actuators
        driver_.enable_channel(Channel::CH4, true);  // Fuel pump
        driver_.enable_channel(Channel::CH5, true);
    }
    
    void set_vvt_position(Actuator actuator, float degrees) {
        Channel ch = (actuator == Actuator::VVT_INTAKE) ? Channel::CH0 : Channel::CH1;
        
        // Convert degrees to current (example: 0-50° = 500-1500mA)
        uint16_t current = 500 + static_cast<uint16_t>(degrees * 20.0f);
        driver_.set_current_setpoint(ch, current);
    }
    
    void set_egr_opening(float percent) {
        // 0-100% = 0-1800mA
        uint16_t current = static_cast<uint16_t>(percent * 18.0f);
        driver_.set_current_setpoint(Channel::CH2, current);
        
        if (percent > 0) {
            driver_.enable_channel(Channel::CH2, true);
        }
    }
    
private:
    Driver& driver_;
    
    void configure_vvt(Channel ch) {
        ChannelConfig config{
            .mode = ChannelMode::ICC,
            .current_setpoint_ma = 1000,  // Mid position
            .slew_rate = SlewRate::MEDIUM_2V5_US,
            .open_load_threshold = 4,
            .deep_dither_enabled = true,
            .dither_step_size = 50,  // Small dither for positioning
            .dither_steps = 10,
            .dither_flat = 5
        };
        driver_.configure_channel(ch, config);
    }
};

Example 7: Diagnostic Dashboard

System Health Monitoring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void print_system_status(Driver& driver) {
    std::cout << "\n=== TLE92466ED System Status ===\n";
    
    // Global status
    auto status = driver.get_device_status();
    if (status) {
        std::cout << "Mode: " << (status->config_mode ? "Config" : "Mission") << "\n";
        std::cout << "Init Done: " << (status->init_done ? "Yes" : "No") << "\n";
        std::cout << "Faults: " << (status->any_fault ? "YES" : "No") << "\n";
        
        if (status->any_fault) {
            std::cout << "  VBAT UV: " << status->vbat_uv << "\n";
            std::cout << "  VBAT OV: " << status->vbat_ov << "\n";
            std::cout << "  OT Warn: " << status->ot_warning << "\n";
            std::cout << "  OT Error: " << status->ot_error << "\n";
        }
    }
    
    // Channel status
    std::cout << "\nChannel Status:\n";
    std::cout << "CH | Setpt | Actual | DC%  | Faults\n";
    std::cout << "---+-------+--------+------+--------\n";
    
    for (int i = 0; i < 6; i++) {
        Channel ch = static_cast<Channel>(i);
        
        auto setpoint = driver.get_current_setpoint(ch);
        auto actual = driver.get_average_current(ch);
        auto dc = driver.get_duty_cycle(ch);
        auto diag = driver.get_channel_diagnostics(ch);
        
        std::cout << " " << i << " | ";
        std::cout << (setpoint ? std::to_string(*setpoint) : "ERR") << " | ";
        std::cout << (actual ? std::to_string(*actual) : "ERR") << " | ";
        std::cout << (dc ? std::to_string(*dc / 655) : "ERR") << " | ";
        
        if (diag) {
            if (diag->overcurrent) std::cout << "OC ";
            if (diag->short_to_ground) std::cout << "SG ";
            if (diag->open_load) std::cout << "OL ";
        }
        std::cout << "\n";
    }
    
    std::cout << "================================\n";
}

Practical Tips

1. Always Check Return Values

1
2
3
4
5
6
7
8
9
// Bad
driver.init();
driver.enable_channel(Channel::CH0, true);

// Good
if (auto result = driver.init(); !result) {
    handle_error(result.error());
    return;
}

2. Service Watchdogs

1
2
3
4
5
6
// In main loop
while (running) {
    driver.reload_spi_watchdog(1000);
    // ... other operations
    delay(50ms);  // Must be < watchdog timeout
}

3. Monitor Before Critical Operations

1
2
3
4
5
6
7
// Before enabling high-current load
auto status = driver.get_device_status();
if (status && !status->vbat_uv && !status->ot_error) {
    driver.enable_channel(Channel::CH0, true);
} else {
    log("Cannot enable: system fault");
}

4. Graceful Shutdown

1
2
3
4
5
6
7
8
9
void shutdown() {
    // Disable all outputs
    driver.disable_all_channels();
    
    // Return to safe state
    driver.enter_config_mode();
    
    // Device is now safe
}