| #include "include/power_rail/max34451_status.hpp" |
| |
| namespace max34451 |
| { |
| |
| void max34451BBRParser(const std::vector<uint8_t>& bbrArray, |
| std::ofstream& logFile) |
| { |
| constexpr uint8_t kFaultLogIndex = 1; |
| constexpr uint8_t kStatusCmlIndex = 10; |
| constexpr uint8_t kLogValidIndex = 254; |
| constexpr uint8_t kStatusWordMsbIndex = 13; |
| constexpr uint8_t kStatusWordLsbIndex = 12; |
| |
| logFile << std::hex << std::setw(4) << std::setfill('0'); |
| logFile << "FAULT LOG INDEX: " << static_cast<int>(bbrArray[kFaultLogIndex]) |
| << '\n'; |
| |
| getWordAndLog(logFile, bbrArray, 3, 2, "FAULT_LOG_COUNT: "); |
| getWordAndLog(logFile, bbrArray, 5, 4, "MFR_TIME_COUNT(LSW): "); |
| getWordAndLog(logFile, bbrArray, 7, 6, "MFR_TIME_COUNT(MSW): "); |
| logFile << "STATUS_CML: " << static_cast<int>(bbrArray[kStatusCmlIndex]) |
| << " " << parserStatusCml(bbrArray[kStatusCmlIndex]) << '\n'; |
| logFile << parserStatusWord( |
| getWordAndLog(logFile, bbrArray, kStatusWordMsbIndex, |
| kStatusWordLsbIndex, "STATUS_WORD: ")) |
| << '\n'; |
| |
| constexpr uint8_t kVoutIoutStartIndex = 14; |
| constexpr uint8_t kVoutIoutEndIndex = 29; |
| for (uint8_t i = kVoutIoutStartIndex; i <= kVoutIoutEndIndex; ++i) |
| { |
| const uint8_t page = i - kVoutIoutStartIndex; |
| logFile << "Page " << static_cast<int>(page) |
| << " STATUS_VOUT/STATUS_IOUT: " << static_cast<int>(bbrArray[i]) |
| << '\n'; |
| logFile << "STATUS_VOUT: " << parserStatusVout(bbrArray[i]) << '\n'; |
| logFile << "STATUS_IOUT: " << parserStatusIout(bbrArray[i]) << '\n'; |
| } |
| |
| constexpr uint8_t kMfrSpecificStartIndex = 30; |
| constexpr uint8_t kMfrSpecificEndIndex = 47; |
| for (uint8_t i = kMfrSpecificStartIndex; i <= kMfrSpecificEndIndex; ++i) |
| { |
| // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) |
| const uint8_t page = (i == 46) ? 255 |
| : (i == 47) ? 0 // NOLINT |
| : i - kMfrSpecificStartIndex; |
| logFile << "Page " << static_cast<int>(page) |
| << " STATUS_MFR_SPECIFIC: " << static_cast<int>(bbrArray[i]) |
| << " "; |
| parseStatusMfrSpecific(bbrArray[i], logFile); |
| } |
| |
| constexpr uint8_t kTemperatureStartIndex = 48; |
| constexpr uint8_t kTemperatureEndIndex = 53; |
| for (uint8_t i = kTemperatureStartIndex; i <= kTemperatureEndIndex; ++i) |
| { |
| const uint8_t page = (i == 53) ? 0 : i - 32; |
| logFile << "Page " << static_cast<int>(page) |
| << " STATUS_TEMPERATURE: " << static_cast<int>(bbrArray[i]) |
| << " " << parserStatusTemperature(bbrArray[i]) << '\n'; |
| } |
| |
| getWordAndLog(logFile, bbrArray, 55, 54, "CURRENT_CHANNELS: "); |
| |
| constexpr uint8_t kReadVoutIoutStartIndex = 60; |
| constexpr uint8_t kReadVoutIoutEndIndex = 150; |
| constexpr uint8_t kReadVoutIoutStep = 6; |
| for (uint8_t i = kReadVoutIoutStartIndex; i <= kReadVoutIoutEndIndex; |
| i += kReadVoutIoutStep) |
| { |
| const uint8_t page = (i - kReadVoutIoutStartIndex) / kReadVoutIoutStep; |
| for (uint8_t k = 0; k <= 4; k += 2) |
| { |
| const uint8_t number = i + k; |
| const uint16_t readOut = (bbrArray[number + 1] << 8) | |
| bbrArray[number]; |
| logFile << "Page " << static_cast<int>(page) << " T " |
| << static_cast<int>(k / 2) << " READ_VOUT/READ_IOUT: 0x" |
| << std::setw(4) << std::setfill('0') << readOut << std::dec |
| << '\n'; |
| logFile << "READ_VOUT: " |
| << transferDirectDataFormat(1, readOut, 0, 0) << " mV" |
| << '\n'; |
| logFile << "READ_IOUT: " |
| << transferDirectDataFormat(1, readOut, 2, 0) << " A" |
| << '\n'; |
| } |
| } |
| |
| constexpr uint8_t kMfrPeakStartIndex = 164; |
| constexpr uint8_t kMfrPeakEndIndex = 194; |
| for (uint8_t i = kMfrPeakStartIndex; i <= kMfrPeakEndIndex; i += 2) |
| { |
| const uint8_t page = (i - kMfrPeakStartIndex) / 2; |
| getWordAndLog(logFile, bbrArray, i + 1, i, |
| "Page " + std::to_string(page) + |
| " MFR_VOUT_PEAK/MFR_IOUT_PEAK: "); |
| } |
| |
| constexpr uint8_t kMfrVoutMinStartIndex = 196; |
| constexpr uint8_t kMfrVoutMinEndIndex = 226; |
| for (uint8_t i = kMfrVoutMinStartIndex; i <= kMfrVoutMinEndIndex; i += 2) |
| { |
| const uint8_t page = (i - kMfrVoutMinStartIndex) / 2; |
| logFile << transferDirectDataFormat( |
| 1, |
| getWordAndLog(logFile, bbrArray, i + 1, i, |
| "Page " + std::to_string(page) + |
| " MFR_VOUT_MIN: "), |
| 0, 0) |
| << " mV" << '\n'; |
| } |
| |
| constexpr uint8_t kReadTempStartIndex = 232; |
| constexpr uint8_t kReadTempEndIndex = 240; |
| for (uint8_t i = kReadTempStartIndex; i <= kReadTempEndIndex; i += 2) |
| { |
| const uint8_t page = (i - kReadTempStartIndex) / 2 + 16; |
| logFile << transferDirectDataFormat( |
| 1, |
| getWordAndLog(logFile, bbrArray, i + 1, i, |
| "Page " + std::to_string(page) + |
| " READ_TEMPERATURE_1: "), |
| 2, 0) |
| << " C" << '\n'; |
| } |
| |
| constexpr uint8_t kMfrTempPeakStartIndex = 242; |
| constexpr uint8_t kMfrTempPeakEndIndex = 250; |
| for (uint8_t i = kMfrTempPeakStartIndex; i <= kMfrTempPeakEndIndex; i += 2) |
| { |
| const uint8_t page = (i - kMfrTempPeakStartIndex) / 2 + 16; |
| getWordAndLog(logFile, bbrArray, i + 1, i, |
| "Page " + std::to_string(page) + |
| " MFR_TEMPERATURE_PEAK: "); |
| } |
| |
| logFile << "LOG_VALID: 0x" << std::setw(2) << std::setfill('0') |
| << static_cast<int>(bbrArray[kLogValidIndex]) << std::dec << '\n'; |
| logFile << std::dec; |
| } |
| |
| uint16_t getMfrChannelConfig(const Max34451Logger& logger) |
| { |
| const auto data = util::i2cReadBytes(logger.bus, logger.address, |
| logger.mfrChannelConfigReg, 2); |
| if (data.size() < 2) |
| { |
| lg2::error("Failed to read MFR_CHANNEL_CONFIG"); |
| return 0; |
| } |
| return static_cast<uint16_t>(data[0]) | |
| (static_cast<uint16_t>(data[1]) << 8); |
| } |
| |
| void setMfrChannelConfig(const Max34451Logger& logger, const uint16_t value) |
| { |
| util::i2cWriteByte(logger.bus, logger.address, logger.mfrChannelConfigReg, |
| value); |
| } |
| |
| std::vector<uint8_t> getMfrPsenConfig(const Max34451Logger& logger) |
| { |
| return util::i2cReadBytes(logger.bus, logger.address, |
| logger.mfrPsenConfigReg, 4); |
| } |
| |
| std::vector<uint8_t> getMfrPwmConfig(const Max34451Logger& logger) |
| { |
| return util::i2cReadBytes(logger.bus, logger.address, |
| logger.mfrPwmConfigReg, 4); |
| } |
| |
| uint8_t getMfrNvFaultLog(const Max34451Logger& logger) |
| { |
| return util::i2cReadByte(logger.bus, logger.address, |
| logger.mfrNvFaultLogReg); |
| } |
| |
| uint16_t getStatusMfrNvLogConfig(const Max34451Logger& logger) |
| { |
| const auto data = util::i2cReadBytes(logger.bus, logger.address, |
| logger.mfrNvLogConfigReg, 2); |
| if (data.size() < 2) |
| { |
| lg2::error("Failed to read STATUS_MFR_NV_LOG_CONFIG"); |
| return 0; |
| } |
| return static_cast<uint16_t>(data[0]) | |
| (static_cast<uint16_t>(data[1]) << 8); |
| } |
| |
| uint8_t getStatusCmlMax34451(const Max34451Logger& logger) |
| { |
| return util::i2cReadByte(logger.bus, logger.address, logger.statusCmlReg); |
| } |
| |
| std::string parserStatusCmlMax34451(const uint8_t statusCmlMax34451) |
| { |
| if (statusCmlMax34451 == 0xff) |
| { |
| return "INVALID"; |
| } |
| |
| std::map<uint8_t, std::string> statusMap = {{7, "COMM_FAULT "}, |
| {6, "DATA_FAULT "}, |
| {2, "BACKUP_FAULT "}, |
| {1, "MAIN_FAULT "}, |
| {0, "FAULT_LOG_FULL "}}; |
| |
| std::string result; |
| for (const auto& [bit, status] : statusMap) |
| { |
| if ((statusCmlMax34451 & (1 << bit)) != 0) |
| { |
| result += status; |
| } |
| } |
| return result; |
| } |
| |
| void getStatusMfrSpecific(const Max34451Logger& logger, std::ofstream& logFile) |
| { |
| const uint8_t statusMfrSpecific = util::i2cReadByte( |
| logger.bus, logger.address, logger.statusMfrSpecificReg); |
| logFile << "STATUS_MFR_SPECIFIC = 0x" << std::hex |
| << static_cast<int>(statusMfrSpecific) << " "; |
| parseStatusMfrSpecific(statusMfrSpecific, logFile); |
| } |
| |
| void parseStatusMfrSpecific(const uint8_t statusMfrSpecific, |
| std::ofstream& logFile) |
| { |
| if ((statusMfrSpecific & (1 << 7)) != 0) |
| { |
| logFile << "OFF "; |
| } |
| if ((statusMfrSpecific & (1 << 3)) != 0) |
| { |
| logFile << "MARGIN_FAULT "; |
| } |
| if ((statusMfrSpecific & (1 << 2)) != 0) |
| { |
| logFile << "POWER_GOOD# "; |
| } |
| logFile << '\n'; |
| } |
| |
| void getMax34451Status(const Max34451Logger& logger, std::ofstream& logFile) |
| { |
| logFile << "MAX34451 BUS:" << std::to_string(logger.bus) |
| << " ADDR:" << std::hex << std::to_string(logger.address) << "\n"; |
| |
| auto logRailStatus = [&](const std::string& railName, uint8_t pageIndex) { |
| loadPage(logger.bus, logger.address, pageIndex); |
| logFile << railName << " Status:\n"; |
| logFile << "STATUS_VOUT: " |
| << parserStatusVout(getStatusVout(logger.bus, logger.address)) |
| << "\n"; |
| logFile << "STATUS_IOUT: " |
| << parserStatusIout(getStatusIout(logger.bus, logger.address)) |
| << "\n"; |
| logFile << "STATUS_CML: " |
| << parserStatusCmlMax34451(getStatusCmlMax34451(logger)) |
| << "\n"; |
| logFile << "MFR_CHANNEL_CONFIG: " << std::hex |
| << getMfrChannelConfig(logger) << "\n"; |
| logFile << "READ_VOUT: " |
| << transferDirectDataFormat( |
| 1, getStatusByte(logger.bus, logger.address), 0, 0) |
| << " mV\n"; |
| logFile << "READ_IOUT: " |
| << transferDirectDataFormat( |
| 1, getStatusByte(logger.bus, logger.address), 2, 0) |
| << " A\n"; |
| logFile << "VOUT_OV_FAULT_LIMIT: " |
| << transferDirectDataFormat( |
| 1, getVoutOvFaultLimit(logger.bus, logger.address), 0, 0) |
| << " mV\n"; |
| logFile << "IOUT_OC_FAULT_LIMIT: " |
| << transferDirectDataFormat( |
| 1, getIoutOcFaultLimit(logger.bus, logger.address), 2, 0) |
| << " A\n"; |
| |
| if (pageIndex < 12) |
| { |
| logFile << "MFR_PSEN_CONFIG: "; |
| for (const auto& psenConfig : getMfrPsenConfig(logger)) |
| { |
| logFile << std::hex << static_cast<int>(psenConfig) << " "; |
| } |
| logFile << "\n"; |
| } |
| |
| getStatusMfrSpecific(logger, logFile); |
| logFile << "STATUS_MFR_NV_LOG_CONFIG = " |
| << getStatusMfrNvLogConfig(logger) << "\n"; |
| |
| if (pageIndex < 8) |
| { |
| logFile << "MFR_PWM_CONFIG: "; |
| for (const auto& pwmConfig : getMfrPwmConfig(logger)) |
| { |
| logFile << std::hex << static_cast<int>(pwmConfig) << " "; |
| } |
| logFile << "\n"; |
| } |
| }; |
| |
| for (const auto& seqReg : logger.sequencerRegisters) |
| { |
| for (const auto& [page, railName] : seqReg[kPageToPowerRailMap].items()) |
| { |
| const uint8_t pageIndex = util::parseDec(page); |
| logRailStatus(railName.get<std::string>(), pageIndex); |
| } |
| } |
| logFile << getPage255Config(logger) << '\n'; |
| } |
| |
| uint16_t getFaultStatus(const Max34451Logger& logger) |
| { |
| const auto statusWord = util::i2cReadBytes(logger.bus, logger.address, |
| logger.statusWordReg, 2); |
| return (statusWord[1] << 8) | statusWord[0]; |
| } |
| |
| std::string getStatusMfrSpecificMax34451Page255(const Max34451Logger& logger) |
| { |
| const uint8_t statusMfrSpecific = util::i2cReadByte( |
| logger.bus, logger.address, logger.statusMfrSpecificReg); |
| |
| std::string result; |
| result += "STATUS_MFR_SPECIFIC = 0x" + std::to_string(statusMfrSpecific) + |
| " "; |
| |
| std::map<uint8_t, std::string> statusMap = {{7, "LOCK "}, |
| {6, "FAULT_INPUT "}, |
| {5, "POR "}, |
| {4, "WATCHDOG_INT "}, |
| {3, "CONTROL# "}}; |
| |
| if (statusMfrSpecific != 0) |
| { |
| for (const auto& [bit, status] : statusMap) |
| { |
| if ((statusMfrSpecific & (1 << bit)) != 0) |
| { |
| result += status; |
| } |
| } |
| } |
| else |
| { |
| result += "STATUS_MFR_SPECIFIC INVALID"; |
| } |
| return result; |
| } |
| |
| std::string getPage255Config(const Max34451Logger& logger) |
| { |
| std::string result; |
| |
| result += "get page255 status and config\n"; |
| loadPage(logger.bus, logger.address, 255); |
| result += "ON_OFF_CONFIG = " + |
| std::to_string(getOnOffConfig(logger.bus, logger.address)) + "\n"; |
| result += getStatusMfrSpecificMax34451Page255(logger) + "\n"; |
| |
| return result; |
| } |
| |
| void runMax34451Logger(const std::filesystem::path& configFilePath, |
| const std::filesystem::path& logDirPath, |
| const bool logDetails) |
| { |
| std::filesystem::create_directories(logDirPath); |
| |
| Max34451Logger logger; |
| logger.loadConfig(configFilePath.string()); |
| |
| const std::string logFilePath = logDirPath / (logger.source + ".log"); |
| std::ofstream logFile(logFilePath); |
| |
| unbindI2cDevice(logger.bus, logger.address); |
| usleep(1000000); |
| |
| const uint16_t faultStatus = getStatusWord(logger.bus, logger.address); |
| if (faultStatus != 0x0000) |
| { |
| lg2::info("MAX34451: Fault detected! Status word: {FAULT}", "FAULT", |
| faultStatus); |
| logFile << "MAX34451: Fault detected! Status word: " << std::hex |
| << faultStatus << '\n'; |
| |
| if (logDetails) |
| { |
| logFile << "[MAX34451 VR log]" << '\n'; |
| getMax34451Status(logger, logFile); |
| } |
| |
| forceMax34451Log(logger); |
| std::vector<uint8_t> max34451BB = getMax34451BB(logger); |
| if (!max34451BB.empty()) |
| { |
| if (logDetails) |
| { |
| max34451BBRParser(max34451BB, logFile); |
| } |
| else |
| { |
| max34451PowerRailParser(logger, max34451BB, logFile); |
| } |
| } |
| clearMax34451BB(logger); |
| } |
| else |
| { |
| lg2::info("MAX34451: No fault detected."); |
| } |
| |
| bindI2cDevice(logger.source, logger.bus, logger.address); |
| |
| lg2::info("MAX34451: Status check completed."); |
| |
| logFile.close(); |
| } |
| |
| } // namespace max34451 |
| |
| int main(int argc, char* argv[]) |
| { |
| CLI::App app{"MAX34451 Status Checker"}; |
| std::filesystem::path configFilePath; |
| std::filesystem::path logDirPath; |
| bool logDetails = false; |
| |
| std::cout << "max34451 status" << '\n'; |
| |
| app.add_option("-c,--config", configFilePath, "Config file path") |
| ->required() |
| ->check(CLI::ExistingFile); |
| app.add_option("-l,--log", logDirPath, "Log directory path")->required(); |
| app.add_flag("-d,--details", logDetails, "Enable detailed logging"); |
| |
| try |
| { |
| app.parse(argc, argv); |
| } |
| catch (const CLI::Error& e) |
| { |
| return app.exit(e); |
| } |
| |
| max34451::runMax34451Logger(configFilePath, logDirPath, logDetails); |
| |
| return EXIT_SUCCESS; |
| } |