474 lines
15 KiB
C++
474 lines
15 KiB
C++
#include <fsfw/tasks/TaskFactory.h>
|
|
#include <fsfw/timemanager/Stopwatch.h>
|
|
#include <fsfw_hal/linux/spi/ManualCsLockGuard.h>
|
|
#include <linux/tcs/Max31865RtdPolling.h>
|
|
|
|
#define OBSW_RTD_AUTO_MODE 1
|
|
|
|
#if OBSW_RTD_AUTO_MODE == 1
|
|
static constexpr uint8_t BASE_CFG = (MAX31865::Bias::ON << MAX31865::CfgBitPos::BIAS_SEL) |
|
|
(MAX31865::Wires::FOUR_WIRE << MAX31865::CfgBitPos::WIRE_SEL) |
|
|
(MAX31865::ConvMode::AUTO << MAX31865::CfgBitPos::CONV_MODE);
|
|
#else
|
|
static constexpr uint8_t BASE_CFG =
|
|
(MAX31865::Bias::OFF << MAX31865::CfgBitPos::BIAS_SEL) |
|
|
(MAX31865::Wires::FOUR_WIRE << MAX31865::CfgBitPos::WIRE_SEL) |
|
|
(MAX31865::ConvMode::NORM_OFF << MAX31865::CfgBitPos::CONV_MODE);
|
|
#endif
|
|
|
|
Max31865RtdPolling::Max31865RtdPolling(object_id_t objectId, SpiComIF* lowLevelComIF,
|
|
GpioIF* gpioIF)
|
|
: SystemObject(objectId), rtds(EiveMax31855::NUM_RTDS), comIF(lowLevelComIF), gpioIF(gpioIF) {
|
|
readerLock = MutexFactory::instance()->createMutex();
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::performOperation(uint8_t operationCode) {
|
|
using namespace MAX31865;
|
|
ReturnValue_t result = returnvalue::OK;
|
|
static_cast<void>(result);
|
|
// Measured to take 0-1 ms in debug build
|
|
// Stopwatch watch;
|
|
periodicInitHandling();
|
|
#if OBSW_RTD_AUTO_MODE == 0
|
|
// 10 ms delay for VBIAS startup
|
|
TaskFactory::delayTask(10);
|
|
result = periodicReadReqHandling();
|
|
if (result != returnvalue::OK) {
|
|
return result;
|
|
}
|
|
// After requesting, 65 milliseconds delay required
|
|
TaskFactory::delayTask(65);
|
|
#endif
|
|
|
|
return periodicReadHandling();
|
|
}
|
|
|
|
bool Max31865RtdPolling::rtdIsActive(uint8_t idx) {
|
|
if (rtds[idx]->on and rtds[idx]->db.active and rtds[idx]->db.configured) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::periodicInitHandling() {
|
|
using namespace MAX31865;
|
|
ReturnValue_t result = returnvalue::OK;
|
|
for (auto& rtd : rtds) {
|
|
if (rtd == nullptr) {
|
|
continue;
|
|
}
|
|
bool mustPerformInitHandling = false;
|
|
bool doWriteLowThreshold = false;
|
|
bool doWriteHighThreshold = false;
|
|
{
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
if (mg.getLockResult() != returnvalue::OK) {
|
|
sif::warning << "Max31865RtdReader::periodicInitHandling: Mutex lock failed" << std::endl;
|
|
continue;
|
|
}
|
|
mustPerformInitHandling =
|
|
(rtd->on or rtd->db.active) and not rtd->db.configured and rtd->cd.hasTimedOut();
|
|
doWriteHighThreshold = rtd->writeHighThreshold;
|
|
doWriteLowThreshold = rtd->writeLowThreshold;
|
|
}
|
|
if (mustPerformInitHandling) {
|
|
// Please note that using the manual CS lock wrapper here is problematic. Might be a SPI
|
|
// or hardware specific issue where the CS needs to be pulled high and then low again
|
|
// between transfers
|
|
result = writeCfgReg(rtd->spiCookie, BASE_CFG);
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "writeCfgReg");
|
|
continue;
|
|
}
|
|
if (doWriteLowThreshold) {
|
|
result = writeLowThreshold(rtd->spiCookie, rtd->lowThreshold);
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "writeLowThreshold");
|
|
}
|
|
}
|
|
if (doWriteHighThreshold) {
|
|
result = writeHighThreshold(rtd->spiCookie, rtd->highThreshold);
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "writeHighThreshold");
|
|
}
|
|
}
|
|
result = clearFaultStatus(rtd->spiCookie);
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "clearFaultStatus");
|
|
}
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
rtd->db.configured = true;
|
|
rtd->db.active = true;
|
|
}
|
|
}
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::periodicReadReqHandling() {
|
|
using namespace MAX31865;
|
|
updateActiveRtdsArray();
|
|
// Now request one shot config for all active RTDs
|
|
for (auto& rtd : rtds) {
|
|
if (rtd == nullptr) {
|
|
continue;
|
|
}
|
|
if (activeRtdsArray[rtd->idx]) {
|
|
ReturnValue_t result = writeCfgReg(rtd->spiCookie, BASE_CFG | (1 << CfgBitPos::ONE_SHOT));
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "writeCfgReg");
|
|
// Release mutex ASAP
|
|
return returnvalue::FAILED;
|
|
}
|
|
}
|
|
}
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::periodicReadHandling() {
|
|
using namespace MAX31865;
|
|
auto result = returnvalue::OK;
|
|
updateActiveRtdsArray();
|
|
// Now read the RTD values
|
|
for (auto& rtd : rtds) {
|
|
if (rtd == nullptr) {
|
|
continue;
|
|
}
|
|
if (activeRtdsArray[rtd->idx]) {
|
|
// Please note that using the manual CS lock wrapper here is problematic. Might be a SPI
|
|
// or hardware specific issue where the CS needs to be pulled high and then low again
|
|
// between transfers
|
|
uint16_t rtdVal = 0;
|
|
bool faultBitSet = false;
|
|
result = writeCfgReg(rtd->spiCookie, BASE_CFG);
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "writeCfgReg");
|
|
continue;
|
|
}
|
|
result = readRtdVal(rtd->spiCookie, rtdVal, faultBitSet);
|
|
// sif::debug << "RTD Val: " << rtdVal << std::endl;
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtd, result, "readRtdVal");
|
|
continue;
|
|
}
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
if (faultBitSet) {
|
|
rtd->db.faultBitSet = faultBitSet;
|
|
}
|
|
rtd->db.adcCode = rtdVal;
|
|
}
|
|
}
|
|
#if OBSW_RTD_AUTO_MODE == 0
|
|
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, BASE_CFG);
|
|
}
|
|
}
|
|
#endif
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::initializeInterface(CookieIF* cookie) {
|
|
if (cookie == nullptr) {
|
|
throw std::invalid_argument("Invalid MAX31865 Reader Cookie");
|
|
}
|
|
auto* rtdCookie = dynamic_cast<Max31865ReaderCookie*>(cookie);
|
|
ReturnValue_t result = comIF->initializeInterface(rtdCookie->spiCookie);
|
|
if (result != returnvalue::OK) {
|
|
return result;
|
|
}
|
|
if (rtdCookie->idx > EiveMax31855::NUM_RTDS) {
|
|
throw std::invalid_argument("Invalid RTD index");
|
|
}
|
|
rtds[rtdCookie->idx] = rtdCookie;
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
if (dbLen == 0) {
|
|
dbLen = rtdCookie->db.getSerializedSize();
|
|
}
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::sendMessage(CookieIF* cookie, const uint8_t* sendData,
|
|
size_t sendLen) {
|
|
if (cookie == nullptr) {
|
|
return returnvalue::FAILED;
|
|
}
|
|
auto* rtdCookie = dynamic_cast<Max31865ReaderCookie*>(cookie);
|
|
if (rtdCookie == nullptr) {
|
|
return returnvalue::FAILED;
|
|
}
|
|
// Empty command.. don't fail for now
|
|
if (sendLen < 1) {
|
|
return returnvalue::OK;
|
|
}
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
if (mg.getLockResult() != returnvalue::OK) {
|
|
sif::warning << "Max31865RtdReader::sendMessage: Mutex lock failed" << std::endl;
|
|
return returnvalue::FAILED;
|
|
}
|
|
uint8_t cmdRaw = sendData[0];
|
|
if (cmdRaw > EiveMax31855::RtdCommands::NUM_CMDS) {
|
|
sif::warning << "Max31865RtdReader::sendMessage: Invalid command" << std::endl;
|
|
return returnvalue::FAILED;
|
|
}
|
|
|
|
auto thresholdHandler = [&]() {
|
|
rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2];
|
|
rtdCookie->highThreshold = (sendData[3] << 8) | sendData[4];
|
|
rtdCookie->writeLowThreshold = true;
|
|
rtdCookie->writeHighThreshold = true;
|
|
};
|
|
|
|
auto cmd = static_cast<EiveMax31855::RtdCommands>(sendData[0]);
|
|
switch (cmd) {
|
|
case (EiveMax31855::RtdCommands::ON): {
|
|
if (not rtdCookie->on) {
|
|
rtdCookie->cd.setTimeout(MAX31865::WARMUP_MS);
|
|
rtdCookie->on = true;
|
|
rtdCookie->db.active = false;
|
|
rtdCookie->db.configured = false;
|
|
if (sendLen == 5) {
|
|
thresholdHandler();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case (EiveMax31855::RtdCommands::ACTIVE): {
|
|
if (not rtdCookie->on) {
|
|
rtdCookie->cd.setTimeout(MAX31865::WARMUP_MS);
|
|
rtdCookie->on = true;
|
|
rtdCookie->db.active = true;
|
|
rtdCookie->db.configured = false;
|
|
} else {
|
|
rtdCookie->db.active = true;
|
|
}
|
|
if (sendLen == 5) {
|
|
thresholdHandler();
|
|
}
|
|
break;
|
|
}
|
|
case (EiveMax31855::RtdCommands::OFF): {
|
|
rtdCookie->on = false;
|
|
rtdCookie->db.active = false;
|
|
rtdCookie->db.configured = false;
|
|
break;
|
|
}
|
|
case (EiveMax31855::RtdCommands::HIGH_TRESHOLD): {
|
|
if (sendLen == 3) {
|
|
rtdCookie->highThreshold = (sendData[1] << 8) | sendData[2];
|
|
rtdCookie->writeHighThreshold = true;
|
|
} else {
|
|
return returnvalue::FAILED;
|
|
}
|
|
break;
|
|
}
|
|
case (EiveMax31855::RtdCommands::LOW_THRESHOLD): {
|
|
if (sendLen == 3) {
|
|
rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2];
|
|
rtdCookie->writeLowThreshold = true;
|
|
} else {
|
|
return returnvalue::FAILED;
|
|
}
|
|
break;
|
|
}
|
|
case (EiveMax31855::RtdCommands::CFG): {
|
|
ReturnValue_t result = writeCfgReg(rtdCookie->spiCookie, BASE_CFG);
|
|
if (result != returnvalue::OK) {
|
|
handleSpiError(rtdCookie, result, "writeCfgReg");
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
// TODO: Only implement if needed
|
|
break;
|
|
}
|
|
}
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
|
|
|
|
ReturnValue_t Max31865RtdPolling::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
|
|
size_t* size) {
|
|
auto* rtdCookie = dynamic_cast<Max31865ReaderCookie*>(cookie);
|
|
if (rtdCookie == nullptr) {
|
|
return returnvalue::FAILED;
|
|
}
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
if (mg.getLockResult() != returnvalue::OK) {
|
|
// TODO: Emit warning
|
|
return returnvalue::FAILED;
|
|
}
|
|
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 != returnvalue::OK) {
|
|
// TODO: Emit warning
|
|
return returnvalue::FAILED;
|
|
}
|
|
*buffer = reinterpret_cast<uint8_t*>(rtdCookie->exchangeBuf.data());
|
|
*size = serLen;
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::writeCfgReg(SpiCookie* cookie, uint8_t cfg) {
|
|
using namespace MAX31865;
|
|
return writeNToReg(cookie, CONFIG, 1, &cfg, nullptr);
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::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 Max31865RtdPolling::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 != returnvalue::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 Max31865RtdPolling::readCfgReg(SpiCookie* cookie, uint8_t& cfg) {
|
|
using namespace MAX31865;
|
|
uint8_t* replyPtr = nullptr;
|
|
auto result = readNFromReg(cookie, CONFIG, 1, &replyPtr);
|
|
if (result == returnvalue::OK) {
|
|
cfg = replyPtr[0];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::writeLowThreshold(SpiCookie* cookie, uint16_t val) {
|
|
using namespace MAX31865;
|
|
uint8_t cmd[2] = {static_cast<uint8_t>((val >> 8) & 0xff), static_cast<uint8_t>(val & 0xff)};
|
|
return writeNToReg(cookie, LOW_THRESHOLD, 2, cmd, nullptr);
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::writeHighThreshold(SpiCookie* cookie, uint16_t val) {
|
|
using namespace MAX31865;
|
|
uint8_t cmd[2] = {static_cast<uint8_t>((val >> 8) & 0xff), static_cast<uint8_t>(val & 0xff)};
|
|
return writeNToReg(cookie, HIGH_THRESHOLD, 2, cmd, nullptr);
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::readLowThreshold(SpiCookie* cookie, uint16_t& lowThreshold) {
|
|
using namespace MAX31865;
|
|
uint8_t* replyPtr = nullptr;
|
|
auto result = readNFromReg(cookie, LOW_THRESHOLD, 2, &replyPtr);
|
|
if (result == returnvalue::OK) {
|
|
lowThreshold = (replyPtr[0] << 8) | replyPtr[1];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::readHighThreshold(SpiCookie* cookie, uint16_t& highThreshold) {
|
|
using namespace MAX31865;
|
|
uint8_t* replyPtr = nullptr;
|
|
auto result = readNFromReg(cookie, HIGH_THRESHOLD, 2, &replyPtr);
|
|
if (result == returnvalue::OK) {
|
|
highThreshold = (replyPtr[0] << 8) | replyPtr[1];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::writeNToReg(SpiCookie* cookie, uint8_t reg, size_t n,
|
|
uint8_t* cmd, uint8_t** reply) {
|
|
using namespace MAX31865;
|
|
if (n > cmdBuf.size() - 1) {
|
|
return returnvalue::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 Max31865RtdPolling::readRtdVal(SpiCookie* cookie, uint16_t& val, bool& faultBitSet) {
|
|
using namespace MAX31865;
|
|
uint8_t* replyPtr = nullptr;
|
|
auto result = readNFromReg(cookie, RTD, 2, &replyPtr);
|
|
if (result != returnvalue::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 Max31865RtdPolling::readNFromReg(SpiCookie* cookie, uint8_t reg, size_t n,
|
|
uint8_t** reply) {
|
|
using namespace MAX31865;
|
|
if (n > 4) {
|
|
return returnvalue::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 != returnvalue::OK) {
|
|
return returnvalue::FAILED;
|
|
}
|
|
|
|
size_t dummyLen = 0;
|
|
uint8_t* replyPtr = nullptr;
|
|
result = comIF->readReceivedMessage(cookie, &replyPtr, &dummyLen);
|
|
if (result != returnvalue::OK) {
|
|
return result;
|
|
}
|
|
if (reply != nullptr) {
|
|
*reply = replyPtr + 1;
|
|
}
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::updateActiveRtdsArray() {
|
|
MutexGuard mg(readerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
|
if (mg.getLockResult() != returnvalue::OK) {
|
|
sif::warning << "Max31865RtdReader::periodicReadHandling: Mutex lock failed" << std::endl;
|
|
return returnvalue::FAILED;
|
|
}
|
|
for (const auto& rtd : rtds) {
|
|
activeRtdsArray[rtd->idx] = rtdIsActive(rtd->idx);
|
|
}
|
|
return returnvalue::OK;
|
|
}
|
|
|
|
ReturnValue_t Max31865RtdPolling::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 Max31865RtdPolling::initialize() {
|
|
csLock = comIF->getCsMutex();
|
|
return SystemObject::initialize();
|
|
}
|