TLE92466ED Driver 2.0.0
API reference
Loading...
Searching...
No Matches
tle92466ed_spi_interface.hpp
Go to the documentation of this file.
1
30#pragma once
31#include <cstdarg>
32#include <cstdint>
33#include <span>
34
36
37namespace tle92466ed {
38
45enum class CommError : uint8_t {
46 None = 0,
47 BusError,
48 Timeout,
53 CRCError,
62 NoReply,
64};
65
73enum class CtrlPin : uint8_t {
74 RESN,
75 EN,
76 FAULTN
77};
78
88enum class GpioSignal : uint8_t {
89 INACTIVE = 0,
90 ACTIVE = 1
91};
92
99enum class LogLevel : uint8_t {
100 Error = 0,
101 Warn,
102 Info,
103 Debug,
104 Verbose
105};
106
115template <typename T>
117
131struct SPIConfig {
132 uint32_t frequency{1'000'000};
133 uint8_t mode{1};
134 bool msb_first{true};
135 uint32_t timeout_ms{100};
136};
137
138//==============================================================================
139// SPI FRAME STRUCTURES (32-BIT)
140//==============================================================================
141
200union SPIFrame {
201 uint32_t word;
202
204 struct {
205 uint32_t data : 16;
206 uint32_t rw : 1;
207 uint32_t address : 7;
208 uint32_t crc : 8;
210
212 struct {
213 uint32_t _data_low : 16;
214 uint32_t _field_16 : 1;
215 uint32_t _field_17_21 : 5;
216 uint32_t reply_mode : 2;
217 uint32_t crc : 8;
219
221 struct {
222 uint32_t data : 16;
223 uint32_t rw_echo : 1;
224 uint32_t status : 5;
225 uint32_t reply_mode : 2;
226 uint32_t crc : 8;
228
230 struct {
231 uint32_t data : 22;
232 uint32_t reply_mode : 2;
233 uint32_t crc : 8;
235
237 struct {
238 uint32_t fault_flags : 8;
239 uint32_t _reserved : 8;
240 uint32_t _reserved2 : 1;
241 uint32_t _reserved3 : 5;
242 uint32_t reply_mode : 2;
243 uint32_t _dont_care : 8;
245
266 [[nodiscard]] static constexpr SPIFrame MakeRead(uint16_t addr) noexcept {
267 SPIFrame frame{};
268 frame.tx_fields.rw = 0; // Read (bit 16)
269 frame.tx_fields.address = 0; // Bits 23:17 are don't care for reads
270 frame.tx_fields.data = (addr & 0xFFFF); // 16-bit address in bits 15:0 (full 16-bit address)
271 frame.tx_fields.crc = 0; // CRC calculated separately
272 return frame;
273 }
274
296 [[nodiscard]] static constexpr SPIFrame MakeWrite(uint16_t addr, uint16_t data) noexcept {
297 SPIFrame frame{};
298 frame.tx_fields.rw = 1; // Write (bit 16)
299 frame.tx_fields.address = (addr) & 0x7F; // Lower 7 bits [23:17]
300 frame.tx_fields.data = data; // Data in bits [15:0]
301 frame.tx_fields.crc = 0; // CRC calculated separately
302 return frame;
303 }
304};
305
306static_assert(sizeof(SPIFrame) == 4, "SPIFrame must be exactly 4 bytes");
307
327 bool clk_too_fast : 1;
328 bool clk_too_slow : 1;
329 bool adc_bandgap_ok : 1;
330 bool supply_2v5_ok : 1;
331 bool supply_1v5_ok : 1;
332
338 [[nodiscard]] static constexpr CriticalFaultFlags Extract(const SPIFrame& frame) noexcept {
339 CriticalFaultFlags flags{};
340 uint8_t fault_byte = frame.rx_fault.fault_flags;
341 flags.wd_ref_clk_missing = (fault_byte & 0x01) != 0;
342 flags.dig_clk_too_fast = (fault_byte & 0x02) != 0;
343 flags.dig_clk_too_slow = (fault_byte & 0x04) != 0;
344 flags.clk_too_fast = (fault_byte & 0x08) != 0;
345 flags.clk_too_slow = (fault_byte & 0x10) != 0;
346 flags.adc_bandgap_ok = (fault_byte & 0x20) != 0;
347 flags.supply_2v5_ok = (fault_byte & 0x40) != 0;
348 flags.supply_1v5_ok = (fault_byte & 0x80) != 0;
349 return flags;
350 }
351};
352
356enum class ReplyMode : uint8_t {
357 REPLY_16BIT = 0b00,
358 REPLY_22BIT = 0b01,
359 CRITICAL_FAULT = 0b10,
360 RESERVED = 0b11
361};
362
366enum class SPIStatus : uint8_t {
367 NO_ERROR = 0b00000,
368 SPI_FRAME_ERROR = 0b00001,
369 CRC_ERROR = 0b00010,
370 WRITE_RO_REG = 0b00011,
371 INTERNAL_BUS_FAULT = 0b00100
372};
373
428template <typename Derived>
430public:
431
445 [[nodiscard]] CommResult<void> Init() noexcept {
446 return static_cast<Derived*>(this)->Init();
447 }
448
458 [[nodiscard]] CommResult<void> Deinit() noexcept {
459 return static_cast<Derived*>(this)->Deinit();
460 }
461
493 [[nodiscard]] CommResult<uint32_t> Transfer32(uint32_t tx_data) noexcept {
494 return static_cast<Derived*>(this)->Transfer32(tx_data);
495 }
496
521 [[nodiscard]] CommResult<void> TransferMulti(std::span<const uint32_t> tx_data,
522 std::span<uint32_t> rx_data) noexcept {
523 return static_cast<Derived*>(this)->TransferMulti(tx_data, rx_data);
524 }
525
540 [[nodiscard]] CommResult<void> Delay(uint32_t microseconds) noexcept {
541 return static_cast<Derived*>(this)->Delay(microseconds);
542 }
543
561 [[nodiscard]] CommResult<void> Configure(const SPIConfig& config) noexcept {
562 return static_cast<Derived*>(this)->Configure(config);
563 }
564
574 [[nodiscard]] bool IsReady() const noexcept {
575 return static_cast<const Derived*>(this)->IsReady();
576 }
577
587 [[nodiscard]] CommError GetLastError() const noexcept {
588 return static_cast<const Derived*>(this)->GetLastError();
589 }
590
600 [[nodiscard]] CommResult<void> ClearErrors() noexcept {
601 return static_cast<Derived*>(this)->ClearErrors();
602 }
603
625 [[nodiscard]] CommResult<void> GpioSet(CtrlPin pin, GpioSignal signal) noexcept {
626 return static_cast<Derived*>(this)->GpioSet(pin, signal);
627 }
628
647 [[nodiscard]] CommResult<GpioSignal> GpioRead(CtrlPin pin) noexcept {
648 return static_cast<Derived*>(this)->GpioRead(pin);
649 }
650
656 [[nodiscard]] CommResult<void> GpioSetActive(CtrlPin pin) noexcept {
657 return GpioSet(pin, GpioSignal::ACTIVE);
658 }
659
665 [[nodiscard]] CommResult<void> GpioSetInactive(CtrlPin pin) noexcept {
666 return GpioSet(pin, GpioSignal::INACTIVE);
667 }
668
684 void Log(LogLevel level, const char* tag, const char* format, ...) noexcept {
685 va_list args{};
686 va_start(args, format);
687 static_cast<Derived*>(this)->Log(level, tag, format, args);
688 va_end(args);
689 }
690
729 [[nodiscard]] CommResult<uint32_t> Read(uint16_t address, bool verify_crc = true) noexcept;
730
732 static constexpr size_t kMaxPipelinedReads = 12;
733
767 [[nodiscard]] CommResult<void> ReadMulti(std::span<const uint16_t> addresses,
768 std::span<uint32_t> values,
769 uint16_t& valid_mask) noexcept;
770
793 [[nodiscard]] CommResult<void> Write(uint16_t address, uint16_t value,
794 bool verify_crc = true) noexcept;
795
810 [[nodiscard]] uint8_t LastCriticalFaultFlags() const noexcept {
812 }
813
815 [[nodiscard]] bool SawCriticalFault() const noexcept {
816 return saw_critical_fault_;
817 }
818
822 SpiInterface(const SpiInterface&) = delete;
824
825protected:
832 SpiInterface() = default;
833
837 SpiInterface(SpiInterface&&) noexcept = default;
838 SpiInterface& operator=(SpiInterface&&) noexcept = default;
839
841 void NoteCriticalFault(const SPIFrame& frame) noexcept {
842 last_critical_fault_flags_ = static_cast<uint8_t>(frame.rx_fault.fault_flags);
843 saw_critical_fault_ = true;
844 }
845
848
853 ~SpiInterface() = default;
854};
855
856#if __cpp_concepts >= 201907L
857#include <concepts>
870template <typename T>
871concept SpiInterfaceLike =
872 requires(T comm, uint32_t data, SPIConfig cfg, CtrlPin pin, GpioSignal signal) {
873 { comm.Init() } -> std::same_as<CommResult<void>>;
874 { comm.Transfer32(data) } -> std::same_as<CommResult<uint32_t>>;
875 { comm.IsReady() } -> std::same_as<bool>;
876 { comm.Configure(cfg) } -> std::same_as<CommResult<void>>;
877 { comm.GpioSet(pin, signal) } -> std::same_as<CommResult<void>>;
878 { comm.GpioRead(pin) } -> std::same_as<CommResult<GpioSignal>>;
879 };
880#endif // __cpp_concepts
881
882} // namespace tle92466ed
883
884// Include registers header for CRC functions (after SPIFrame is defined)
886
887namespace tle92466ed {
888
889//==============================================================================
890// INLINE IMPLEMENTATIONS
891//==============================================================================
892
893template <typename Derived>
894inline CommResult<uint32_t> SpiInterface<Derived>::Read(uint16_t address, bool verify_crc) noexcept {
895 // Create read frame
896 SPIFrame tx_frame = SPIFrame::MakeRead(address);
897
898 // Calculate and set CRC
899 tx_frame.tx_fields.crc = CalculateFrameCrc(tx_frame);
900
901 /* Pipelined SPI: the reply to a command only appears on the NEXT CS window,
902 * so the device always has one reply outstanding. That makes a bare
903 * command+dummy pair depend on what the previous transaction left behind —
904 * one dropped or extra frame anywhere shifts every later read by one slot,
905 * and the caller silently gets the *previous* register's value (bench
906 * 2026-08-12: PIN_STAT returning CH_CTRL's 0x8000, FB_VOLTAGE2 returning 0).
907 *
908 * Sending a leading dummy makes the transaction self-synchronising: it
909 * absorbs whatever reply was outstanding, so the reply to `tx_frame` is
910 * always in the last slot regardless of prior bus state.
911 *
912 * The dummies address 0, NOT `address`. Re-sending the same read as the
913 * dummy queues a second reply and the part answers with a reserved
914 * reply-mode instead of the register (bench 2026-08-12: ICVID never
915 * validated, DriverError::SPIFrameError every attempt; MakeRead(0) restored
916 * identity on the same hardware). TransferMulti keeps one bus-ownership
917 * window across all three frames on shared multi-slave buses. */
918 SPIFrame dummy_frame = SPIFrame::MakeRead(0);
919 dummy_frame.tx_fields.crc = CalculateFrameCrc(dummy_frame);
920
921 const uint32_t tx_words[3] = {dummy_frame.word, tx_frame.word,
922 dummy_frame.word};
923
924 /* A reply is only believed when it is well formed: CRC valid *and* the reply
925 * width the register is defined to answer with. Bench measurement
926 * (2026-08-13, 150 frames) found 4 % of frames corrupt and 25 % of
927 * FB_STAT reads carrying the preceding ICVID reply — the latter with a valid
928 * CRC, so CRC alone does not reject it. Both used to be accepted as data,
929 * which is the origin of the "sticky-zero CH_CTRL", the impossible IMAX, the
930 * open-load reported on a fitted resistor, and INIT_DONE reading 0 on a
931 * healthy device. Two CRC attempts keep freeze-complete (2× FB_UPD +
932 * FB_I_AVG + FB_DC) inside a 2 ms host control-loop budget; four attempts ×
933 * Mode1 IdleAll+30 µs per frame ran last_step ~9–11 ms on hold ticks.
934 * ICVID Init has its own recovery path. */
935 constexpr uint8_t kReadAttempts = 2;
936 const ExpectedReply expected = ExpectedReplyFor(address);
937 uint32_t rx_all[3] = {};
938 uint32_t last_reply = 0;
939 uint32_t last_flush = 0;
940 bool saw_any_frame = false;
941
942 for (uint8_t attempt = 0; attempt < kReadAttempts; ++attempt) {
943 auto multi = static_cast<Derived*>(this)->TransferMulti(
944 std::span<const uint32_t>{tx_words}, std::span<uint32_t>{rx_all});
945 if (!multi) {
946 return tle::unexpected(multi.error());
947 }
948 /* Slot 2 answers the command; slot 1 answers the flush dummy and is only
949 * kept as a fallback for the ICVID scan below. */
950 last_reply = rx_all[2];
951 last_flush = rx_all[1];
952
953 SPIFrame candidate{};
954 candidate.word = last_reply;
955
956 /* Undriven MISO — nothing to validate, and retrying is pointless if the
957 * part is absent, but cheap if a single window was missed. */
958 if (last_reply == 0U || last_reply == 0xFFFFFFFFU) {
959 continue;
960 }
961 saw_any_frame = true;
962
963 /* Critical fault frame (UV / clock / supplies) first: its bits 31:24 are
964 * "don't care", not a CRC (datasheet 5.2.3.5), so a CRC check would reject
965 * it and report CRCError instead of the fault. The device is answering
966 * truthfully that it cannot serve the read, so this is not retried. */
967 if (candidate.rx_common.reply_mode == 0x02) {
968 NoteCriticalFault(candidate);
970 }
971
972 if (!VerifyFrameCrc(candidate)) {
973 continue;
974 }
975
976 const bool width_ok =
977 (expected == ExpectedReply::Bits22) ? (candidate.rx_common.reply_mode == 0x01)
978 : (candidate.rx_common.reply_mode == 0x00);
979 if (!width_ok) {
980 continue; /* Another register's reply landed in this slot. */
981 }
982
983 return (candidate.rx_common.reply_mode == 0x01)
984 ? static_cast<uint32_t>(candidate.rx_22bit.data)
985 : static_cast<uint32_t>(candidate.rx_16bit.data);
986 }
987
988 const uint32_t rx_words[2] = {last_flush, last_reply};
989
990 /* ICVID-only: recover a valid device ID from a misaligned 32-bit MISO word
991 * on long soft-CS / Mode1 buses (data may sit in bits[23:8], or appear one
992 * bit early as 0x82xx/0x80xx/0xC0xx). Restricted to ICVID so lane noise
993 * cannot false-match 0xC1xx inside other registers. */
994 if (address == CentralReg::ICVID) {
995 auto extract_icvid_candidate = [](uint32_t word) noexcept -> uint16_t {
996 if (word == 0U || word == 0xFFFFFFFFU) {
997 return 0;
998 }
999 SPIFrame f{};
1000 f.word = word;
1001 const uint16_t cands[] = {
1002 static_cast<uint16_t>(f.rx_16bit.data),
1003 static_cast<uint16_t>((word >> 8) & 0xFFFFU),
1004 static_cast<uint16_t>((word >> 16) & 0xFFFFU),
1005 static_cast<uint16_t>(word & 0xFFFFU),
1006 };
1007 for (uint16_t c : cands) {
1008 if (DeviceID::IsValidDevice(c)) {
1009 return c;
1010 }
1011 /* One-bit-early Mode1 phantoms of a real 0xC1xx ID. */
1012 if ((c & 0xFF00U) == 0x8200U || (c & 0xFF00U) == 0x8000U ||
1013 (c & 0xFF00U) == 0xC000U) {
1014 const uint16_t unshift =
1015 static_cast<uint16_t>((static_cast<uint32_t>(c) << 1) & 0xFFFFU);
1016 if (DeviceID::IsValidDevice(unshift)) {
1017 return unshift;
1018 }
1019 }
1020 }
1021 return 0;
1022 };
1023 for (uint32_t word : {rx_words[1], rx_words[0]}) {
1024 const uint16_t id = extract_icvid_candidate(word);
1025 if (id != 0U) {
1026 return static_cast<uint32_t>(id);
1027 }
1028 }
1029 }
1030
1031 /* Every attempt produced a frame that could not be trusted. Reporting the
1032 * failure is the whole point: the caller can retry, hold its last known
1033 * value, or degrade — all of which beat decoding a corrupt word as state.
1034 *
1035 * `verify_crc` no longer gates this. It used to let callers opt out of
1036 * validation, and every diagnostic path did (`ReadRegister(addr, false)`),
1037 * which is why corrupt and stale frames reached the decoders in the first
1038 * place. The parameter is retained for API compatibility. */
1039 (void)verify_crc;
1040 return tle::unexpected(saw_any_frame ? CommError::CRCError
1042}
1043
1044template <typename Derived>
1046 std::span<const uint16_t> addresses, std::span<uint32_t> values,
1047 uint16_t& valid_mask) noexcept {
1048 valid_mask = 0U;
1049 const size_t n = addresses.size();
1050 if (n == 0U || n > kMaxPipelinedReads || values.size() != n) {
1052 }
1053
1054 SPIFrame dummy_frame = SPIFrame::MakeRead(0);
1055 dummy_frame.tx_fields.crc = CalculateFrameCrc(dummy_frame);
1056
1057 /* [flush dummy][cmd 0..n-1][trailing dummy]: the reply to command i is
1058 * clocked out during frame i+2. See the ReadMulti doc comment. */
1059 uint32_t tx_words[kMaxPipelinedReads + 2] = {};
1060 uint32_t rx_words[kMaxPipelinedReads + 2] = {};
1061 tx_words[0] = dummy_frame.word;
1062 for (size_t i = 0; i < n; ++i) {
1063 SPIFrame cmd = SPIFrame::MakeRead(addresses[i]);
1064 cmd.tx_fields.crc = CalculateFrameCrc(cmd);
1065 tx_words[i + 1] = cmd.word;
1066 values[i] = 0U;
1067 }
1068 tx_words[n + 1] = dummy_frame.word;
1069
1070 const size_t frames = n + 2U;
1071 auto multi = static_cast<Derived*>(this)->TransferMulti(
1072 std::span<const uint32_t>{tx_words, frames},
1073 std::span<uint32_t>{rx_words, frames});
1074 if (!multi) {
1075 return tle::unexpected(multi.error());
1076 }
1077
1078 for (size_t i = 0; i < n; ++i) {
1079 SPIFrame candidate{};
1080 candidate.word = rx_words[i + 2U];
1081 if (candidate.word == 0U || candidate.word == 0xFFFFFFFFU) {
1082 continue; /* Undriven MISO. */
1083 }
1084 if (candidate.rx_common.reply_mode == 0x02) {
1085 /* Critical fault (UV / clock / supplies), checked before the CRC because
1086 * bits 31:24 of this frame are "don't care" (datasheet 5.2.3.5). The
1087 * device cannot serve any of these reads, so abandon the whole burst. */
1088 NoteCriticalFault(candidate);
1090 }
1091 if (!VerifyFrameCrc(candidate)) {
1092 continue;
1093 }
1094 const ExpectedReply expected = ExpectedReplyFor(addresses[i]);
1095 const bool width_ok = (expected == ExpectedReply::Bits22)
1096 ? (candidate.rx_common.reply_mode == 0x01)
1097 : (candidate.rx_common.reply_mode == 0x00);
1098 if (!width_ok) {
1099 continue; /* Another register's reply landed in this slot. */
1100 }
1101 values[i] = (candidate.rx_common.reply_mode == 0x01)
1102 ? static_cast<uint32_t>(candidate.rx_22bit.data)
1103 : static_cast<uint32_t>(candidate.rx_16bit.data);
1104 valid_mask = static_cast<uint16_t>(valid_mask | (1U << i));
1105 }
1106 return {};
1107}
1108
1109template <typename Derived>
1110inline CommResult<void> SpiInterface<Derived>::Write(uint16_t address, uint16_t value,
1111 bool verify_crc) noexcept {
1112 // Create write frame
1113 SPIFrame tx_frame = SPIFrame::MakeWrite(address, value);
1114
1115 // Calculate and set CRC
1116 tx_frame.tx_fields.crc = CalculateFrameCrc(tx_frame);
1117
1118 /* Leading flush dummy then a trailing dummy to clock the write response —
1119 * same self-synchronising pattern as Read(), so the status bits checked
1120 * below belong to this write and not to whatever ran before it. (See Read()
1121 * for why the dummies must not repeat the accessed address.) */
1122 SPIFrame dummy_frame = SPIFrame::MakeRead(0);
1123 dummy_frame.tx_fields.crc = CalculateFrameCrc(dummy_frame);
1124
1125 const uint32_t tx_words[3] = {dummy_frame.word, tx_frame.word,
1126 dummy_frame.word};
1127 uint32_t rx_words[3] = {};
1128 auto multi = static_cast<Derived*>(this)->TransferMulti(
1129 std::span<const uint32_t>{tx_words}, std::span<uint32_t>{rx_words});
1130 if (!multi) {
1131 return tle::unexpected(multi.error());
1132 }
1133
1134 // Response to the write frame lands in the last slot.
1135 SPIFrame rx_frame{};
1136 rx_frame.word = rx_words[2];
1137
1138 // Critical fault frame first — keep the cause byte for the caller. Its bits
1139 // 31:24 are "don't care", not a CRC (datasheet 5.2.3.5), so checking the CRC
1140 // first turned a supply/clock fault into CommError::CRCError.
1141 if (rx_frame.rx_common.reply_mode == 0x02) {
1142 NoteCriticalFault(rx_frame);
1144 }
1145
1146 // Verify CRC if requested
1147 if (verify_crc && !VerifyFrameCrc(rx_frame)) {
1149 }
1150
1151 // Check for errors in status field (for 16-bit reply frames)
1152 if (rx_frame.rx_common.reply_mode == 0x00) {
1153 // Check status field for errors
1154 if (rx_frame.rx_16bit.status != 0x00) {
1155 // Status indicates an error
1157 }
1158 }
1159
1160 return {};
1161}
1162
1163} // namespace tle92466ed
CRTP-based Communication Interface template class.
Definition tle92466ed_spi_interface.hpp:429
CommResult< void > Configure(const SPIConfig &config) noexcept
Configure SPI parameters.
Definition tle92466ed_spi_interface.hpp:561
bool saw_critical_fault_
Definition tle92466ed_spi_interface.hpp:847
static constexpr size_t kMaxPipelinedReads
Largest burst ReadMulti will issue in one CS-session chain.
Definition tle92466ed_spi_interface.hpp:732
SpiInterface & operator=(const SpiInterface &)=delete
SpiInterface(const SpiInterface &)=delete
Prevent copying.
CommResult< GpioSignal > GpioRead(CtrlPin pin) noexcept
Read GPIO control pin signal state (input state).
Definition tle92466ed_spi_interface.hpp:647
void Log(LogLevel level, const char *tag, const char *format,...) noexcept
Log a message with specified severity level and tag.
Definition tle92466ed_spi_interface.hpp:684
uint8_t last_critical_fault_flags_
Definition tle92466ed_spi_interface.hpp:846
CommResult< uint32_t > Transfer32(uint32_t tx_data) noexcept
Transfer 32-bit data via SPI (full-duplex)
Definition tle92466ed_spi_interface.hpp:493
CommResult< uint32_t > Read(uint16_t address, bool verify_crc=true) noexcept
Read a register from the TLE92466ED (High-Level API)
Definition tle92466ed_spi_interface.hpp:894
void NoteCriticalFault(const SPIFrame &frame) noexcept
Latch a critical-fault reply so callers can report the cause.
Definition tle92466ed_spi_interface.hpp:841
SpiInterface(SpiInterface &&) noexcept=default
Allow moving.
~SpiInterface()=default
Protected destructor.
CommResult< void > GpioSetInactive(CtrlPin pin) noexcept
Set GPIO pin to inactive state (convenience method).
Definition tle92466ed_spi_interface.hpp:665
CommResult< void > Deinit() noexcept
Deinitialize the hardware interface.
Definition tle92466ed_spi_interface.hpp:458
CommResult< void > ReadMulti(std::span< const uint16_t > addresses, std::span< uint32_t > values, uint16_t &valid_mask) noexcept
Read several registers in ONE pipelined chain.
Definition tle92466ed_spi_interface.hpp:1045
CommResult< void > ClearErrors() noexcept
Clear any pending errors.
Definition tle92466ed_spi_interface.hpp:600
CommResult< void > Init() noexcept
Initialize the hardware interface.
Definition tle92466ed_spi_interface.hpp:445
CommResult< void > Write(uint16_t address, uint16_t value, bool verify_crc=true) noexcept
Write a register to the TLE92466ED (High-Level API)
Definition tle92466ed_spi_interface.hpp:1110
uint8_t LastCriticalFaultFlags() const noexcept
Fault byte from the most recent critical-fault reply frame.
Definition tle92466ed_spi_interface.hpp:810
CommResult< void > GpioSet(CtrlPin pin, GpioSignal signal) noexcept
Set GPIO control pin signal state (output control).
Definition tle92466ed_spi_interface.hpp:625
CommResult< void > Delay(uint32_t microseconds) noexcept
Delay for specified duration.
Definition tle92466ed_spi_interface.hpp:540
CommError GetLastError() const noexcept
Get the last error that occurred.
Definition tle92466ed_spi_interface.hpp:587
CommResult< void > GpioSetActive(CtrlPin pin) noexcept
Set GPIO pin to active state (convenience method).
Definition tle92466ed_spi_interface.hpp:656
CommResult< void > TransferMulti(std::span< const uint32_t > tx_data, std::span< uint32_t > rx_data) noexcept
Transfer multiple 32-bit words via SPI in one CS assertion window.
Definition tle92466ed_spi_interface.hpp:521
bool IsReady() const noexcept
Check if hardware is ready for communication.
Definition tle92466ed_spi_interface.hpp:574
bool SawCriticalFault() const noexcept
True once a critical-fault reply frame has been decoded.
Definition tle92466ed_spi_interface.hpp:815
SpiInterface()=default
Protected constructor to prevent direct instantiation.
Expected type — holds either a value of type T or an error of type E.
Definition tle92466ed_expected.hpp:82
Wrapper for an error value (mirrors std::unexpected)
Definition tle92466ed_expected.hpp:56
constexpr uint16_t ICVID
IC Version and ID.
Definition tle92466ed_registers.hpp:59
constexpr bool IsValidDevice(uint16_t icvid) noexcept
Check if ICVID value is valid for TLE92466ED.
Definition tle92466ed_registers.hpp:101
Definition tle92466ed.hpp:15
GpioSignal
GPIO signal states with board-agnostic naming.
Definition tle92466ed_spi_interface.hpp:88
@ ACTIVE
Active signal state (logical active)
@ INACTIVE
Inactive signal state (logical inactive)
SPIStatus
SPI Status codes.
Definition tle92466ed_spi_interface.hpp:366
@ SPI_FRAME_ERROR
SPI frame error.
@ INTERNAL_BUS_FAULT
Internal bus fault.
@ WRITE_RO_REG
Write to read-only register.
@ CRC_ERROR
Parity/CRC error.
@ InvalidParameter
Invalid parameter value.
@ CRCError
CRC mismatch in SPI communication.
CtrlPin
Control pin enumeration for TLE92466ED.
Definition tle92466ed_spi_interface.hpp:73
@ RESN
Reset pin (active low) - Must be HIGH for device operation.
@ FAULTN
Fault pin (active low) - Indicates device fault condition.
@ EN
Enable pin (active high) - Enables/disables output channels.
bool VerifyFrameCrc(const tle92466ed::SPIFrame &frame) noexcept
Verify CRC in received frame.
Definition tle92466ed_registers.hpp:2357
ExpectedReply
Reply-frame width a register is required to answer with.
Definition tle92466ed_registers.hpp:2117
@ Bits22
Reply mode 01B — 22-bit data, no status field.
CommError
Error codes for communication interface operations.
Definition tle92466ed_spi_interface.hpp:45
@ BusError
SPI bus communication error.
@ HardwareNotReady
Hardware not initialized or ready.
@ InvalidParameter
Invalid parameter passed to function.
@ BufferOverflow
Buffer size exceeded.
@ CRCError
CRC mismatch error.
@ NoReply
MISO carried no frame at all (all-zero or all-ones word).
@ TransferError
Data transfer failed.
@ UnknownError
Unknown error occurred.
@ Timeout
Operation timed out.
ReplyMode
SPI Reply Mode enumeration.
Definition tle92466ed_spi_interface.hpp:356
@ REPLY_16BIT
16-bit reply frame
@ REPLY_22BIT
22-bit reply frame (extended data)
@ CRITICAL_FAULT
Critical fault frame.
constexpr ExpectedReply ExpectedReplyFor(uint16_t address) noexcept
Reply width the device must use when answering a read of address.
Definition tle92466ed_registers.hpp:2143
uint8_t CalculateFrameCrc(const tle92466ed::SPIFrame &frame) noexcept
Calculate CRC for SPI frame.
Definition tle92466ed_registers.hpp:2343
LogLevel
Log severity levels for driver logging.
Definition tle92466ed_spi_interface.hpp:99
@ Info
Informational messages.
@ Warn
Warning messages.
@ Error
Error messages (highest severity)
@ Debug
Debug messages (lowest severity)
@ Verbose
Verbose messages (lowest severity, most detailed)
Helper structure for critical fault frame flags.
Definition tle92466ed_spi_interface.hpp:323
bool dig_clk_too_fast
Bit 1: Digital clock too fast (1=YES, 0=NO)
Definition tle92466ed_spi_interface.hpp:325
static constexpr CriticalFaultFlags Extract(const SPIFrame &frame) noexcept
Extract fault flags from critical fault frame.
Definition tle92466ed_spi_interface.hpp:338
bool clk_too_fast
Bit 3: Clock too fast (1=YES, 0=NO)
Definition tle92466ed_spi_interface.hpp:327
bool supply_2v5_ok
Bit 6: 2V5 supply OK (1=OK, 0=NOT OK)
Definition tle92466ed_spi_interface.hpp:330
bool dig_clk_too_slow
Bit 2: Digital clock too slow (1=YES, 0=NO)
Definition tle92466ed_spi_interface.hpp:326
bool supply_1v5_ok
Bit 7: 1V5 supply OK (1=OK, 0=NOT OK)
Definition tle92466ed_spi_interface.hpp:331
bool clk_too_slow
Bit 4: Clock too slow (1=YES, 0=NO)
Definition tle92466ed_spi_interface.hpp:328
bool wd_ref_clk_missing
Bit 0: Clock watchdog reference clock missing (1=MISSING, 0=OK)
Definition tle92466ed_spi_interface.hpp:324
bool adc_bandgap_ok
Bit 5: ADC Bandgap OK (1=OK, 0=NOT OK)
Definition tle92466ed_spi_interface.hpp:329
SPI transaction configuration.
Definition tle92466ed_spi_interface.hpp:131
uint8_t mode
SPI mode (CPOL=0, CPHA=1 for TLE92466ED)
Definition tle92466ed_spi_interface.hpp:133
bool msb_first
MSB first transmission.
Definition tle92466ed_spi_interface.hpp:134
uint32_t frequency
SPI clock frequency in Hz (max 8 MHz, datasheet 5.1.4 Table 18)
Definition tle92466ed_spi_interface.hpp:132
uint32_t timeout_ms
Transaction timeout in milliseconds.
Definition tle92466ed_spi_interface.hpp:135
C++20-compatible expected<T,E> polyfill for TLE92466ED driver.
Register definitions and bit field mappings for TLE92466ED.
32-bit SPI frame structure for TLE92466ED communication
Definition tle92466ed_spi_interface.hpp:200
uint32_t _field_16
Field at bit 16 (interpretation depends on reply mode)
Definition tle92466ed_spi_interface.hpp:214
struct tle92466ed::SPIFrame::@1 rx_common
MISO (Receive) frame structure - common fields.
uint32_t data
Data field [15:0].
Definition tle92466ed_spi_interface.hpp:205
uint32_t fault_flags
Fault flags [7:0].
Definition tle92466ed_spi_interface.hpp:238
uint32_t _reserved2
Reserved/Don't care [16].
Definition tle92466ed_spi_interface.hpp:240
uint32_t crc
CRC-8 SAE J1850 [31:24].
Definition tle92466ed_spi_interface.hpp:208
uint32_t address
Register address [23:17].
Definition tle92466ed_spi_interface.hpp:207
uint32_t reply_mode
Reply mode [23:22] - determines frame type.
Definition tle92466ed_spi_interface.hpp:216
uint32_t _reserved
Reserved/Don't care [15:8].
Definition tle92466ed_spi_interface.hpp:239
uint32_t rw_echo
R/W bit echoed [16].
Definition tle92466ed_spi_interface.hpp:223
struct tle92466ed::SPIFrame::@0 tx_fields
MOSI (Transmit) frame structure.
uint32_t _dont_care
Don't care [31:24] (not CRC in fault frames)
Definition tle92466ed_spi_interface.hpp:243
uint32_t _reserved3
Reserved/Don't care [21:17].
Definition tle92466ed_spi_interface.hpp:241
uint32_t _data_low
Lower data bits [15:0] (interpretation depends on reply mode)
Definition tle92466ed_spi_interface.hpp:213
struct tle92466ed::SPIFrame::@2 rx_16bit
MISO 16-bit Reply Frame (Reply Mode = 00B)
struct tle92466ed::SPIFrame::@4 rx_fault
MISO Critical Fault Frame (Reply Mode = 10B)
uint32_t rw
Read/Write bit [16] (1=Write, 0=Read)
Definition tle92466ed_spi_interface.hpp:206
uint32_t _field_17_21
Fields at bits 21:17 (interpretation depends on reply mode)
Definition tle92466ed_spi_interface.hpp:215
uint32_t status
Status bits [21:17].
Definition tle92466ed_spi_interface.hpp:224
uint32_t word
Complete 32-bit frame.
Definition tle92466ed_spi_interface.hpp:201
static constexpr SPIFrame MakeRead(uint16_t addr) noexcept
Construct read frame (without CRC - must be calculated separately)
Definition tle92466ed_spi_interface.hpp:266
static constexpr SPIFrame MakeWrite(uint16_t addr, uint16_t data) noexcept
Construct write frame (without CRC - must be calculated separately)
Definition tle92466ed_spi_interface.hpp:296
struct tle92466ed::SPIFrame::@3 rx_22bit
MISO 22-bit Reply Frame (Reply Mode = 01B)