HF-MAX22200 Driver 0.1.0-dev
HF-MAX22200 C++ Driver
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>
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>
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_) {
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, power-up delay
65 spi_interface_.GpioSetActive(CtrlPin::ENABLE);
66 spi_interface_.DelayUs(500); // 0.5ms power-up delay
67
68 constexpr int kMaxInitRetries = 3;
71
72 for (int attempt = 0; attempt < kMaxInitRetries; ++attempt) {
73 // Step 2: Read STATUS register to clear UVM and deassert nFAULT
75 if (result != DriverStatus::OK) {
76 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
77 updateStatistics(false);
78 return result;
79 }
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
91 if (result != DriverStatus::OK) {
92 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
93 updateStatistics(false);
94 return result;
95 }
97 continue; // Communication error — retry
98 }
99
100 // Cache the status for ONCH updates
101 cached_status_ = status;
102
103 // Step 4: Read STATUS again to verify UVM is cleared
105 if (result != DriverStatus::OK) {
106 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
107 updateStatistics(false);
108 return result;
109 }
111 continue; // Communication error — retry
112 }
113
114 if (status.undervoltage) {
115 // UVM still set — device may not have full power
116 // Continue anyway but note the condition
117 }
118
119 cached_status_ = status;
120 initialized_ = true;
121 updateStatistics(true);
122 return DriverStatus::OK;
123 }
124
125 // COMER persisted after all retries
126 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
127 updateStatistics(false);
129}
130
131template <typename SpiType>
133 if (!initialized_) {
134 return DriverStatus::OK;
135 }
136
137 // Disable all channels
139
140 // Set ACTIVE=0
141 cached_status_.active = false;
142 cached_status_.channels_on_mask = 0;
143 WriteStatus(cached_status_);
144
145 // ENABLE pin LOW
146 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
147
148 initialized_ = false;
149 updateStatistics(true);
150 return DriverStatus::OK;
151}
152
153template <typename SpiType>
155 return initialized_;
156}
157
158// ============================================================================
159// STATUS Register Operations
160// ============================================================================
161
162template <typename SpiType>
166 if (result == DriverStatus::OK) {
167 status.fromRegister(raw);
168 cached_status_ = status; // Keep cache in sync for FREQM, ONCH, duty limits, etc.
169 }
170 return result;
171}
172
173template <typename SpiType>
175 uint32_t raw = status.toRegister();
176 DriverStatus result = writeReg32(RegBank::STATUS, raw);
177 if (result == DriverStatus::OK) {
178 cached_status_ = status; // Keep cache in sync so FREQM, ONCH, etc. are correct for calculations
179 }
180 return result;
181}
182
183// ============================================================================
184// Channel Configuration
185// ============================================================================
186
187template <typename SpiType>
189 const ChannelConfig &config) {
190 if (!IsValidChannel(channel)) {
191 updateStatistics(false);
193 }
194
195 // CDR requires board IFS (from SetBoardConfig); IFS is set by RREF, not per channel
196 if (config.drive_mode == DriveMode::CDR && board_config_.full_scale_current_ma == 0) {
197 updateStatistics(false);
198 return DriverStatus::INVALID_PARAMETER; // Call SetBoardConfig with RREF first
199 }
200
201 // Datasheet: SRC mode only for fCHOP < 50 kHz
202 if (config.slew_rate_control_enabled &&
203 getChopFreqKhz(cached_status_.master_clock_80khz, config.chop_freq) >= 50) {
204 updateStatistics(false);
206 }
207
208 uint32_t reg_val = config.toRegister(board_config_.full_scale_current_ma, cached_status_.master_clock_80khz);
210 updateStatistics(result == DriverStatus::OK);
211 return result;
212}
213
214template <typename SpiType>
216 ChannelConfig &config) const {
217 if (!IsValidChannel(channel)) {
218 updateStatistics(false);
220 }
221
224 if (result == DriverStatus::OK) {
225 // Pass context (IFS and FREQM) for proper conversion to user units
226 config.fromRegister(raw, board_config_.full_scale_current_ma, cached_status_.master_clock_80khz);
227 }
228 updateStatistics(result == DriverStatus::OK);
229 return result;
230}
231
232template <typename SpiType>
244
245template <typename SpiType>
257
258// ============================================================================
259// Channel Enable/Disable (ONCH bits in STATUS[31:24])
260// ============================================================================
261
262template <typename SpiType>
266
267template <typename SpiType>
271
272template <typename SpiType>
274 if (!IsValidChannel(channel)) {
275 updateStatistics(false);
277 }
278 if (enable) {
279 cached_status_.channels_on_mask |= (1u << channel);
280 } else {
281 cached_status_.channels_on_mask &= ~(1u << channel);
282 }
283 return SetChannelsOn(cached_status_.channels_on_mask);
284}
285
286template <typename SpiType>
290
291template <typename SpiType>
295
296template <typename SpiType>
298 cached_status_.channels_on_mask = enable ? 0xFFu : 0x00u;
299 return SetChannelsOn(cached_status_.channels_on_mask);
300}
301
302template <typename SpiType>
309
310template <typename SpiType>
313 if (pair_index > 3) {
314 updateStatistics(false);
316 }
317 const uint8_t shift = pair_index * 2;
318 const uint8_t pair_mask = 3u << shift;
320 switch (state) {
322 bits = 0;
323 break;
325 bits = 1u << shift; // ONCHx = 1, ONCHy = 0
326 break;
328 bits = 2u << shift; // ONCHx = 0, ONCHy = 1
329 break;
331 bits = 3u << shift;
332 break;
333 default:
334 bits = 0;
335 break;
336 }
337 cached_status_.channels_on_mask = (cached_status_.channels_on_mask & ~pair_mask) | bits;
338 return SetChannelsOn(cached_status_.channels_on_mask);
339}
340
341// ============================================================================
342// Fault Operations
343// ============================================================================
344
345template <typename SpiType>
349 if (result == DriverStatus::OK) {
350 faults.fromRegister(raw);
351 }
352 updateStatistics(result == DriverStatus::OK);
353 return result;
354}
355
356template <typename SpiType>
361
362template <typename SpiType>
373
374template <typename SpiType>
377 FaultStatus &faults) const {
378 DriverStatus result = writeCommandRegister(RegBank::FAULT, false, false);
379 if (result != DriverStatus::OK) {
380 updateStatistics(false);
381 return result;
382 }
385 result = readData32WithTx(tx, raw);
386 if (result == DriverStatus::OK) {
387 faults.fromRegister(raw);
388 }
389 updateStatistics(result == DriverStatus::OK);
390 return result;
391}
392
393template <typename SpiType>
397
398template <typename SpiType>
400 // Reading the STATUS register clears fault flags
403 if (result == DriverStatus::OK) {
404 cached_status_ = status;
405 }
406 return result;
407}
408
409template <typename SpiType>
413 if (result == DriverStatus::OK) {
414 config.fromRegister(raw);
415 }
416 updateStatistics(result == DriverStatus::OK);
417 return result;
418}
419
420template <typename SpiType>
422 DriverStatus result = writeReg32(RegBank::CFG_DPM, config.toRegister());
423 updateStatistics(result == DriverStatus::OK);
424 return result;
425}
426
427template <typename SpiType>
429 for (uint8_t ch = 0; ch < NUM_CHANNELS_; ++ch) {
432 if (result != DriverStatus::OK) {
433 return result;
434 }
435 bool enable = (channel_mask & (1u << ch)) != 0;
436 if (config.plunger_movement_detection_enabled != enable) {
437 config.plunger_movement_detection_enabled = enable;
439 if (result != DriverStatus::OK) {
440 return result;
441 }
442 }
443 }
444 return DriverStatus::OK;
445}
446
447template <typename SpiType>
449 float dip_threshold_ma,
450 float debounce_ms) {
451 if (board_config_.full_scale_current_ma == 0) {
452 updateStatistics(false);
454 }
457 if (result != DriverStatus::OK) {
458 return result;
459 }
460 // ISTART = plunger_movement_start_current × (IFS/127) => value = round(start_ma / IFS_ma * 127)
461 float ratio = start_current_ma / static_cast<float>(board_config_.full_scale_current_ma);
462 config.plunger_movement_start_current = static_cast<uint8_t>(ratio * 127.0f + 0.5f);
463 if (config.plunger_movement_start_current > 127) config.plunger_movement_start_current = 127;
464 ratio = dip_threshold_ma / static_cast<float>(board_config_.full_scale_current_ma);
465 uint32_t ipth = static_cast<uint32_t>(ratio * 127.0f + 0.5f);
466 config.plunger_movement_current_threshold = (ipth > 15) ? 15 : static_cast<uint8_t>(ipth);
467 // TDEB = plunger_movement_debounce_time / fCHOP; use actual fCHOP from cached STATUS (FREQM + FMAIN_DIV4)
469 float periods_f = debounce_ms * static_cast<float>(fchop_khz) + 0.5f;
470 uint32_t periods = static_cast<uint32_t>(periods_f);
471 config.plunger_movement_debounce_time = (periods > 15) ? 15 : static_cast<uint8_t>(periods);
472 return WriteDpmConfig(config);
473}
474
475// ============================================================================
476// Device Control
477// ============================================================================
478
479template <typename SpiType>
483
484template <typename SpiType>
488
489template <typename SpiType>
491 if (enable) {
492 spi_interface_.GpioSetActive(CtrlPin::ENABLE);
493 } else {
494 spi_interface_.GpioSetInactive(CtrlPin::ENABLE);
495 }
496 updateStatistics(true);
497 return DriverStatus::OK;
498}
499
500template <typename SpiType>
509
510// ============================================================================
511// Raw Register Access (for debug)
512// ============================================================================
513
514template <typename SpiType>
519
520template <typename SpiType>
524
525template <typename SpiType>
530
531template <typename SpiType>
535
536template <typename SpiType>
538 return last_fault_byte_;
539}
540
541// ============================================================================
542// Statistics
543// ============================================================================
544
545template <typename SpiType>
547 return statistics_;
548}
549
550template <typename SpiType>
552 statistics_ = DriverStatistics{};
553}
554
555// ============================================================================
556// Callbacks
557// ============================================================================
558
559template <typename SpiType>
561 void *user_data) {
562 fault_callback_ = callback;
563 fault_user_data_ = user_data;
564}
565
566template <typename SpiType>
568 void *user_data) {
569 state_callback_ = callback;
570 state_user_data_ = user_data;
571}
572
573// ============================================================================
574// Private: Two-Phase SPI Protocol Implementation
575// ============================================================================
576
585template <typename SpiType>
587 bool is_write,
588 bool mode8) const {
590 uint8_t rx_byte = 0xFF;
591
592 // CMD pin HIGH for Command Register write
593 spi_interface_.GpioSetActive(CtrlPin::CMD);
594
595 // CS LOW → transfer 1 byte → CS HIGH
596 // Note: ESP-IDF handles CS automatically in the Transfer call
597 bool success = spi_interface_.Transfer(&cmd_byte, &rx_byte, 1);
598
599 // CMD pin LOW (prepare for data phase)
600 spi_interface_.GpioSetInactive(CtrlPin::CMD);
601
602 if (!success) {
604 }
605
606 // Store the fault flags byte returned by the device
607 last_fault_byte_ = rx_byte;
608
609 return DriverStatus::OK;
610}
611
619template <typename SpiType>
620DriverStatus MAX22200<SpiType>::writeData32(uint32_t value) const {
621 uint8_t tx[4] = {
622 static_cast<uint8_t>(value & 0xFF),
623 static_cast<uint8_t>((value >> 8) & 0xFF),
624 static_cast<uint8_t>((value >> 16) & 0xFF),
625 static_cast<uint8_t>((value >> 24) & 0xFF)};
626 uint8_t rx[4];
627
628 bool success = spi_interface_.Transfer(tx, rx, 4);
629 if (!success) {
631 }
632 return DriverStatus::OK;
633}
634
641template <typename SpiType>
642DriverStatus MAX22200<SpiType>::readData32(uint32_t &value) const {
643 uint8_t tx[4] = {0, 0, 0, 0};
644 uint8_t rx[4];
645
646 bool success = spi_interface_.Transfer(tx, rx, 4);
647 if (!success) {
649 }
650
651 value = (static_cast<uint32_t>(rx[0]) << 24) |
652 (static_cast<uint32_t>(rx[1]) << 16) |
653 (static_cast<uint32_t>(rx[2]) << 8) |
654 static_cast<uint32_t>(rx[3]);
655 return DriverStatus::OK;
656}
657
658template <typename SpiType>
659DriverStatus MAX22200<SpiType>::readData32WithTx(const uint8_t tx[4],
660 uint32_t &value) const {
661 uint8_t rx[4];
662 bool success = spi_interface_.Transfer(tx, rx, 4);
663 if (!success) {
665 }
666 value = (static_cast<uint32_t>(rx[0]) << 24) |
667 (static_cast<uint32_t>(rx[1]) << 16) |
668 (static_cast<uint32_t>(rx[2]) << 8) |
669 static_cast<uint32_t>(rx[3]);
670 return DriverStatus::OK;
671}
672
680template <typename SpiType>
681DriverStatus MAX22200<SpiType>::writeData8(uint8_t value) const {
682 uint8_t rx;
683 bool success = spi_interface_.Transfer(&value, &rx, 1);
684 if (!success) {
686 }
687 return DriverStatus::OK;
688}
689
696template <typename SpiType>
697DriverStatus MAX22200<SpiType>::readData8(uint8_t &value) const {
698 uint8_t tx = 0;
699 bool success = spi_interface_.Transfer(&tx, &value, 1);
700 if (!success) {
702 }
703 return DriverStatus::OK;
704}
705
706// ============================================================================
707// Private: Convenience wrappers (Command Register + Data)
708// ============================================================================
709
710template <typename SpiType>
711DriverStatus MAX22200<SpiType>::writeReg32(uint8_t bank,
712 uint32_t value) const {
713 DriverStatus result = writeCommandRegister(bank, true, false);
714 if (result != DriverStatus::OK) return result;
715 return writeData32(value);
716}
717
718template <typename SpiType>
719DriverStatus MAX22200<SpiType>::readReg32(uint8_t bank,
720 uint32_t &value) const {
721 DriverStatus result = writeCommandRegister(bank, false, false);
722 if (result != DriverStatus::OK) return result;
723 return readData32(value);
724}
725
726template <typename SpiType>
727DriverStatus MAX22200<SpiType>::writeReg8(uint8_t bank, uint8_t value) const {
728 DriverStatus result = writeCommandRegister(bank, true, true);
729 if (result != DriverStatus::OK) return result;
730 return writeData8(value);
731}
732
733template <typename SpiType>
734DriverStatus MAX22200<SpiType>::readReg8(uint8_t bank, uint8_t &value) const {
735 DriverStatus result = writeCommandRegister(bank, false, true);
736 if (result != DriverStatus::OK) return result;
737 return readData8(value);
738}
739
740// ============================================================================
741// Board/Scale Configuration
742// ============================================================================
743
744template <typename SpiType>
746 board_config_ = config;
747}
748
749template <typename SpiType>
751 return board_config_;
752}
753
754// ============================================================================
755// Convenience APIs: Current in Real Units (CDR Mode)
756// ============================================================================
757
758template <typename SpiType>
760 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
761 updateStatistics(false);
763 }
764
765 // Clamp to max_current_ma if set
766 if (board_config_.max_current_ma > 0 && ma > board_config_.max_current_ma) {
767 ma = board_config_.max_current_ma;
768 }
769
772 if (result != DriverStatus::OK) {
773 return result;
774 }
775 config.drive_mode = DriveMode::CDR;
776 config.hit_setpoint = static_cast<float>(ma);
778}
779
780template <typename SpiType>
782 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
783 updateStatistics(false);
785 }
786
787 if (board_config_.max_current_ma > 0 && ma > board_config_.max_current_ma) {
788 ma = board_config_.max_current_ma;
789 }
790
793 if (result != DriverStatus::OK) {
794 return result;
795 }
796 config.drive_mode = DriveMode::CDR;
797 config.hold_setpoint = static_cast<float>(ma);
799}
800
801template <typename SpiType>
803 return SetHitCurrentMa(channel, static_cast<uint32_t>(amps * 1000.0f + 0.5f));
804}
805
806template <typename SpiType>
808 return SetHoldCurrentMa(channel, static_cast<uint32_t>(amps * 1000.0f + 0.5f));
809}
810
811template <typename SpiType>
813 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
814 updateStatistics(false);
816 }
817
818 // Clamp percent to 0-100
819 if (percent < 0.0f) percent = 0.0f;
820 if (percent > 100.0f) percent = 100.0f;
821
822 // Convert percent to mA, then use SetHitCurrentMa
823 uint32_t ma = static_cast<uint32_t>((percent / 100.0f) * board_config_.full_scale_current_ma + 0.5f);
824 return SetHitCurrentMa(channel, ma);
825}
826
827template <typename SpiType>
829 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
830 updateStatistics(false);
832 }
833
834 if (percent < 0.0f) percent = 0.0f;
835 if (percent > 100.0f) percent = 100.0f;
836
837 uint32_t ma = static_cast<uint32_t>((percent / 100.0f) * board_config_.full_scale_current_ma + 0.5f);
838 return SetHoldCurrentMa(channel, ma);
839}
840
841template <typename SpiType>
843 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
844 updateStatistics(false);
846 }
847
850 if (result != DriverStatus::OK) {
851 return result;
852 }
853
854 // Return user unit directly (CDR mode stores mA)
855 ma = static_cast<uint32_t>(config.hit_setpoint + 0.5f);
856 return DriverStatus::OK;
857}
858
859template <typename SpiType>
861 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
862 updateStatistics(false);
864 }
865
868 if (result != DriverStatus::OK) {
869 return result;
870 }
871
872 // Return user unit directly (CDR mode stores mA)
873 ma = static_cast<uint32_t>(config.hold_setpoint + 0.5f);
874 return DriverStatus::OK;
875}
876
877template <typename SpiType>
879 float &percent) const {
880 if (!IsValidChannel(channel)) {
881 updateStatistics(false);
883 }
884
887 if (result != DriverStatus::OK) {
888 return result;
889 }
890
891 if (board_config_.full_scale_current_ma > 0) {
892 percent = (config.hit_setpoint / static_cast<float>(board_config_.full_scale_current_ma)) * 100.0f;
893 } else {
894 percent = 0.0f;
895 }
896 return DriverStatus::OK;
897}
898
899template <typename SpiType>
901 float &percent) const {
902 if (!IsValidChannel(channel)) {
903 updateStatistics(false);
905 }
906
909 if (result != DriverStatus::OK) {
910 return result;
911 }
912
913 if (board_config_.full_scale_current_ma > 0) {
914 percent = (config.hold_setpoint / static_cast<float>(board_config_.full_scale_current_ma)) * 100.0f;
915 } else {
916 percent = 0.0f;
917 }
918 return DriverStatus::OK;
919}
920
921// ============================================================================
922// Convenience APIs: Duty Cycle in Percent (VDR Mode)
923// ============================================================================
924
925template <typename SpiType>
927 if (!IsValidChannel(channel)) {
928 updateStatistics(false);
930 }
931
932 // Clamp to max_duty_percent if set
933 if (board_config_.max_duty_percent > 0 && percent > board_config_.max_duty_percent) {
934 percent = board_config_.max_duty_percent;
935 }
936
937 // Get current config to read FREQM, FREQ_CFG, SRC for duty limits
940 if (result != DriverStatus::OK) {
941 return result;
942 }
943
944 // Get duty limits based on FREQM + FREQ_CFG + SRC
946 result = GetDutyLimits(cached_status_.master_clock_80khz, config.chop_freq, config.slew_rate_control_enabled, limits);
947 if (result != DriverStatus::OK) {
948 return result;
949 }
950
951 // Clamp to [δMIN, δMAX]
952 if (percent < limits.min_percent) percent = limits.min_percent;
953 if (percent > limits.max_percent) percent = limits.max_percent;
954
955 // Set user unit directly (VDR mode)
956 config.drive_mode = DriveMode::VDR;
957 config.hit_setpoint = percent;
959}
960
961template <typename SpiType>
963 if (!IsValidChannel(channel)) {
964 updateStatistics(false);
966 }
967
968 if (board_config_.max_duty_percent > 0 && percent > board_config_.max_duty_percent) {
969 percent = board_config_.max_duty_percent;
970 }
971
974 if (result != DriverStatus::OK) {
975 return result;
976 }
977
979 result = GetDutyLimits(cached_status_.master_clock_80khz, config.chop_freq, config.slew_rate_control_enabled, limits);
980 if (result != DriverStatus::OK) {
981 return result;
982 }
983
984 if (percent < limits.min_percent) percent = limits.min_percent;
985 if (percent > limits.max_percent) percent = limits.max_percent;
986
987 // Set user unit directly (VDR mode)
988 config.drive_mode = DriveMode::VDR;
989 config.hold_setpoint = percent;
991}
992
993template <typename SpiType>
995 float &percent) const {
996 if (!IsValidChannel(channel)) {
997 updateStatistics(false);
999 }
1000
1003 if (result != DriverStatus::OK) {
1004 return result;
1005 }
1006
1007 // Return user unit directly (VDR mode stores duty %)
1008 percent = config.hit_setpoint;
1009 return DriverStatus::OK;
1010}
1011
1012template <typename SpiType>
1014 float &percent) const {
1015 if (!IsValidChannel(channel)) {
1016 updateStatistics(false);
1018 }
1019
1022 if (result != DriverStatus::OK) {
1023 return result;
1024 }
1025
1026 // Return user unit directly (VDR mode stores duty %)
1027 percent = config.hold_setpoint;
1028 return DriverStatus::OK;
1029}
1030
1031template <typename SpiType>
1033 if (!IsValidChannel(channel)) {
1035 }
1038 if (result != DriverStatus::OK) {
1039 return result;
1040 }
1041 return GetDutyLimits(cached_status_.master_clock_80khz, config.chop_freq,
1042 config.slew_rate_control_enabled, limits);
1043}
1044
1045template <typename SpiType>
1046DriverStatus MAX22200<SpiType>::GetDutyLimits(bool master_clock_80khz, ChopFreq chop_freq,
1047 bool slew_rate_control_enabled, DutyLimits &limits) {
1048 // Compute fCHOP from FREQM and FREQ_CFG (datasheet Table 1)
1050 switch (chop_freq) {
1052 fchop_khz = master_clock_80khz ? 20 : 25;
1053 break;
1055 fchop_khz = master_clock_80khz ? 26 : 33; // 26.66 -> 26, 33.33 -> 33
1056 break;
1058 fchop_khz = master_clock_80khz ? 40 : 50;
1059 break;
1060 case ChopFreq::FMAIN:
1061 fchop_khz = master_clock_80khz ? 80 : 100;
1062 break;
1063 default:
1064 limits = DutyLimits();
1066 }
1067
1068 // Datasheet Table 2: Min and Max Duty Cycle
1069 if (slew_rate_control_enabled) {
1070 // SRC = 1 (Slew-Rate Controlled)
1071 if (fchop_khz == 100 || fchop_khz == 80) {
1072 // FREQ_CFG = 11 with SRC=1: not valid per datasheet (SRC only for fCHOP < 50kHz)
1073 limits = DutyLimits();
1075 }
1076 // SRC=1: δMIN = 7%, δMAX = 93% for valid frequencies
1077 limits.min_percent = 7;
1078 limits.max_percent = 93;
1079 } else {
1080 // SRC = 0 (Fast Mode)
1081 if (fchop_khz == 100 || fchop_khz == 80) {
1082 // FREQ_CFG = 11: δMIN = 8%, δMAX = 92%
1083 limits.min_percent = 8;
1084 limits.max_percent = 92;
1085 } else {
1086 // Other FREQ_CFG: δMIN = 4%, δMAX = 96%
1087 limits.min_percent = 4;
1088 limits.max_percent = 96;
1089 }
1090 }
1091
1092 return DriverStatus::OK;
1093}
1094
1095// ============================================================================
1096// Convenience APIs: HIT Time in Milliseconds
1097// ============================================================================
1098
1099template <typename SpiType>
1101 if (!IsValidChannel(channel)) {
1102 updateStatistics(false);
1104 }
1105
1108 if (result != DriverStatus::OK) {
1109 return result;
1110 }
1111
1112 // Reject NaN/Inf
1113 if (!std::isfinite(ms)) {
1114 updateStatistics(false);
1116 }
1117 // Reject positive finite ms beyond 8-bit representable range (raw 1–254) for this channel's chop freq
1118 if (ms > 0.0f && ms > getMaxHitTimeMs(cached_status_.master_clock_80khz, config.chop_freq)) {
1119 updateStatistics(false);
1121 }
1122
1123 config.hit_time_ms = ms;
1125}
1126
1127template <typename SpiType>
1129 if (!IsValidChannel(channel)) {
1130 updateStatistics(false);
1132 }
1133
1136 if (result != DriverStatus::OK) {
1137 return result;
1138 }
1139
1140 // Return user unit directly
1141 ms = config.hit_time_ms;
1142 return DriverStatus::OK;
1143}
1144
1145// ============================================================================
1146// Convenience APIs: One-Shot Channel Configuration
1147// ============================================================================
1148
1149template <typename SpiType>
1151 uint8_t channel, uint32_t hit_ma, uint32_t hold_ma, float hit_time_ms,
1152 SideMode side_mode, ChopFreq chop_freq, bool slew_rate_control_enabled,
1153 bool open_load_detection_enabled, bool plunger_movement_detection_enabled,
1154 bool hit_current_check_enabled) {
1155 if (!IsValidChannel(channel) || board_config_.full_scale_current_ma == 0) {
1156 updateStatistics(false);
1158 }
1159
1161 config.drive_mode = DriveMode::CDR;
1162 config.side_mode = side_mode;
1163 config.chop_freq = chop_freq;
1164 config.slew_rate_control_enabled = slew_rate_control_enabled;
1165 config.open_load_detection_enabled = open_load_detection_enabled;
1166 config.plunger_movement_detection_enabled = plunger_movement_detection_enabled;
1167 config.hit_current_check_enabled = hit_current_check_enabled;
1168
1169 // Clamp to max_current_ma if set
1170 if (board_config_.max_current_ma > 0 && hit_ma > board_config_.max_current_ma) {
1171 hit_ma = board_config_.max_current_ma;
1172 }
1173 if (board_config_.max_current_ma > 0 && hold_ma > board_config_.max_current_ma) {
1174 hold_ma = board_config_.max_current_ma;
1175 }
1176
1177 // Set user units directly (CDR mode: mA)
1178 config.hit_setpoint = static_cast<float>(hit_ma);
1179 config.hold_setpoint = static_cast<float>(hold_ma);
1180 config.hit_time_ms = hit_time_ms;
1181
1183}
1184
1185template <typename SpiType>
1187 uint8_t channel, float hit_duty_percent, float hold_duty_percent,
1188 float hit_time_ms, SideMode side_mode, ChopFreq chop_freq,
1189 bool slew_rate_control_enabled, bool open_load_detection_enabled,
1190 bool plunger_movement_detection_enabled, bool hit_current_check_enabled) {
1191 if (!IsValidChannel(channel)) {
1192 updateStatistics(false);
1194 }
1195
1197 config.drive_mode = DriveMode::VDR;
1198 config.side_mode = side_mode;
1199 config.chop_freq = chop_freq;
1200 config.slew_rate_control_enabled = slew_rate_control_enabled;
1201 config.open_load_detection_enabled = open_load_detection_enabled;
1202 config.plunger_movement_detection_enabled = plunger_movement_detection_enabled;
1203 config.hit_current_check_enabled = hit_current_check_enabled;
1204
1205 // Clamp to max_duty_percent if set
1206 if (board_config_.max_duty_percent > 0 &&
1207 hit_duty_percent > board_config_.max_duty_percent) {
1208 hit_duty_percent = board_config_.max_duty_percent;
1209 }
1210 if (board_config_.max_duty_percent > 0 &&
1211 hold_duty_percent > board_config_.max_duty_percent) {
1212 hold_duty_percent = board_config_.max_duty_percent;
1213 }
1214
1215 // Clamp to duty limits
1217 DriverStatus result = GetDutyLimits(cached_status_.master_clock_80khz, chop_freq,
1218 slew_rate_control_enabled, limits);
1219 if (result != DriverStatus::OK) {
1220 return result;
1221 }
1222 if (hit_duty_percent < limits.min_percent) hit_duty_percent = limits.min_percent;
1223 if (hit_duty_percent > limits.max_percent) hit_duty_percent = limits.max_percent;
1224 if (hold_duty_percent < limits.min_percent) hold_duty_percent = limits.min_percent;
1225 if (hold_duty_percent > limits.max_percent) hold_duty_percent = limits.max_percent;
1226
1227 // Set user units directly (VDR mode: duty %)
1228 config.hit_setpoint = hit_duty_percent;
1229 config.hold_setpoint = hold_duty_percent;
1230 config.hit_time_ms = hit_time_ms;
1231
1233}
1234
1235// ============================================================================
1236// Private: Statistics
1237// ============================================================================
1238
1239template <typename SpiType>
1241 statistics_.total_transfers++;
1242 if (!success) {
1243 statistics_.failed_transfers++;
1244 }
1245}
1246
1247} // namespace max22200
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:526
DriverStatus SetChannelEnabled(uint8_t channel, bool enable)
Set a channel on or off (convenience when toggling from a variable)
Definition max22200.ipp:273
DriverStatus DisableDevice()
Disable device (ENABLE pin low); low-power state.
Definition max22200.ipp:485
DriverStatus SetDeviceEnable(bool enable)
Set ENABLE pin state (true = on, false = off)
Definition max22200.ipp:490
~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:532
DriverStatistics GetStatistics() const
Definition max22200.ipp:546
void SetBoardConfig(const BoardConfig &config)
Set board configuration (IFS, max current, max duty)
Definition max22200.ipp:745
bool IsInitialized() const
Check if driver is initialized.
Definition max22200.ipp:154
DriverStatus GetFaultPinState(bool &fault_active) const
Read nFAULT pin state (true = fault active, false = no fault)
Definition max22200.ipp:501
DriverStatus EnableDevice()
Enable device (ENABLE pin high); SPI and channels can be used.
Definition max22200.ipp:480
DriverStatus SetFullBridgeState(uint8_t pair_index, FullBridgeState state)
Set full-bridge state for a channel pair (datasheet Table 7)
Definition max22200.ipp:311
DriverStatus SetHoldDutyPercent(uint8_t channel, float percent)
Set HOLD duty cycle in percent (VDR mode)
Definition max22200.ipp:962
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:1046
DriverStatus ReadRegister32(uint8_t bank, uint32_t &value) const
Read a 32-bit register by bank address.
Definition max22200.ipp:515
DriverStatus ClearChannelFaults(uint8_t channel_mask, FaultStatus *out_faults=nullptr) const
Clear fault flags for selected channels only (MAX22200A)
Definition max22200.ipp:363
void SetFaultCallback(FaultCallback callback, void *user_data)
Definition max22200.ipp:560
DriverStatus ReadFaultFlags(StatusConfig &status) const
Read fault flags from STATUS register.
Definition max22200.ipp:394
DriverStatus Deinitialize()
Deinitialize — disable channels, ACTIVE=0, ENABLE low.
Definition max22200.ipp:132
DriverStatus ClearAllFaults()
Clear all fault flags (read FAULT register and discard)
Definition max22200.ipp:357
DriverStatus SetHoldCurrentA(uint8_t channel, float amps)
Set HOLD current in Amps (CDR mode)
Definition max22200.ipp:807
DriverStatus GetHitCurrentMa(uint8_t channel, uint32_t &ma) const
Get HIT current in milliamps (CDR mode)
Definition max22200.ipp:842
DriverStatus GetChannelConfig(uint8_t channel, ChannelConfig &config) const
Read a channel's configuration.
Definition max22200.ipp:215
DriverStatus WriteDpmConfig(const DpmConfig &config)
Write DPM algorithm configuration (CFG_DPM register)
Definition max22200.ipp:421
DriverStatus ConfigureAllChannels(const ChannelConfigArray &configs)
Configure all channels.
Definition max22200.ipp:233
DriverStatus GetAllChannelConfigs(ChannelConfigArray &configs) const
Get all channel configurations.
Definition max22200.ipp:246
static bool IsValidChannel(uint8_t channel)
Definition max22200.hpp:829
DriverStatus DisableAllChannels()
Turn off all channels.
Definition max22200.ipp:292
DriverStatus WriteStatus(const StatusConfig &status)
Write the STATUS register (writable bits only)
Definition max22200.ipp:174
DriverStatus SetHoldCurrentPercent(uint8_t channel, float percent)
Set HOLD current as percentage of IFS (CDR mode)
Definition max22200.ipp:828
DriverStatus GetHoldDutyPercent(uint8_t channel, float &percent) const
Get HOLD duty cycle in percent (VDR mode)
Definition max22200.ipp:1013
DriverStatus ConfigureDpm(float start_current_ma, float dip_threshold_ma, float debounce_ms)
Configure DPM in real units (easy API)
Definition max22200.ipp:448
DriverStatus SetHitCurrentA(uint8_t channel, float amps)
Set HIT current in Amps (CDR mode)
Definition max22200.ipp:802
DriverStatus ReadDpmConfig(DpmConfig &config) const
Read DPM algorithm configuration (CFG_DPM register)
Definition max22200.ipp:410
DriverStatus DisableChannel(uint8_t channel)
Turn off a channel (set ONCHx = 0)
Definition max22200.ipp:268
void ResetStatistics()
Definition max22200.ipp:551
DriverStatus SetHitCurrentMa(uint8_t channel, uint32_t ma)
Set HIT current in milliamps (CDR mode)
Definition max22200.ipp:759
DriverStatus SetChannelsOn(uint8_t channel_mask)
Definition max22200.ipp:303
BoardConfig GetBoardConfig() const
Get current board configuration.
Definition max22200.ipp:750
DriverStatus WriteRegister32(uint8_t bank, uint32_t value)
Write a 32-bit register by bank address.
Definition max22200.ipp:521
DriverStatus SetHitDutyPercent(uint8_t channel, float percent)
Set HIT duty cycle in percent (VDR mode)
Definition max22200.ipp:926
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:375
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:1150
DriverStatus SetDpmEnabledChannels(uint8_t channel_mask)
Definition max22200.ipp:428
DriverStatus GetHoldCurrentMa(uint8_t channel, uint32_t &ma) const
Get HOLD current in milliamps (CDR mode)
Definition max22200.ipp:860
DriverStatus GetHitDutyPercent(uint8_t channel, float &percent) const
Get HIT duty cycle in percent (VDR mode)
Definition max22200.ipp:994
DriverStatus EnableChannel(uint8_t channel)
Turn on a channel (set ONCHx = 1)
Definition max22200.ipp:263
DriverStatus GetHoldCurrentPercent(uint8_t channel, float &percent) const
Get HOLD current as percentage of IFS (CDR mode)
Definition max22200.ipp:900
DriverStatus SetHoldCurrentMa(uint8_t channel, uint32_t ma)
Set HOLD current in milliamps (CDR mode)
Definition max22200.ipp:781
DriverStatus ConfigureChannel(uint8_t channel, const ChannelConfig &config)
Configure a channel (write full 32-bit CFG_CHx register)
Definition max22200.ipp:188
DriverStatus GetHitTimeMs(uint8_t channel, float &ms) const
Get HIT time in milliseconds.
Definition max22200.ipp:1128
void SetStateChangeCallback(StateChangeCallback callback, void *user_data)
Definition max22200.ipp:567
DriverStatus GetHitCurrentPercent(uint8_t channel, float &percent) const
Get HIT current as percentage of IFS (CDR mode)
Definition max22200.ipp:878
DriverStatus SetHitCurrentPercent(uint8_t channel, float percent)
Set HIT current as percentage of IFS (CDR mode)
Definition max22200.ipp:812
DriverStatus SetHitTimeMs(uint8_t channel, float ms)
Set HIT time in milliseconds.
Definition max22200.ipp:1100
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:346
DriverStatus ClearFaultFlags()
Clear fault flags by reading STATUS register.
Definition max22200.ipp:399
DriverStatus SetAllChannelsEnabled(bool enable)
Set all channels on or off at once (convenience when toggling from a variable)
Definition max22200.ipp:297
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:1186
uint8_t GetLastFaultByte() const
Definition max22200.ipp:537
DriverStatus EnableAllChannels()
Turn on all channels.
Definition max22200.ipp:287
DriverStatus ReadStatus(StatusConfig &status) const
Read the full 32-bit STATUS register.
Definition max22200.ipp:163
constexpr uint8_t build(uint8_t bank, bool write, bool mode8=false)
Definition max22200_registers.hpp:172
constexpr uint8_t STATUS
Status register (32-bit) — channel on/off, HW config, faults, ACTIVE.
Definition max22200_registers.hpp:95
constexpr uint8_t FAULT
Fault register (32-bit, read-only) — per-channel fault flags.
Definition max22200_registers.hpp:104
constexpr uint8_t CFG_DPM
DPM configuration register (32-bit) — global DPM algorithm settings.
Definition max22200_registers.hpp:105
constexpr uint8_t FAULT_BYTE_COMER
Fault byte value returned on SDO when COMER is set (per datasheet Figure 6)
Definition max22200_registers.hpp:251
Definition max22200.ipp:15
@ VDR
Voltage Drive Regulation (VDRnCDR = 1) — low-side or high-side.
@ CDR
Current Drive Regulation (VDRnCDR = 0) — low-side only.
std::array< ChannelConfig, NUM_CHANNELS_ > ChannelConfigArray
Array type for channel configurations.
Definition max22200_types.hpp:970
SideMode
High-side / Low-side selection (HSnLS bit in CFG_CHx[6])
Definition max22200_types.hpp:123
DriverStatus
Driver status enumeration.
Definition max22200_types.hpp:923
@ INITIALIZATION_ERROR
Initialization failed.
@ COMMUNICATION_ERROR
SPI communication error.
@ OK
Operation successful.
@ INVALID_PARAMETER
Invalid parameter provided.
FullBridgeState
Full-bridge state for a channel pair (datasheet Table 7)
Definition max22200_types.hpp:913
@ 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 to GND)
@ HiZ
ONCHx=0, ONCHy=0 — high impedance.
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 MAX_SPI_FREQ_STANDALONE_
Maximum SPI clock frequency without daisy chaining (Hz)
Definition max22200_registers.hpp:70
constexpr uint8_t getChannelCfgBank(uint8_t channel)
Definition max22200_registers.hpp:535
GpioSignal
Abstract signal level for control pins.
Definition max22200_spi_interface.hpp:85
@ ACTIVE
Pin function is asserted.
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.
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:981
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:271
void(*)(uint8_t channel, FaultType fault_type, void *user_data) FaultCallback
Callback function type for fault events.
Definition max22200_types.hpp:975
@ FAULT
Fault status output (active-low, open-drain)
@ CMD
Command mode select (HIGH = SPI register, LOW = direct drive)
@ ENABLE
Output enable (active-high on the physical pin)
Board/scale configuration for unit-based APIs.
Definition max22200_types.hpp:1010
uint32_t full_scale_current_ma
Full-scale current in mA (from RREF: IFS = KFS×1000/RREF)
Definition max22200_types.hpp:1011
uint32_t max_current_ma
Max current limit in mA (0 = no limit, applies to all channels)
Definition max22200_types.hpp:1012
uint8_t max_duty_percent
Max duty limit in percent (0 = no limit, applies to VDR mode)
Definition max22200_types.hpp:1013
Channel configuration structure.
Definition max22200_types.hpp:360
DPM (Detection of Plunger Movement) algorithm configuration.
Definition max22200_types.hpp:882
Driver statistics structure.
Definition max22200_types.hpp:1082
uint32_t total_transfers
Definition max22200_types.hpp:1083
uint32_t failed_transfers
Definition max22200_types.hpp:1084
Duty cycle limits (δMIN, δMAX) for a given configuration.
Definition max22200_types.hpp:1057
Per-channel fault information from FAULT register (0x09)
Definition max22200_types.hpp:805
STATUS register structure.
Definition max22200_types.hpp:634
bool master_clock_80khz
Master clock base (false=100kHz, true=80kHz); affects fCHOP.
Definition max22200_types.hpp:648
uint8_t channels_on_mask
Definition max22200_types.hpp:636
bool active
Global enable (0=low-power, 1=normal); set to 1 during init.
Definition max22200_types.hpp:657