#include "Max31865RtdLowlevelHandler.h" #include #include #include Max31865RtdReader::Max31865RtdReader(object_id_t objectId, SpiComIF* lowLevelComIF, GpioIF* gpioIF) : SystemObject(objectId), rtds(EiveMax31855::NUM_RTDS), comIF(lowLevelComIF), gpioIF(gpioIF) { readerMutex = MutexFactory::instance()->createMutex(); } ReturnValue_t Max31865RtdReader::performOperation(uint8_t operationCode) { using namespace MAX31865; ReturnValue_t result = RETURN_OK; // Stopwatch watch; if (periodicInitHandling()) { // 10 ms delay for VBIAS startup TaskFactory::delayTask(10); } else { // No devices usable (e.g. TCS board off) return RETURN_OK; } result = periodicReadReqHandling(); if (result != RETURN_OK) { return result; } // After requesting, 65 milliseconds delay required TaskFactory::delayTask(65); return periodicReadHandling(); } bool Max31865RtdReader::rtdIsActive(uint8_t idx) { if (rtds[idx]->on and rtds[idx]->active and rtds[idx]->configured) { return true; } return false; } bool Max31865RtdReader::periodicInitHandling() { using namespace MAX31865; MutexGuard mg(readerMutex); ReturnValue_t result = RETURN_OK; if (mg.getLockResult() != RETURN_OK) { sif::warning << "Max31865RtdReader::periodicInitHandling: Mutex lock failed" << std::endl; return false; } for (auto& rtd : rtds) { if (rtd == nullptr) { continue; } if ((rtd->on or rtd->active) and not rtd->configured and rtd->cd.hasTimedOut()) { ManualCsLockWrapper mg(csLock, gpioIF, rtd->spiCookie, csTimeoutType, csTimeoutMs); if (mg.lockResult != RETURN_OK or mg.gpioResult != RETURN_OK) { sif::error << "Max31865RtdReader::periodicInitHandling: Manual CS lock failed" << std::endl; break; } uint8_t cfg = rtd->defCfg; result = writeCfgReg(rtd->spiCookie, cfg); if (result != HasReturnvaluesIF::RETURN_OK) { handleSpiError(rtd, result, "writeCfgReg"); } if (rtd->writeLowThreshold) { result = writeLowThreshold(rtd->spiCookie, rtd->lowThreshold); if (result != HasReturnvaluesIF::RETURN_OK) { handleSpiError(rtd, result, "writeLowThreshold"); } } if (rtd->writeHighThreshold) { result = writeHighThreshold(rtd->spiCookie, rtd->highThreshold); if (result != HasReturnvaluesIF::RETURN_OK) { handleSpiError(rtd, result, "writeHighThreshold"); } } result = clearFaultStatus(rtd->spiCookie); if (result != HasReturnvaluesIF::RETURN_OK) { handleSpiError(rtd, result, "clearFaultStatus"); } rtd->configured = true; rtd->db.configured = true; if (rtd->active) { rtd->db.active = true; } } if (rtd->active and rtd->configured and not rtd->db.active) { rtd->db.active = true; } } bool someRtdUsable = false; for (auto& rtd : rtds) { if (rtd == nullptr) { continue; } if (rtdIsActive(rtd->idx)) { someRtdUsable = true; result = writeBiasSel(Bias::ON, rtd->spiCookie, rtd->defCfg); } } return someRtdUsable; } ReturnValue_t Max31865RtdReader::periodicReadReqHandling() { using namespace MAX31865; MutexGuard mg(readerMutex); if (mg.getLockResult() != RETURN_OK) { sif::warning << "Max31865RtdReader::periodicReadReqHandling: Mutex lock failed" << std::endl; return RETURN_FAILED; } // Now request one shot config for all active RTDs for (auto& rtd : rtds) { if (rtd == nullptr) { continue; } if (rtdIsActive(rtd->idx)) { ReturnValue_t result = writeCfgReg(rtd->spiCookie, rtd->defCfg | (1 << CfgBitPos::ONE_SHOT)); if (result != RETURN_OK) { handleSpiError(rtd, result, "writeCfgReg"); // Release mutex ASAP return RETURN_FAILED; } } } return RETURN_OK; } ReturnValue_t Max31865RtdReader::periodicReadHandling() { using namespace MAX31865; auto result = RETURN_OK; MutexGuard mg(readerMutex); if (mg.getLockResult() != RETURN_OK) { sif::warning << "Max31865RtdReader::periodicReadReqHandling: Mutex lock failed" << std::endl; return RETURN_FAILED; } // Now read the RTD values for (auto& rtd : rtds) { if (rtd == nullptr) { continue; } if (rtdIsActive(rtd->idx)) { uint16_t rtdVal = 0; bool faultBitSet = false; result = readRtdVal(rtd->spiCookie, rtdVal, faultBitSet); if (result != RETURN_OK) { handleSpiError(rtd, result, "readRtdVal"); return RETURN_FAILED; } if (faultBitSet) { rtd->db.faultBitSet = faultBitSet; } rtd->db.adcCode = rtdVal; } } for (auto& rtd : rtds) { if (rtd == nullptr) { continue; } // Even if a device was made inactive, turn off the bias here. If it was turned off, not // necessary anymore.. if (rtd->on) { result = writeBiasSel(Bias::OFF, rtd->spiCookie, rtd->defCfg); } } return RETURN_OK; } ReturnValue_t Max31865RtdReader::initializeInterface(CookieIF* cookie) { if (cookie == nullptr) { throw std::invalid_argument("Invalid MAX31865 Reader Cookie"); } auto* rtdCookie = dynamic_cast(cookie); ReturnValue_t result = comIF->initializeInterface(rtdCookie->spiCookie); if (result != HasReturnvaluesIF::RETURN_OK) { return result; } if (rtdCookie->idx > EiveMax31855::NUM_RTDS) { throw std::invalid_argument("Invalid RTD index"); } rtds[rtdCookie->idx] = rtdCookie; MutexGuard mg(readerMutex); if (dbLen == 0) { dbLen = rtdCookie->db.getSerializedSize(); } return RETURN_OK; } ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) { if (cookie == nullptr) { return RETURN_FAILED; } // Empty command.. don't fail for now if (sendLen < 1) { return RETURN_OK; } MutexGuard mg(readerMutex); if (mg.getLockResult() != RETURN_OK) { sif::warning << "Max31865RtdReader::sendMessage: Mutex lock failed" << std::endl; return RETURN_FAILED; } auto* rtdCookie = dynamic_cast(cookie); uint8_t cmdRaw = sendData[0]; if (cmdRaw > EiveMax31855::RtdCommands::NUM_CMDS) { sif::warning << "Max31865RtdReader::sendMessage: Invalid command" << std::endl; return RETURN_FAILED; } auto thresholdHandler = [](Max31865ReaderCookie* rtdCookie, const uint8_t* sendData) { rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2]; rtdCookie->highThreshold = (sendData[3] << 8) | sendData[4]; rtdCookie->writeLowThreshold = true; rtdCookie->writeHighThreshold = true; }; auto cmd = static_cast(sendData[0]); switch (cmd) { case (EiveMax31855::RtdCommands::ON): { if (not rtdCookie->on) { rtdCookie->cd.setTimeout(MAX31865::WARMUP_MS); rtdCookie->cd.resetTimer(); rtdCookie->on = true; rtdCookie->active = false; rtdCookie->configured = false; if (sendLen == 5) { thresholdHandler(rtdCookie, sendData); } } break; } case (EiveMax31855::RtdCommands::ACTIVE): { if (not rtdCookie->on) { rtdCookie->cd.setTimeout(MAX31865::WARMUP_MS); rtdCookie->cd.resetTimer(); rtdCookie->on = true; rtdCookie->active = true; rtdCookie->configured = false; } else { rtdCookie->active = true; } if (sendLen == 5) { thresholdHandler(rtdCookie, sendData); } break; } case (EiveMax31855::RtdCommands::OFF): { rtdCookie->on = false; rtdCookie->active = false; rtdCookie->configured = false; break; } case (EiveMax31855::RtdCommands::HIGH_TRESHOLD): { if (sendLen == 3) { rtdCookie->highThreshold = (sendData[1] << 8) | sendData[2]; rtdCookie->writeHighThreshold = true; } else { return RETURN_FAILED; } break; } case (EiveMax31855::RtdCommands::LOW_THRESHOLD): { if (sendLen == 3) { rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2]; rtdCookie->writeLowThreshold = true; } else { return RETURN_FAILED; } break; } case (EiveMax31855::RtdCommands::CFG): default: { // TODO: Only implement if needed break; } } return RETURN_OK; } ReturnValue_t Max31865RtdReader::getSendSuccess(CookieIF* cookie) { return RETURN_OK; } ReturnValue_t Max31865RtdReader::requestReceiveMessage(CookieIF* cookie, size_t requestLen) { return RETURN_OK; } ReturnValue_t Max31865RtdReader::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { MutexGuard mg(readerMutex); if (mg.getLockResult() != RETURN_OK) { // TODO: Emit warning return RETURN_FAILED; } auto* rtdCookie = dynamic_cast(cookie); uint8_t* exchangePtr = rtdCookie->exchangeBuf.data(); size_t serLen = 0; auto result = rtdCookie->db.serialize(&exchangePtr, &serLen, rtdCookie->exchangeBuf.size(), SerializeIF::Endianness::MACHINE); if (result != RETURN_OK) { // TODO: Emit warning return RETURN_FAILED; } *buffer = reinterpret_cast(rtdCookie->exchangeBuf.data()); *size = serLen; return RETURN_OK; } ReturnValue_t Max31865RtdReader::writeCfgReg(SpiCookie* cookie, uint8_t cfg) { using namespace MAX31865; return writeNToReg(cookie, CONFIG, 1, &cfg, nullptr); } ReturnValue_t Max31865RtdReader::writeBiasSel(MAX31865::Bias bias, SpiCookie* cookie, uint8_t baseCfg) { using namespace MAX31865; if (bias == MAX31865::Bias::OFF) { baseCfg &= ~(1 << CfgBitPos::BIAS_SEL); } else { baseCfg |= (1 << CfgBitPos::BIAS_SEL); } return writeCfgReg(cookie, baseCfg); } ReturnValue_t Max31865RtdReader::clearFaultStatus(SpiCookie* cookie) { using namespace MAX31865; // Read back the current configuration to avoid overwriting it when clearing te fault status uint8_t currentCfg = 0; auto result = readCfgReg(cookie, currentCfg); if (result != RETURN_OK) { return result; } // Clear bytes 5, 3 and 2 which need to be 0 currentCfg &= ~0x2C; currentCfg |= (1 << CfgBitPos::FAULT_STATUS_CLEAR); return writeCfgReg(cookie, currentCfg); } ReturnValue_t Max31865RtdReader::readCfgReg(SpiCookie* cookie, uint8_t& cfg) { using namespace MAX31865; uint8_t* replyPtr = nullptr; auto result = readNFromReg(cookie, CONFIG, 1, &replyPtr); if (result == RETURN_OK) { cfg = replyPtr[0]; } return result; } ReturnValue_t Max31865RtdReader::writeLowThreshold(SpiCookie* cookie, uint16_t val) { using namespace MAX31865; uint8_t cmd[2] = {static_cast((val >> 8) & 0xff), static_cast(val & 0xff)}; return writeNToReg(cookie, LOW_THRESHOLD, 2, cmd, nullptr); } ReturnValue_t Max31865RtdReader::writeHighThreshold(SpiCookie* cookie, uint16_t val) { using namespace MAX31865; uint8_t cmd[2] = {static_cast((val >> 8) & 0xff), static_cast(val & 0xff)}; return writeNToReg(cookie, HIGH_THRESHOLD, 2, cmd, nullptr); } ReturnValue_t Max31865RtdReader::readLowThreshold(SpiCookie* cookie, uint16_t& lowThreshold) { using namespace MAX31865; uint8_t* replyPtr = nullptr; auto result = readNFromReg(cookie, LOW_THRESHOLD, 2, &replyPtr); if (result == RETURN_OK) { lowThreshold = (replyPtr[0] << 8) | replyPtr[1]; } return result; } ReturnValue_t Max31865RtdReader::readHighThreshold(SpiCookie* cookie, uint16_t& highThreshold) { using namespace MAX31865; uint8_t* replyPtr = nullptr; auto result = readNFromReg(cookie, HIGH_THRESHOLD, 2, &replyPtr); if (result == RETURN_OK) { highThreshold = (replyPtr[0] << 8) | replyPtr[1]; } return result; } ReturnValue_t Max31865RtdReader::writeNToReg(SpiCookie* cookie, uint8_t reg, size_t n, uint8_t* cmd, uint8_t** reply) { using namespace MAX31865; if (n > cmdBuf.size() - 1) { return HasReturnvaluesIF::RETURN_FAILED; } cmdBuf[0] = reg | WRITE_BIT; for (size_t idx = 0; idx < n; idx++) { cmdBuf[idx + 1] = cmd[idx]; } return comIF->sendMessage(cookie, cmdBuf.data(), n + 1); } ReturnValue_t Max31865RtdReader::readRtdVal(SpiCookie* cookie, uint16_t& val, bool& faultBitSet) { using namespace MAX31865; uint8_t* replyPtr = nullptr; auto result = readNFromReg(cookie, RTD, 2, &replyPtr); if (result != RETURN_OK) { return result; } if (replyPtr[1] & 0b0000'0001) { faultBitSet = true; } // Shift 1 to the right to remove fault bit val = ((replyPtr[0] << 8) | replyPtr[1]) >> 1; return result; } ReturnValue_t Max31865RtdReader::readNFromReg(SpiCookie* cookie, uint8_t reg, size_t n, uint8_t** reply) { using namespace MAX31865; if (n > 4) { return HasReturnvaluesIF::RETURN_FAILED; } // Clear write bit in any case reg &= ~WRITE_BIT; cmdBuf[0] = reg; std::memset(cmdBuf.data() + 1, 0, n); ReturnValue_t result = comIF->sendMessage(cookie, cmdBuf.data(), n + 1); if (result != RETURN_OK) { return RETURN_FAILED; } size_t dummyLen = 0; uint8_t* replyPtr = nullptr; result = comIF->readReceivedMessage(cookie, &replyPtr, &dummyLen); if (result != RETURN_OK) { return result; } if (reply != nullptr) { *reply = replyPtr + 1; } return RETURN_OK; } ReturnValue_t Max31865RtdReader::handleSpiError(Max31865ReaderCookie* cookie, ReturnValue_t result, const char* ctx) { cookie->db.spiErrorCount.value += 1; sif::warning << "Max31865RtdReader::handleSpiError: " << ctx << " | Failed with result " << result << std::endl; return result; } ReturnValue_t Max31865RtdReader::initialize() { csLock = comIF->getCsMutex(); return SystemObject::initialize(); }