MAX22200 Driver
API reference
Loading...
Searching...
No Matches
max22200.ipp
Go to the documentation of this file.
1
11#pragma once
12#include <cmath>
13#include <cstring>
14
15namespace max22200 {
16
17// ============================================================================
18// Constructor / Destructor
19// ============================================================================
20
21template <typename SpiType>
22MAX22200<SpiType>::MAX22200(SpiType &spi_interface)
23 : spi_interface_(spi_interface), initialized_(false), statistics_(),
24 last_fault_byte_(0xFF), cached_status_(), board_config_(),
25 fault_callback_(nullptr), fault_user_data_(nullptr),
26 state_callback_(nullptr), state_user_data_(nullptr) {}
27
28template <typename SpiType>
29MAX22200<SpiType>::MAX22200(SpiType &spi_interface, const BoardConfig &board_config)
30 : spi_interface_(spi_interface), initialized_(false), statistics_(),
31 last_fault_byte_(0xFF), cached_status_(), board_config_(board_config),
32 fault_callback_(nullptr), fault_user_data_(nullptr),
33 state_callback_(nullptr), state_user_data_(nullptr) {}
34
35template <typename SpiType>
37 if (initialized_) {
38 Deinitialize();
39 }
40}
41
42// ============================================================================
43// Initialization (per datasheet Figure 6 flowchart)
44// ============================================================================
45
46template <typename SpiType>
48 if (initialized_) {
49 return DriverStatus::OK;
50 }
51
52 // Step 0: Initialize SPI interface
53 if (!spi_interface_.Initialize()) {
54 updateStatistics(false);
56 }
57
58 // Configure SPI: Mode 0 (CPOL=0, CPHA=0), MSB first
59 if (!spi_interface_.Configure(MAX_SPI_FREQ_STANDALONE_, 0, true)) {
60 updateStatistics(false);
62 }
63
64 // Step 1: ENABLE pin HIGH, then tEN (0.5 ms, ENABLE rising edge to SPI ready) before the first access
65 spi_interface_.GpioSetActive(CtrlPin::ENABLE);
66 spi_interface_.DelayUs(ENABLE_TIME_US_);
67
68 constexpr int kMaxInitRetries = 3;
69 StatusConfig status;
70 DriverStatus result;
71
72 for (int attempt = 0; attempt < kMaxInitRetries; ++attempt) {
73 // Step 2: Read STATUS register to clear UVM and deassert nFAULT
74 result = ReadStatus(status);
75 if (result != DriverStatus::OK) {
76 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
77 updateStatistics(false);
78 return result;
79 }
80 if (GetLastFaultByte() == StatusReg::FAULT_BYTE_COMER) {
81 continue; // Communication error — retry per datasheet Figure 6
82 }
83
84 // Step 3: Write STATUS register — set ACTIVE=1, all channels off,
85 // channel-pair modes independent, mask communication fault (default)
86 status.active = true;
87 status.channels_on_mask = 0x00; // All channels off initially
88 status.communication_error_masked = true; // Mask communication fault (reset default)
89
90 result = WriteStatus(status);
91 if (result != DriverStatus::OK) {
92 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
93 updateStatistics(false);
94 return result;
95 }
96 if (GetLastFaultByte() == StatusReg::FAULT_BYTE_COMER) {
97 continue; // Communication error — retry
98 }
99
100 // Cache the status for ONCH updates
101 cached_status_ = status;
102
103 // Wake-up: tWU (2.5 ms) from ACTIVE = 1 to normal operation, the "wake-up completed?" step of
104 // Figure 6. Without it a channel switched on right after Initialize() would not drive its output.
105 spi_interface_.DelayUs(WAKE_UP_TIME_US_);
106
107 // Step 4: Read STATUS again to verify UVM is cleared
108 result = ReadStatus(status);
109 if (result != DriverStatus::OK) {
110 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
111 updateStatistics(false);
112 return result;
113 }
114 if (GetLastFaultByte() == StatusReg::FAULT_BYTE_COMER) {
115 continue; // Communication error — retry
116 }
117
118 if (status.undervoltage) {
119 // UVM still set — device may not have full power
120 // Continue anyway but note the condition
121 }
122
123 cached_status_ = status;
124 initialized_ = true;
125 updateStatistics(true);
126 return DriverStatus::OK;
127 }
128
129 // COMER persisted after all retries
130 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
131 updateStatistics(false);
133}
134
135template <typename SpiType>
137 if (!initialized_) {
138 return DriverStatus::OK;
139 }
140
141 // Disable all channels
142 DisableAllChannels();
143
144 // Set ACTIVE=0
145 cached_status_.active = false;
146 cached_status_.channels_on_mask = 0;
147 WriteStatus(cached_status_);
148
149 // ENABLE pin LOW
150 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
151
152 initialized_ = false;
153 updateStatistics(true);
154 return DriverStatus::OK;
155}
156
157template <typename SpiType>
159 return initialized_;
160}
161
162// ============================================================================
163// STATUS Register Operations
164// ============================================================================
165
166template <typename SpiType>
168 uint32_t raw;
169 DriverStatus result = readReg32(RegBank::STATUS, raw);
170 if (result == DriverStatus::OK) {
171 status.fromRegister(raw);
172 cached_status_ = status; // Keep cache in sync for FREQM, ONCH, duty limits, etc.
173 // UVM is set at every power-up and Initialize() reads it to clear it: report only afterwards.
174 if (initialized_) {
175 notifyStatusFaults(status);
176 }
177 }
178 return result;
179}
180
181template <typename SpiType>
183 uint32_t raw = status.toRegister();
184 DriverStatus result = writeReg32(RegBank::STATUS, raw);
185 if (result == DriverStatus::OK) {
186 cached_status_ = status; // Keep cache in sync so FREQM, ONCH, etc. are correct for calculations
187 }
188 return result;
189}
190
191// ============================================================================
192// Channel Configuration
193// ============================================================================
194
195template <typename SpiType>
197 const ChannelConfig &config) {
198 if (!IsValidChannel(channel)) {
199 updateStatistics(false);
201 }
202
203 // CDR requires board IFS (from SetBoardConfig); IFS is set by RREF, not per channel
204 if (config.drive_mode == DriveMode::CDR && board_config_.full_scale_current_ma == 0) {
205 updateStatistics(false);
206 return DriverStatus::INVALID_PARAMETER; // Call SetBoardConfig with RREF first
207 }
208
209 // Datasheet: SRC mode only for fCHOP < 50 kHz
210 if (config.slew_rate_control_enabled &&
211 getChopFreqKhz(cached_status_.master_clock_80khz, config.chop_freq) >= 50) {
212 updateStatistics(false);
214 }
215
216 uint32_t reg_val = config.toRegister(board_config_.full_scale_current_ma, cached_status_.master_clock_80khz);
217 DriverStatus result = writeReg32(getChannelCfgBank(channel), reg_val);
218 updateStatistics(result == DriverStatus::OK);
219 return result;
220}
221
222template <typename SpiType>
224 ChannelConfig &config) const {
225 if (!IsValidChannel(channel)) {
226 updateStatistics(false);
228 }
229
230 uint32_t raw;
231 DriverStatus result = readReg32(getChannelCfgBank(channel), raw);
232 if (result == DriverStatus::OK) {
233 // Pass context (IFS and FREQM) for proper conversion to user units
234 config.fromRegister(raw, board_config_.full_scale_current_ma, cached_status_.master_clock_80khz);
235 }
236 updateStatistics(result == DriverStatus::OK);
237 return result;
238}
239
240template <typename SpiType>
242 const ChannelConfigArray &configs) {
244 for (uint8_t ch = 0; ch < NUM_CHANNELS_; ++ch) {
245 DriverStatus result = ConfigureChannel(ch, configs[ch]);
246 if (result != DriverStatus::OK) {
247 status = result;
248 }
249 }
250 return status;
251}
252
253template <typename SpiType>
255 ChannelConfigArray &configs) const {
257 for (uint8_t ch = 0; ch < NUM_CHANNELS_; ++ch) {
258 DriverStatus result = GetChannelConfig(ch, configs[ch]);
259 if (result != DriverStatus::OK) {
260 status = result;
261 }
262 }
263 return status;
264}
265
266// ============================================================================
267// Channel Enable/Disable (ONCH bits in STATUS[31:24])
268// ============================================================================
269
270template <typename SpiType>
272 return SetChannelEnabled(channel, true);
273}
274
275template <typename SpiType>
277 return SetChannelEnabled(channel, false);
278}
279
280template <typename SpiType>
282 if (!IsValidChannel(channel)) {
283 updateStatistics(false);
285 }
286 if (enable) {
287 cached_status_.channels_on_mask |= (1u << channel);
288 } else {
289 cached_status_.channels_on_mask &= ~(1u << channel);
290 }
291 return SetChannelsOn(cached_status_.channels_on_mask);
292}
293
294template <typename SpiType>
296 return SetAllChannelsEnabled(true);
297}
298
299template <typename SpiType>
301 return SetAllChannelsEnabled(false);
302}
303
304template <typename SpiType>
306 cached_status_.channels_on_mask = enable ? 0xFFu : 0x00u;
307 return SetChannelsOn(cached_status_.channels_on_mask);
308}
309
310template <typename SpiType>
312 cached_status_.channels_on_mask = channel_mask;
313 DriverStatus result = writeReg8(RegBank::STATUS, channel_mask);
314 updateStatistics(result == DriverStatus::OK);
315 return result;
316}
317
318template <typename SpiType>
320 FullBridgeState state) {
321 if (pair_index > 3) {
322 updateStatistics(false);
324 }
325 const uint8_t shift = pair_index * 2;
326 const uint8_t pair_mask = 3u << shift;
327 uint8_t bits;
328 switch (state) {
330 bits = 0;
331 break;
333 bits = 1u << shift; // ONCHx = 1, ONCHy = 0
334 break;
336 bits = 2u << shift; // ONCHx = 0, ONCHy = 1
337 break;
339 bits = 3u << shift;
340 break;
341 default:
342 bits = 0;
343 break;
344 }
345 cached_status_.channels_on_mask = (cached_status_.channels_on_mask & ~pair_mask) | bits;
346 return SetChannelsOn(cached_status_.channels_on_mask);
347}
348
349// ============================================================================
350// Fault Operations
351// ============================================================================
352
353template <typename SpiType>
355 uint32_t raw;
356 DriverStatus result = readReg32(RegBank::FAULT, raw);
357 if (result == DriverStatus::OK) {
358 faults.fromRegister(raw);
359 notifyFaults(faults);
360 }
361 updateStatistics(result == DriverStatus::OK);
362 return result;
363}
364
365template <typename SpiType>
367 FaultStatus f;
368 return ReadFaultRegister(f);
369}
370
371template <typename SpiType>
373 FaultStatus *out_faults) const {
374 FaultStatus temp;
375 DriverStatus result = ReadFaultRegisterSelectiveClear(
376 channel_mask, channel_mask, channel_mask, channel_mask, temp);
377 if (result == DriverStatus::OK && out_faults) {
378 *out_faults = temp;
379 }
380 return result;
381}
382
383template <typename SpiType>
385 uint8_t ocp_mask, uint8_t hhf_mask, uint8_t olf_mask, uint8_t dpm_mask,
386 FaultStatus &faults) const {
387 DriverStatus result = writeCommandRegister(RegBank::FAULT, false, false);
388 if (result != DriverStatus::OK) {
389 updateStatistics(false);
390 return result;
391 }
392 const uint8_t tx[4] = {ocp_mask, hhf_mask, olf_mask, dpm_mask};
393 uint32_t raw;
394 result = readData32WithTx(tx, raw);
395 if (result == DriverStatus::OK) {
396 faults.fromRegister(raw);
397 notifyFaults(faults);
398 }
399 updateStatistics(result == DriverStatus::OK);
400 return result;
401}
402
403template <typename SpiType>
405 return ReadStatus(status);
406}
407
408template <typename SpiType>
410 // Reading the STATUS register clears fault flags
411 StatusConfig status;
412 DriverStatus result = ReadStatus(status);
413 if (result == DriverStatus::OK) {
414 cached_status_ = status;
415 }
416 return result;
417}
418
419template <typename SpiType>
421 uint32_t raw;
422 DriverStatus result = readReg32(RegBank::CFG_DPM, raw);
423 if (result == DriverStatus::OK) {
424 config.fromRegister(raw);
425 }
426 updateStatistics(result == DriverStatus::OK);
427 return result;
428}
429
430template <typename SpiType>
432 DriverStatus result = writeReg32(RegBank::CFG_DPM, config.toRegister());
433 updateStatistics(result == DriverStatus::OK);
434 return result;
435}
436
437template <typename SpiType>
439 for (uint8_t ch = 0; ch < NUM_CHANNELS_; ++ch) {
440 ChannelConfig config;
441 DriverStatus result = GetChannelConfig(ch, config);
442 if (result != DriverStatus::OK) {
443 return result;
444 }
445 bool enable = (channel_mask & (1u << ch)) != 0;
446 if (config.plunger_movement_detection_enabled != enable) {
448 result = ConfigureChannel(ch, config);
449 if (result != DriverStatus::OK) {
450 return result;
451 }
452 }
453 }
454 return DriverStatus::OK;
455}
456
457template <typename SpiType>
459 float dip_threshold_ma,
460 float debounce_ms) {
461 // DPM_TDEB counts chopping periods of the channel being monitored (CFG_DPM, TDEB = DPM_TDEB / fCHOP),
462 // and fCHOP is per channel (FREQ_CFG). Use the frequency of the channels that have DPM_EN set; with
463 // none set, use the reset value FREQ_CFG = 00 (FMAIN_DIV4).
464 bool found = false;
465 ChopFreq chop_freq = ChopFreq::FMAIN_DIV4;
466 for (uint8_t ch = 0; ch < NUM_CHANNELS_; ++ch) {
467 uint32_t raw = 0;
468 DriverStatus result = readReg32(getChannelCfgBank(ch), raw);
469 if (result != DriverStatus::OK) {
470 updateStatistics(false);
471 return result;
472 }
473 if ((raw & CfgChReg::DPM_EN_BIT) == 0) {
474 continue;
475 }
476 const auto cf = static_cast<ChopFreq>((raw >> CfgChReg::FREQ_CFG_SHIFT) & 0x03u);
477 if (found && cf != chop_freq) {
478 // One TDEB code cannot give the same time at two frequencies: use the explicit overload.
479 updateStatistics(false);
481 }
482 chop_freq = cf;
483 found = true;
484 }
485 return ConfigureDpm(start_current_ma, dip_threshold_ma, debounce_ms, chop_freq);
486}
487
488template <typename SpiType>
490 float dip_threshold_ma,
491 float debounce_ms,
492 ChopFreq chop_freq) {
493 if (board_config_.full_scale_current_ma == 0 || !(debounce_ms >= 0.0f) ||
494 !(start_current_ma >= 0.0f) || !(dip_threshold_ma >= 0.0f)) {
495 updateStatistics(false);
497 }
498 DpmConfig config;
499 DriverStatus result = ReadDpmConfig(config);
500 if (result != DriverStatus::OK) {
501 return result;
502 }
503 // ISTART = plunger_movement_start_current × (IFS/127) => value = round(start_ma / IFS_ma * 127)
504 float ratio = start_current_ma / static_cast<float>(board_config_.full_scale_current_ma);
505 uint32_t istart = static_cast<uint32_t>(ratio * 127.0f + 0.5f);
506 config.plunger_movement_start_current = (istart > 127) ? 127 : static_cast<uint8_t>(istart);
507 ratio = dip_threshold_ma / static_cast<float>(board_config_.full_scale_current_ma);
508 uint32_t ipth = static_cast<uint32_t>(ratio * 127.0f + 0.5f);
509 config.plunger_movement_current_threshold = (ipth > 15) ? 15 : static_cast<uint8_t>(ipth);
510 // TDEB = DPM_TDEB / fCHOP => DPM_TDEB = round(TDEB × fCHOP), with fCHOP = fMAIN / divider
511 // (fMAIN = 100 kHz, or 80 kHz with FREQM = 1; divider 4, 3, 2, 1 for FREQ_CFG 00..11, Table 1).
512 const float fchop_khz = getChopFreqKhzExact(cached_status_.master_clock_80khz, chop_freq);
513 const uint32_t periods = static_cast<uint32_t>(debounce_ms * fchop_khz + 0.5f);
514 config.plunger_movement_debounce_time = (periods > 15) ? 15 : static_cast<uint8_t>(periods);
515 return WriteDpmConfig(config);
516}
517
518// ============================================================================
519// Device Control
520// ============================================================================
521
522template <typename SpiType>
524 return SetDeviceEnable(true);
525}
526
527template <typename SpiType>
529 return SetDeviceEnable(false);
530}
531
532template <typename SpiType>
534 if (enable) {
535 spi_interface_.GpioSetActive(CtrlPin::ENABLE);
536 } else {
537 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
538 }
539 updateStatistics(true);
540 return DriverStatus::OK;
541}
542
543template <typename SpiType>
545 GpioSignal signal;
546 if (!spi_interface_.GpioRead(CtrlPin::FAULT, signal)) {
548 }
549 fault_active = (signal == GpioSignal::ACTIVE);
550 return DriverStatus::OK;
551}
552
553// ============================================================================
554// Raw Register Access (for debug)
555// ============================================================================
556
557template <typename SpiType>
559 uint32_t &value) const {
560 return readReg32(bank, value);
561}
562
563template <typename SpiType>
565 return writeReg32(bank, value);
566}
567
568template <typename SpiType>
570 uint8_t &value) const {
571 return readReg8(bank, value);
572}
573
574template <typename SpiType>
576 return writeReg8(bank, value);
577}
578
579template <typename SpiType>
581 return last_fault_byte_;
582}
583
584// ============================================================================
585// Statistics
586// ============================================================================
587
588template <typename SpiType>
590 return statistics_;
591}
592
593template <typename SpiType>
595 statistics_ = DriverStatistics{};
596}
597
598// ============================================================================
599// Callbacks
600// ============================================================================
601
602template <typename SpiType>
603void MAX22200<SpiType>::notifyFaults(const FaultStatus &faults) const {
604 if (fault_callback_ == nullptr) {
605 return;
606 }
607 for (uint8_t ch = 0; ch < NUM_CHANNELS_; ++ch) {
608 const uint8_t bit = static_cast<uint8_t>(1u << ch);
609 if (faults.overcurrent_channel_mask & bit) fault_callback_(ch, FaultType::OCP, fault_user_data_);
610 if (faults.hit_not_reached_channel_mask & bit) fault_callback_(ch, FaultType::HHF, fault_user_data_);
611 if (faults.open_load_fault_channel_mask & bit) fault_callback_(ch, FaultType::OLF, fault_user_data_);
612 if (faults.plunger_movement_fault_channel_mask & bit) {
613 fault_callback_(ch, FaultType::DPM, fault_user_data_);
614 }
615 }
616}
617
618template <typename SpiType>
619void MAX22200<SpiType>::notifyStatusFaults(const StatusConfig &status) const {
620 if (fault_callback_ == nullptr) {
621 return;
622 }
623 // Chip-wide flags only; the per-channel OCP / HHF / OLF / DPM summary bits are reported, with
624 // their channel, when the FAULT register is read.
625 if (status.overtemperature) fault_callback_(0xFF, FaultType::OVT, fault_user_data_);
626 if (status.undervoltage) fault_callback_(0xFF, FaultType::UVM, fault_user_data_);
627 if (status.communication_error) fault_callback_(0xFF, FaultType::COMER, fault_user_data_);
628}
629
630template <typename SpiType>
632 void *user_data) {
633 fault_callback_ = callback;
634 fault_user_data_ = user_data;
635}
636
637template <typename SpiType>
639 void *user_data) {
640 state_callback_ = callback;
641 state_user_data_ = user_data;
642}
643
644// ============================================================================
645// Private: Two-Phase SPI Protocol Implementation
646// ============================================================================
647
656template <typename SpiType>
658 bool is_write,
659 bool mode8) const {
660 uint8_t cmd_byte = CommandReg::build(bank, is_write, mode8);
661 uint8_t rx_byte = 0xFF;
662
663 // CMD pin HIGH for Command Register write
664 spi_interface_.GpioSetActive(CtrlPin::CMD);
665
666 // CS LOW → transfer 1 byte → CS HIGH
667 // Note: ESP-IDF handles CS automatically in the Transfer call
668 bool success = spi_interface_.Transfer(&cmd_byte, &rx_byte, 1);
669
670 // CMD pin LOW (prepare for data phase)
671 spi_interface_.GpioSetInactive(CtrlPin::CMD);
672
673 if (!success) {
675 }
676
677 // Store the fault flags byte returned by the device
678 last_fault_byte_ = rx_byte;
679
680 return DriverStatus::OK;
681}
682
690template <typename SpiType>
691DriverStatus MAX22200<SpiType>::writeData32(uint32_t value) const {
692 uint8_t tx[4] = {
693 static_cast<uint8_t>(value & 0xFF),
694 static_cast<uint8_t>((value >> 8) & 0xFF),
695 static_cast<uint8_t>((value >> 16) & 0xFF),
696 static_cast<uint8_t>((value >> 24) & 0xFF)};
697 uint8_t rx[4];
698
699 bool success = spi_interface_.Transfer(tx, rx, 4);
700 if (!success) {
702 }
703 return DriverStatus::OK;
704}
705
712template <typename SpiType>
713DriverStatus MAX22200<SpiType>::readData32(uint32_t &value) const {
714 uint8_t tx[4] = {0, 0, 0, 0};
715 uint8_t rx[4];
716
717 bool success = spi_interface_.Transfer(tx, rx, 4);
718 if (!success) {
720 }
721
722 value = (static_cast<uint32_t>(rx[0]) << 24) |
723 (static_cast<uint32_t>(rx[1]) << 16) |
724 (static_cast<uint32_t>(rx[2]) << 8) |
725 static_cast<uint32_t>(rx[3]);
726 return DriverStatus::OK;
727}
728
729template <typename SpiType>
730DriverStatus MAX22200<SpiType>::readData32WithTx(const uint8_t tx[4],
731 uint32_t &value) const {
732 uint8_t rx[4];
733 bool success = spi_interface_.Transfer(tx, rx, 4);
734 if (!success) {
736 }
737 value = (static_cast<uint32_t>(rx[0]) << 24) |
738 (static_cast<uint32_t>(rx[1]) << 16) |
739 (static_cast<uint32_t>(rx[2]) << 8) |
740 static_cast<uint32_t>(rx[3]);
741 return DriverStatus::OK;
742}
743
751template <typename SpiType>
752DriverStatus MAX22200<SpiType>::writeData8(uint8_t value) const {
753 uint8_t rx;
754 bool success = spi_interface_.Transfer(&value, &rx, 1);
755 if (!success) {
757 }
758 return DriverStatus::OK;
759}
760
767template <typename SpiType>
768DriverStatus MAX22200<SpiType>::readData8(uint8_t &value) const {
769 uint8_t tx = 0;
770 bool success = spi_interface_.Transfer(&tx, &value, 1);
771 if (!success) {
773 }
774 return DriverStatus::OK;
775}
776
777// ============================================================================
778// Private: Convenience wrappers (Command Register + Data)
779// ============================================================================
780
781template <typename SpiType>
782DriverStatus MAX22200<SpiType>::writeReg32(uint8_t bank,
783 uint32_t value) const {
784 DriverStatus result = writeCommandRegister(bank, true, false);
785 if (result != DriverStatus::OK) return result;
786 return writeData32(value);
787}
788
789template <typename SpiType>
790DriverStatus MAX22200<SpiType>::readReg32(uint8_t bank,
791 uint32_t &value) const {
792 DriverStatus result = writeCommandRegister(bank, false, false);
793 if (result != DriverStatus::OK) return result;
794 return readData32(value);
795}
796
797template <typename SpiType>
798DriverStatus MAX22200<SpiType>::writeReg8(uint8_t bank, uint8_t value) const {
799 DriverStatus result = writeCommandRegister(bank, true, true);
800 if (result != DriverStatus::OK) return result;
801 return writeData8(value);
802}
803
804template <typename SpiType>
805DriverStatus MAX22200<SpiType>::readReg8(uint8_t bank, uint8_t &value) const {
806 DriverStatus result = writeCommandRegister(bank, false, true);
807 if (result != DriverStatus::OK) return result;
808 return readData8(value);
809}
810
811// ============================================================================
812// Board/Scale Configuration
813// ============================================================================
814
815template <typename SpiType>
817 board_config_ = config;
818}
819
820template <typename SpiType>
822 return board_config_;
823}
824
825// ============================================================================
826// Convenience APIs: Current in Real Units (CDR Mode)
827// ============================================================================
828
829template <typename SpiType>
831 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
832 updateStatistics(false);
834 }
835
836 // Clamp to max_current_ma if set
837 if (board_config_.max_current_ma > 0 && ma > board_config_.max_current_ma) {
838 ma = board_config_.max_current_ma;
839 }
840
841 ChannelConfig config;
842 DriverStatus result = GetChannelConfig(channel, config);
843 if (result != DriverStatus::OK) {
844 return result;
845 }
846 // A current only means something on a CDR channel. Do not flip VDRnCDR here: the chip changes it only
847 // while every channel is off and TRIGA/TRIGB are low (datasheet, Programming Procedure step 5c).
848 if (config.drive_mode != DriveMode::CDR) {
849 updateStatistics(false);
851 }
852 config.hit_setpoint = static_cast<float>(ma);
853 return ConfigureChannel(channel, config);
854}
855
856template <typename SpiType>
858 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
859 updateStatistics(false);
861 }
862
863 if (board_config_.max_current_ma > 0 && ma > board_config_.max_current_ma) {
864 ma = board_config_.max_current_ma;
865 }
866
867 ChannelConfig config;
868 DriverStatus result = GetChannelConfig(channel, config);
869 if (result != DriverStatus::OK) {
870 return result;
871 }
872 // A current only means something on a CDR channel. Do not flip VDRnCDR here: the chip changes it only
873 // while every channel is off and TRIGA/TRIGB are low (datasheet, Programming Procedure step 5c).
874 if (config.drive_mode != DriveMode::CDR) {
875 updateStatistics(false);
877 }
878 config.hold_setpoint = static_cast<float>(ma);
879 return ConfigureChannel(channel, config);
880}
881
882template <typename SpiType>
884 return SetHitCurrentMa(channel, static_cast<uint32_t>(amps * 1000.0f + 0.5f));
885}
886
887template <typename SpiType>
889 return SetHoldCurrentMa(channel, static_cast<uint32_t>(amps * 1000.0f + 0.5f));
890}
891
892template <typename SpiType>
894 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
895 updateStatistics(false);
897 }
898
899 // Clamp percent to 0-100
900 if (percent < 0.0f) percent = 0.0f;
901 if (percent > 100.0f) percent = 100.0f;
902
903 // Convert percent to mA, then use SetHitCurrentMa
904 uint32_t ma = static_cast<uint32_t>((percent / 100.0f) * board_config_.full_scale_current_ma + 0.5f);
905 return SetHitCurrentMa(channel, ma);
906}
907
908template <typename SpiType>
910 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
911 updateStatistics(false);
913 }
914
915 if (percent < 0.0f) percent = 0.0f;
916 if (percent > 100.0f) percent = 100.0f;
917
918 uint32_t ma = static_cast<uint32_t>((percent / 100.0f) * board_config_.full_scale_current_ma + 0.5f);
919 return SetHoldCurrentMa(channel, ma);
920}
921
922template <typename SpiType>
923DriverStatus MAX22200<SpiType>::GetHitCurrentMa(uint8_t channel, uint32_t &ma) const {
924 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
925 updateStatistics(false);
927 }
928
929 ChannelConfig config;
930 DriverStatus result = GetChannelConfig(channel, config);
931 if (result != DriverStatus::OK) {
932 return result;
933 }
934
935 // Return user unit directly (CDR mode stores mA)
936 ma = static_cast<uint32_t>(config.hit_setpoint + 0.5f);
937 return DriverStatus::OK;
938}
939
940template <typename SpiType>
941DriverStatus MAX22200<SpiType>::GetHoldCurrentMa(uint8_t channel, uint32_t &ma) const {
942 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
943 updateStatistics(false);
945 }
946
947 ChannelConfig config;
948 DriverStatus result = GetChannelConfig(channel, config);
949 if (result != DriverStatus::OK) {
950 return result;
951 }
952
953 // Return user unit directly (CDR mode stores mA)
954 ma = static_cast<uint32_t>(config.hold_setpoint + 0.5f);
955 return DriverStatus::OK;
956}
957
958template <typename SpiType>
960 float &percent) const {
961 if (!IsValidChannel(channel)) {
962 updateStatistics(false);
964 }
965
966 ChannelConfig config;
967 DriverStatus result = GetChannelConfig(channel, config);
968 if (result != DriverStatus::OK) {
969 return result;
970 }
971
972 if (board_config_.full_scale_current_ma > 0) {
973 percent = (config.hit_setpoint / static_cast<float>(board_config_.full_scale_current_ma)) * 100.0f;
974 } else {
975 percent = 0.0f;
976 }
977 return DriverStatus::OK;
978}
979
980template <typename SpiType>
982 float &percent) const {
983 if (!IsValidChannel(channel)) {
984 updateStatistics(false);
986 }
987
988 ChannelConfig config;
989 DriverStatus result = GetChannelConfig(channel, config);
990 if (result != DriverStatus::OK) {
991 return result;
992 }
993
994 if (board_config_.full_scale_current_ma > 0) {
995 percent = (config.hold_setpoint / static_cast<float>(board_config_.full_scale_current_ma)) * 100.0f;
996 } else {
997 percent = 0.0f;
998 }
999 return DriverStatus::OK;
1000}
1001
1002// ============================================================================
1003// Convenience APIs: Duty Cycle in Percent (VDR Mode)
1004// ============================================================================
1005
1006template <typename SpiType>
1008 if (!IsValidChannel(channel)) {
1009 updateStatistics(false);
1011 }
1012
1013 // Clamp to max_duty_percent if set
1014 if (board_config_.max_duty_percent > 0 && percent > board_config_.max_duty_percent) {
1015 percent = board_config_.max_duty_percent;
1016 }
1017
1018 // Get current config to read FREQM, FREQ_CFG, SRC for duty limits
1019 ChannelConfig config;
1020 DriverStatus result = GetChannelConfig(channel, config);
1021 if (result != DriverStatus::OK) {
1022 return result;
1023 }
1024 // A duty cycle only means something on a VDR channel; never flip VDRnCDR implicitly.
1025 if (config.drive_mode != DriveMode::VDR) {
1026 updateStatistics(false);
1028 }
1029
1030 // Get duty limits based on FREQM + FREQ_CFG + SRC
1031 DutyLimits limits;
1032 result = GetDutyLimits(cached_status_.master_clock_80khz, config.chop_freq, config.slew_rate_control_enabled, limits);
1033 if (result != DriverStatus::OK) {
1034 return result;
1035 }
1036
1037 // Clamp to [δMIN, δMAX]
1038 if (percent < limits.min_percent) percent = limits.min_percent;
1039 if (percent > limits.max_percent) percent = limits.max_percent;
1040
1041 config.hit_setpoint = percent;
1042 return ConfigureChannel(channel, config);
1043}
1044
1045template <typename SpiType>
1047 if (!IsValidChannel(channel)) {
1048 updateStatistics(false);
1050 }
1051
1052 if (board_config_.max_duty_percent > 0 && percent > board_config_.max_duty_percent) {
1053 percent = board_config_.max_duty_percent;
1054 }
1055
1056 ChannelConfig config;
1057 DriverStatus result = GetChannelConfig(channel, config);
1058 if (result != DriverStatus::OK) {
1059 return result;
1060 }
1061 // A duty cycle only means something on a VDR channel; never flip VDRnCDR implicitly.
1062 if (config.drive_mode != DriveMode::VDR) {
1063 updateStatistics(false);
1065 }
1066
1067 DutyLimits limits;
1068 result = GetDutyLimits(cached_status_.master_clock_80khz, config.chop_freq, config.slew_rate_control_enabled, limits);
1069 if (result != DriverStatus::OK) {
1070 return result;
1071 }
1072
1073 if (percent < limits.min_percent) percent = limits.min_percent;
1074 if (percent > limits.max_percent) percent = limits.max_percent;
1075
1076 config.hold_setpoint = percent;
1077 return ConfigureChannel(channel, config);
1078}
1079
1080template <typename SpiType>
1082 float &percent) const {
1083 if (!IsValidChannel(channel)) {
1084 updateStatistics(false);
1086 }
1087
1088 ChannelConfig config;
1089 DriverStatus result = GetChannelConfig(channel, config);
1090 if (result != DriverStatus::OK) {
1091 return result;
1092 }
1093
1094 // Return user unit directly (VDR mode stores duty %)
1095 percent = config.hit_setpoint;
1096 return DriverStatus::OK;
1097}
1098
1099template <typename SpiType>
1101 float &percent) const {
1102 if (!IsValidChannel(channel)) {
1103 updateStatistics(false);
1105 }
1106
1107 ChannelConfig config;
1108 DriverStatus result = GetChannelConfig(channel, config);
1109 if (result != DriverStatus::OK) {
1110 return result;
1111 }
1112
1113 // Return user unit directly (VDR mode stores duty %)
1114 percent = config.hold_setpoint;
1115 return DriverStatus::OK;
1116}
1117
1118template <typename SpiType>
1120 if (!IsValidChannel(channel)) {
1122 }
1123 ChannelConfig config;
1124 DriverStatus result = GetChannelConfig(channel, config);
1125 if (result != DriverStatus::OK) {
1126 return result;
1127 }
1128 return GetDutyLimits(cached_status_.master_clock_80khz, config.chop_freq,
1129 config.slew_rate_control_enabled, limits);
1130}
1131
1132template <typename SpiType>
1133DriverStatus MAX22200<SpiType>::GetDutyLimits(bool master_clock_80khz, ChopFreq chop_freq,
1134 bool slew_rate_control_enabled, DutyLimits &limits) {
1135 // Compute fCHOP from FREQM and FREQ_CFG (datasheet Table 1)
1136 uint32_t fchop_khz;
1137 switch (chop_freq) {
1139 fchop_khz = master_clock_80khz ? 20 : 25;
1140 break;
1142 fchop_khz = master_clock_80khz ? 26 : 33; // 26.66 -> 26, 33.33 -> 33
1143 break;
1145 fchop_khz = master_clock_80khz ? 40 : 50;
1146 break;
1147 case ChopFreq::FMAIN:
1148 fchop_khz = master_clock_80khz ? 80 : 100;
1149 break;
1150 default:
1151 limits = DutyLimits();
1153 }
1154
1155 // Datasheet Table 2: Min and Max Duty Cycle
1156 if (slew_rate_control_enabled) {
1157 // SRC = 1 (Slew-Rate Controlled)
1158 if (fchop_khz == 100 || fchop_khz == 80) {
1159 // FREQ_CFG = 11 with SRC=1: not valid per datasheet (SRC only for fCHOP < 50kHz)
1160 limits = DutyLimits();
1162 }
1163 // SRC=1: δMIN = 7%, δMAX = 93% for valid frequencies
1164 limits.min_percent = 7;
1165 limits.max_percent = 93;
1166 } else {
1167 // SRC = 0 (Fast Mode)
1168 if (fchop_khz == 100 || fchop_khz == 80) {
1169 // FREQ_CFG = 11: δMIN = 8%, δMAX = 92%
1170 limits.min_percent = 8;
1171 limits.max_percent = 92;
1172 } else {
1173 // Other FREQ_CFG: δMIN = 4%, δMAX = 96%
1174 limits.min_percent = 4;
1175 limits.max_percent = 96;
1176 }
1177 }
1178
1179 return DriverStatus::OK;
1180}
1181
1182// ============================================================================
1183// Convenience APIs: HIT Time in Milliseconds
1184// ============================================================================
1185
1186template <typename SpiType>
1188 if (!IsValidChannel(channel)) {
1189 updateStatistics(false);
1191 }
1192
1193 ChannelConfig config;
1194 DriverStatus result = GetChannelConfig(channel, config);
1195 if (result != DriverStatus::OK) {
1196 return result;
1197 }
1198
1199 // Reject NaN/Inf
1200 if (!std::isfinite(ms)) {
1201 updateStatistics(false);
1203 }
1204 // Reject positive finite ms beyond 8-bit representable range (raw 1–254) for this channel's chop freq
1205 if (ms > 0.0f && ms > getMaxHitTimeMs(cached_status_.master_clock_80khz, config.chop_freq)) {
1206 updateStatistics(false);
1208 }
1209
1210 config.hit_time_ms = ms;
1211 return ConfigureChannel(channel, config);
1212}
1213
1214template <typename SpiType>
1215DriverStatus MAX22200<SpiType>::GetHitTimeMs(uint8_t channel, float &ms) const {
1216 if (!IsValidChannel(channel)) {
1217 updateStatistics(false);
1219 }
1220
1221 ChannelConfig config;
1222 DriverStatus result = GetChannelConfig(channel, config);
1223 if (result != DriverStatus::OK) {
1224 return result;
1225 }
1226
1227 // Return user unit directly
1228 ms = config.hit_time_ms;
1229 return DriverStatus::OK;
1230}
1231
1232// ============================================================================
1233// Convenience APIs: One-Shot Channel Configuration
1234// ============================================================================
1235
1236template <typename SpiType>
1238 uint8_t channel, uint32_t hit_ma, uint32_t hold_ma, float hit_time_ms,
1239 SideMode side_mode, ChopFreq chop_freq, bool slew_rate_control_enabled,
1240 bool open_load_detection_enabled, bool plunger_movement_detection_enabled,
1241 bool hit_current_check_enabled) {
1242 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
1243 updateStatistics(false);
1245 }
1246
1247 ChannelConfig config;
1248 config.drive_mode = DriveMode::CDR;
1249 config.side_mode = side_mode;
1250 config.chop_freq = chop_freq;
1251 config.slew_rate_control_enabled = slew_rate_control_enabled;
1252 config.open_load_detection_enabled = open_load_detection_enabled;
1253 config.plunger_movement_detection_enabled = plunger_movement_detection_enabled;
1254 config.hit_current_check_enabled = hit_current_check_enabled;
1255
1256 // Clamp to max_current_ma if set
1257 if (board_config_.max_current_ma > 0 && hit_ma > board_config_.max_current_ma) {
1258 hit_ma = board_config_.max_current_ma;
1259 }
1260 if (board_config_.max_current_ma > 0 && hold_ma > board_config_.max_current_ma) {
1261 hold_ma = board_config_.max_current_ma;
1262 }
1263
1264 // Set user units directly (CDR mode: mA)
1265 config.hit_setpoint = static_cast<float>(hit_ma);
1266 config.hold_setpoint = static_cast<float>(hold_ma);
1267 config.hit_time_ms = hit_time_ms;
1268
1269 return ConfigureChannel(channel, config);
1270}
1271
1272template <typename SpiType>
1274 uint8_t channel, float hit_duty_percent, float hold_duty_percent,
1275 float hit_time_ms, SideMode side_mode, ChopFreq chop_freq,
1276 bool slew_rate_control_enabled, bool open_load_detection_enabled,
1277 bool plunger_movement_detection_enabled, bool hit_current_check_enabled) {
1278 if (!IsValidChannel(channel)) {
1279 updateStatistics(false);
1281 }
1282
1283 ChannelConfig config;
1284 config.drive_mode = DriveMode::VDR;
1285 config.side_mode = side_mode;
1286 config.chop_freq = chop_freq;
1287 config.slew_rate_control_enabled = slew_rate_control_enabled;
1288 config.open_load_detection_enabled = open_load_detection_enabled;
1289 config.plunger_movement_detection_enabled = plunger_movement_detection_enabled;
1290 config.hit_current_check_enabled = hit_current_check_enabled;
1291
1292 // Clamp to max_duty_percent if set
1293 if (board_config_.max_duty_percent > 0 &&
1294 hit_duty_percent > board_config_.max_duty_percent) {
1295 hit_duty_percent = board_config_.max_duty_percent;
1296 }
1297 if (board_config_.max_duty_percent > 0 &&
1298 hold_duty_percent > board_config_.max_duty_percent) {
1299 hold_duty_percent = board_config_.max_duty_percent;
1300 }
1301
1302 // Clamp to duty limits
1303 DutyLimits limits;
1304 DriverStatus result = GetDutyLimits(cached_status_.master_clock_80khz, chop_freq,
1305 slew_rate_control_enabled, limits);
1306 if (result != DriverStatus::OK) {
1307 return result;
1308 }
1309 if (hit_duty_percent < limits.min_percent) hit_duty_percent = limits.min_percent;
1310 if (hit_duty_percent > limits.max_percent) hit_duty_percent = limits.max_percent;
1311 if (hold_duty_percent < limits.min_percent) hold_duty_percent = limits.min_percent;
1312 if (hold_duty_percent > limits.max_percent) hold_duty_percent = limits.max_percent;
1313
1314 // Set user units directly (VDR mode: duty %)
1315 config.hit_setpoint = hit_duty_percent;
1316 config.hold_setpoint = hold_duty_percent;
1317 config.hit_time_ms = hit_time_ms;
1318
1319 return ConfigureChannel(channel, config);
1320}
1321
1322// ============================================================================
1323// Private: Statistics
1324// ============================================================================
1325
1326template <typename SpiType>
1327void MAX22200<SpiType>::updateStatistics(bool success) const {
1328 statistics_.total_transfers++;
1329 if (!success) {
1330 statistics_.failed_transfers++;
1331 }
1332}
1333
1334} // namespace max22200
Main driver class for MAX22200 IC.
Definition max22200.hpp:133
DriverStatus ReadRegister8(uint8_t bank, uint8_t &value) const
Read 8-bit MSB of a register (fast 8-bit mode)
Definition max22200.ipp:569
DriverStatus SetChannelEnabled(uint8_t channel, bool enable)
Set a channel on or off (convenience when toggling from a variable)
Definition max22200.ipp:281
DriverStatus DisableDevice()
Disable device (ENABLE pin low); low-power state.
Definition max22200.ipp:528
DriverStatus SetDeviceEnable(bool enable)
Set ENABLE pin state (true = on, false = off)
Definition max22200.ipp:533
~MAX22200()
Destructor — calls Deinitialize() if initialized.
Definition max22200.ipp:36
DriverStatus WriteRegister8(uint8_t bank, uint8_t value)
Write 8-bit MSB of a register (fast 8-bit mode)
Definition max22200.ipp:575
DriverStatistics GetStatistics() const
Get accumulated driver transfer statistics.
Definition max22200.ipp:589
void SetBoardConfig(const BoardConfig &config)
Set board configuration (IFS, max current, max duty)
Definition max22200.ipp:816
bool IsInitialized() const
Check if driver completed Initialize() successfully.
Definition max22200.ipp:158
DriverStatus GetFaultPinState(bool &fault_active) const
Read nFAULT pin state (true = fault active, false = no fault)
Definition max22200.ipp:544
DriverStatus EnableDevice()
Enable device (ENABLE pin high); SPI and channels can be used.
Definition max22200.ipp:523
DriverStatus SetFullBridgeState(uint8_t pair_index, FullBridgeState state)
Set full-bridge state for a channel pair (datasheet Table 7)
Definition max22200.ipp:319
DriverStatus SetHoldDutyPercent(uint8_t channel, float percent)
Set HOLD duty cycle in percent (VDR mode)
Definition max22200.ipp:1046
static DriverStatus GetDutyLimits(bool master_clock_80khz, ChopFreq chop_freq, bool slew_rate_control_enabled, DutyLimits &limits)
Get duty cycle limits (δMIN, δMAX) for a configuration.
Definition max22200.ipp:1133
DriverStatus ReadRegister32(uint8_t bank, uint32_t &value) const
Read a 32-bit register by bank address (debug/advanced)
Definition max22200.ipp:558
DriverStatus ClearChannelFaults(uint8_t channel_mask, FaultStatus *out_faults=nullptr) const
Clear fault flags for selected channels only (MAX22200A)
Definition max22200.ipp:372
void SetFaultCallback(FaultCallback callback, void *user_data)
Register a fault event callback.
Definition max22200.ipp:631
DriverStatus ReadFaultFlags(StatusConfig &status) const
Read fault flags from STATUS register (byte 0, bits 7:1)
Definition max22200.ipp:404
DriverStatus Deinitialize()
Deinitialize — disable channels, ACTIVE=0, ENABLE low.
Definition max22200.ipp:136
DriverStatus ClearAllFaults()
Clear all fault flags (read FAULT register and discard)
Definition max22200.ipp:366
DriverStatus SetHoldCurrentA(uint8_t channel, float amps)
Set HOLD current in Amps (CDR mode)
Definition max22200.ipp:888
DriverStatus GetHitCurrentMa(uint8_t channel, uint32_t &ma) const
Get HIT current in milliamps (CDR mode)
Definition max22200.ipp:923
DriverStatus GetChannelConfig(uint8_t channel, ChannelConfig &config) const
Read a channel's configuration.
Definition max22200.ipp:223
DriverStatus WriteDpmConfig(const DpmConfig &config)
Write DPM algorithm configuration (CFG_DPM register)
Definition max22200.ipp:431
DriverStatus ConfigureAllChannels(const ChannelConfigArray &configs)
Configure all eight channels (CFG_CH0 through CFG_CH7)
Definition max22200.ipp:241
DriverStatus GetAllChannelConfigs(ChannelConfigArray &configs) const
Read configuration for all eight channels.
Definition max22200.ipp:254
DriverStatus DisableAllChannels()
Turn off all channels.
Definition max22200.ipp:300
DriverStatus WriteStatus(const StatusConfig &status)
Write the STATUS register (writable bits only)
Definition max22200.ipp:182
DriverStatus SetHoldCurrentPercent(uint8_t channel, float percent)
Set HOLD current as percentage of IFS (CDR mode)
Definition max22200.ipp:909
DriverStatus GetHoldDutyPercent(uint8_t channel, float &percent) const
Get HOLD duty cycle in percent (VDR mode)
Definition max22200.ipp:1100
DriverStatus ConfigureDpm(float start_current_ma, float dip_threshold_ma, float debounce_ms)
Configure DPM in real units (easy API)
Definition max22200.ipp:458
DriverStatus SetHitCurrentA(uint8_t channel, float amps)
Set HIT current in Amps (CDR mode)
Definition max22200.ipp:883
DriverStatus ReadDpmConfig(DpmConfig &config) const
Read DPM algorithm configuration (CFG_DPM register)
Definition max22200.ipp:420
DriverStatus DisableChannel(uint8_t channel)
Turn off a channel (set ONCHx = 0)
Definition max22200.ipp:276
void ResetStatistics()
Reset all statistics counters to zero.
Definition max22200.ipp:594
DriverStatus SetHitCurrentMa(uint8_t channel, uint32_t ma)
Set HIT current in milliamps (CDR mode)
Definition max22200.ipp:830
DriverStatus SetChannelsOn(uint8_t channel_mask)
Set which channels are on (fast multi-channel update)
Definition max22200.ipp:311
BoardConfig GetBoardConfig() const
Get current board configuration.
Definition max22200.ipp:821
DriverStatus WriteRegister32(uint8_t bank, uint32_t value)
Write a 32-bit register by bank address (debug/advanced)
Definition max22200.ipp:564
DriverStatus SetHitDutyPercent(uint8_t channel, float percent)
Set HIT duty cycle in percent (VDR mode)
Definition max22200.ipp:1007
MAX22200(SpiType &spi_interface)
Constructor (SPI only; set board config later with SetBoardConfig())
Definition max22200.ipp:22
DriverStatus ReadFaultRegisterSelectiveClear(uint8_t ocp_mask, uint8_t hhf_mask, uint8_t olf_mask, uint8_t dpm_mask, FaultStatus &faults) const
[Advanced] Read FAULT register with per-type selective clear (MAX22200A)
Definition max22200.ipp:384
DriverStatus ConfigureChannelCdr(uint8_t channel, uint32_t hit_ma, uint32_t hold_ma, float hit_time_ms, SideMode side_mode=SideMode::LOW_SIDE, ChopFreq chop_freq=ChopFreq::FMAIN_DIV4, bool slew_rate_control_enabled=false, bool open_load_detection_enabled=false, bool plunger_movement_detection_enabled=false, bool hit_current_check_enabled=false)
Configure channel in real units (CDR mode)
Definition max22200.ipp:1237
DriverStatus SetDpmEnabledChannels(uint8_t channel_mask)
Enable or disable DPM (plunger movement detection) on selected channels.
Definition max22200.ipp:438
DriverStatus GetHoldCurrentMa(uint8_t channel, uint32_t &ma) const
Get HOLD current in milliamps (CDR mode)
Definition max22200.ipp:941
DriverStatus GetHitDutyPercent(uint8_t channel, float &percent) const
Get HIT duty cycle in percent (VDR mode)
Definition max22200.ipp:1081
DriverStatus EnableChannel(uint8_t channel)
Turn on a channel (set ONCHx = 1)
Definition max22200.ipp:271
DriverStatus GetHoldCurrentPercent(uint8_t channel, float &percent) const
Get HOLD current as percentage of IFS (CDR mode)
Definition max22200.ipp:981
DriverStatus SetHoldCurrentMa(uint8_t channel, uint32_t ma)
Set HOLD current in milliamps (CDR mode)
Definition max22200.ipp:857
DriverStatus ConfigureChannel(uint8_t channel, const ChannelConfig &config)
Configure a channel (write full 32-bit CFG_CHx register)
Definition max22200.ipp:196
DriverStatus GetHitTimeMs(uint8_t channel, float &ms) const
Get HIT time in milliseconds.
Definition max22200.ipp:1215
void SetStateChangeCallback(StateChangeCallback callback, void *user_data)
Register a channel state-change callback (stored for application use)
Definition max22200.ipp:638
DriverStatus GetHitCurrentPercent(uint8_t channel, float &percent) const
Get HIT current as percentage of IFS (CDR mode)
Definition max22200.ipp:959
DriverStatus SetHitCurrentPercent(uint8_t channel, float percent)
Set HIT current as percentage of IFS (CDR mode)
Definition max22200.ipp:893
DriverStatus SetHitTimeMs(uint8_t channel, float ms)
Set HIT time in milliseconds.
Definition max22200.ipp:1187
DriverStatus Initialize()
Initialize the driver per datasheet flowchart (Figure 6)
Definition max22200.ipp:47
DriverStatus ReadFaultRegister(FaultStatus &faults) const
Read per-channel fault register (FAULT)
Definition max22200.ipp:354
DriverStatus ClearFaultFlags()
Clear STATUS fault flags by reading the STATUS register.
Definition max22200.ipp:409
DriverStatus SetAllChannelsEnabled(bool enable)
Set all channels on or off at once (convenience when toggling from a variable)
Definition max22200.ipp:305
DriverStatus ConfigureChannelVdr(uint8_t channel, float hit_duty_percent, float hold_duty_percent, float hit_time_ms, SideMode side_mode=SideMode::LOW_SIDE, ChopFreq chop_freq=ChopFreq::FMAIN_DIV4, bool slew_rate_control_enabled=false, bool open_load_detection_enabled=false, bool plunger_movement_detection_enabled=false, bool hit_current_check_enabled=false)
Configure channel in real units (VDR mode)
Definition max22200.ipp:1273
uint8_t GetLastFaultByte() const
Get last fault flags byte received from Command Register write.
Definition max22200.ipp:580
DriverStatus EnableAllChannels()
Turn on all channels.
Definition max22200.ipp:295
DriverStatus ReadStatus(StatusConfig &status) const
Read the full 32-bit STATUS register.
Definition max22200.ipp:167
constexpr uint32_t DPM_EN_BIT
DPM_EN bit (0=disabled, 1=enabled)
Definition max22200_registers.hpp:408
constexpr uint32_t FREQ_CFG_SHIFT
FREQ_CFG bit shift.
Definition max22200_registers.hpp:398
constexpr uint8_t build(uint8_t bank, bool write, bool mode8=false)
Build a Command Register value.
Definition max22200_registers.hpp:187
constexpr uint8_t STATUS
Status register (32-bit) — channel on/off, HW config, faults, ACTIVE.
Definition max22200_registers.hpp:110
constexpr uint8_t FAULT
Fault register (32-bit, read-only) — per-channel fault flags.
Definition max22200_registers.hpp:119
constexpr uint8_t CFG_DPM
DPM configuration register (32-bit) — global DPM algorithm settings.
Definition max22200_registers.hpp:120
constexpr uint8_t FAULT_BYTE_COMER
Fault byte value returned on SDO when COMER is set (per datasheet Figure 6)
Definition max22200_registers.hpp:266
Definition max22200.hpp:78
constexpr uint32_t ENABLE_TIME_US_
Enable time tEN: ENABLE rising edge to SPI ready (us)
Definition max22200_registers.hpp:85
std::array< ChannelConfig, NUM_CHANNELS_ > ChannelConfigArray
Array type for channel configurations.
Definition max22200_types.hpp:1035
@ VDR
Voltage Drive Regulation (VDRnCDR = 1) — low-side or high-side.
@ CDR
Current Drive Regulation (VDRnCDR = 0) — low-side only.
void(*)(uint8_t channel, FaultType fault_type, void *user_data) FaultCallback
Callback function type for fault events.
Definition max22200_types.hpp:1048
SideMode
High-side / Low-side selection (HSnLS bit in CFG_CHx[6])
Definition max22200_types.hpp:123
DriverStatus
Driver operation result codes.
Definition max22200_types.hpp:985
@ INITIALIZATION_ERROR
SPI/GPIO setup or ENABLE sequencing failed during init.
@ COMMUNICATION_ERROR
SPI transfer failed or COMER fault byte (0x04) detected.
@ OK
Operation completed successfully.
@ INVALID_PARAMETER
Channel index, IFS, or unit conversion argument out of range.
FullBridgeState
Full-bridge state for a channel pair (datasheet Table 7)
Definition max22200_types.hpp:973
@ Forward
ONCHx=1, ONCHy=0 — forward (control from CFG_CHy)
@ Reverse
ONCHx=0, ONCHy=1 — reverse (control from CFG_CHx)
@ Brake
ONCHx=1, ONCHy=1 — brake (both outputs driven to GND)
@ HiZ
ONCHx=0, ONCHy=0 — high impedance (both switches off)
uint32_t getChopFreqKhz(bool master_clock_80khz, ChopFreq cf)
Get chopping frequency in kHz for conversion (FREQM + FREQ_CFG)
Definition max22200_types.hpp:224
constexpr uint32_t WAKE_UP_TIME_US_
Wake-up time tWU: ACTIVE = 1 to normal operation, outputs active (us)
Definition max22200_registers.hpp:93
@ HHF
HIT current not reached (HHF)
@ OCP
Overcurrent protection (OCP)
@ UVM
Undervoltage lockout (UVM, from STATUS)
@ OVT
Overtemperature (OVT, from STATUS)
@ OLF
Open-load detection (OLF)
@ DPM
Detection of plunger movement (DPM)
@ COMER
Communication error (COMER, from STATUS)
constexpr uint32_t MAX_SPI_FREQ_STANDALONE_
Maximum SPI clock frequency without daisy chaining (Hz)
Definition max22200_registers.hpp:70
void(*)(uint8_t channel, ChannelState old_state, ChannelState new_state, void *user_data) StateChangeCallback
Callback function type for channel state changes.
Definition max22200_types.hpp:1061
constexpr uint8_t getChannelCfgBank(uint8_t channel)
Get channel configuration register bank address.
Definition max22200_registers.hpp:550
GpioSignal
Abstract signal level for control pins.
Definition max22200_spi_interface.hpp:85
@ ACTIVE
Pin function asserted (ENABLE on, CMD high, fault present on FAULT)
float getChopFreqKhzExact(bool master_clock_80khz, ChopFreq cf)
Exact chopping frequency in kHz (FREQM + FREQ_CFG, datasheet Table 1)
Definition max22200_types.hpp:240
ChopFreq
Chopping frequency setting (FREQ_CFG bits in CFG_CHx[5:4])
Definition max22200_types.hpp:211
@ FMAIN_DIV2
FreqMain / 2 (fCHOP = 50kHz if FREQM=0, 40kHz if FREQM=1). With SRC=1 use only if FREQM=1 (40kHz).
@ FMAIN
FreqMain (fCHOP = 100kHz if FREQM=0, 80kHz if FREQM=1). Not valid with SRC=1.
@ FMAIN_DIV4
FreqMain / 4 (fCHOP = 25kHz if FREQM=0, 20kHz if FREQM=1). Valid with SRC=1.
@ FMAIN_DIV3
FreqMain / 3 (fCHOP = 33.33kHz if FREQM=0, 26.66kHz if FREQM=1). Valid with SRC=1.
constexpr uint8_t NUM_CHANNELS_
Number of channels on the MAX22200.
Definition max22200_registers.hpp:60
float getMaxHitTimeMs(bool master_clock_80khz, ChopFreq cf)
Maximum representable HIT time in ms for raw values 1–254 (255 = continuous).
Definition max22200_types.hpp:313
@ FAULT
nFAULT pin — active-low open-drain fault output (read via GpioRead)
@ CMD
CMD pin — ACTIVE = Command Register phase; INACTIVE = data phase.
@ ENABLE
ENABLE pin — active-high; device sleep when INACTIVE.
Board/scale configuration for unit-based APIs.
Definition max22200_types.hpp:1089
Channel configuration structure.
Definition max22200_types.hpp:402
float hold_setpoint
HOLD setpoint: CDR = mA, VDR = duty % (0–100)
Definition max22200_types.hpp:407
bool open_load_detection_enabled
Open-load detection enable.
Definition max22200_types.hpp:421
void fromRegister(uint32_t val, uint32_t board_ifs_ma, bool master_clock_80khz_param)
Parse a 32-bit register value into user units.
Definition max22200_types.hpp:566
uint32_t toRegister(uint32_t board_ifs_ma=0, bool master_clock_80khz=false) const
Build 32-bit register value from user units.
Definition max22200_types.hpp:497
bool hit_current_check_enabled
HIT current check enable (HHF fault if IHIT not reached)
Definition max22200_types.hpp:423
float hit_time_ms
Definition max22200_types.hpp:408
SideMode side_mode
Side mode: LOW_SIDE (CDR/VDR) or HIGH_SIDE (VDR only)
Definition max22200_types.hpp:418
bool plunger_movement_detection_enabled
Plunger movement detection enable (low-side only)
Definition max22200_types.hpp:422
bool slew_rate_control_enabled
Slew rate control enable; reduces EMI; fCHOP < 50 kHz.
Definition max22200_types.hpp:420
DriveMode drive_mode
Drive mode: CDR (current) or VDR (voltage)
Definition max22200_types.hpp:417
float hit_setpoint
HIT setpoint: CDR = mA, VDR = duty % (0–100)
Definition max22200_types.hpp:406
ChopFreq chop_freq
Chopping frequency divider (FREQ_CFG[1:0])
Definition max22200_types.hpp:419
DPM (Detection of Plunger Movement) algorithm configuration.
Definition max22200_types.hpp:940
uint8_t plunger_movement_start_current
DPM_ISTART (0-127) — starting current for dip search.
Definition max22200_types.hpp:941
uint32_t toRegister() const
Encode writable CFG_DPM fields to a 32-bit register value.
Definition max22200_types.hpp:949
uint8_t plunger_movement_debounce_time
DPM_TDEB (0-15) — min dip duration in chopping periods.
Definition max22200_types.hpp:942
uint8_t plunger_movement_current_threshold
DPM_IPTH (0-15) — min dip amplitude threshold.
Definition max22200_types.hpp:943
void fromRegister(uint32_t val)
Parse a 32-bit CFG_DPM register value.
Definition max22200_types.hpp:956
Driver runtime statistics.
Definition max22200_types.hpp:1165
Duty cycle limits (δMIN, δMAX) for a given configuration.
Definition max22200_types.hpp:1136
uint8_t max_percent
Maximum duty cycle in percent (δMAX)
Definition max22200_types.hpp:1138
uint8_t min_percent
Minimum duty cycle in percent (δMIN)
Definition max22200_types.hpp:1137
Per-channel fault information from FAULT register (0x09)
Definition max22200_types.hpp:859
uint8_t overcurrent_channel_mask
OCP per-channel bitmask (bit N = channel N)
Definition max22200_types.hpp:860
uint8_t plunger_movement_fault_channel_mask
DPM per-channel bitmask (bit N = channel N)
Definition max22200_types.hpp:863
void fromRegister(uint32_t val)
Parse a 32-bit FAULT register value into per-channel bitmasks.
Definition max22200_types.hpp:872
uint8_t open_load_fault_channel_mask
OLF per-channel bitmask (bit N = channel N)
Definition max22200_types.hpp:862
uint8_t hit_not_reached_channel_mask
HHF per-channel bitmask (bit N = channel N)
Definition max22200_types.hpp:861
STATUS register structure.
Definition max22200_types.hpp:683
void fromRegister(uint32_t val)
Parse a 32-bit STATUS register value into this structure.
Definition max22200_types.hpp:760
bool communication_error_masked
COMER mask (default=true); when true COMER does not assert FAULT pin.
Definition max22200_types.hpp:695
bool undervoltage
UVM fault flag (read-only); clear by reading STATUS at init.
Definition max22200_types.hpp:715
uint8_t channels_on_mask
Definition max22200_types.hpp:685
uint32_t toRegister() const
Build 32-bit STATUS register value from writable fields.
Definition max22200_types.hpp:737
bool active
Global enable (0=low-power, 1=normal); set to 1 during init.
Definition max22200_types.hpp:706