eive-obsw/linux/devices/Max31865RtdLowlevelHandler.cpp

480 lines
15 KiB
C++
Raw Normal View History

2022-05-12 11:27:30 +02:00
#include "Max31865RtdLowlevelHandler.h"
#include <fsfw/tasks/TaskFactory.h>
2022-05-14 11:34:25 +02:00
#include <fsfw/timemanager/Stopwatch.h>
#include <fsfw_hal/linux/spi/ManualCsLockGuard.h>
2022-05-14 17:32:33 +02:00
#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
2022-05-14 11:34:25 +02:00
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;
2022-08-24 17:27:47 +02:00
ReturnValue_t result = returnvalue::OK;
2022-05-14 17:32:33 +02:00
static_cast<void>(result);
2022-05-14 11:34:25 +02:00
// Stopwatch watch;
if (periodicInitHandling()) {
2022-05-14 17:32:33 +02:00
#if OBSW_RTD_AUTO_MODE == 0
// 10 ms delay for VBIAS startup
TaskFactory::delayTask(10);
2022-05-14 17:32:33 +02:00
#endif
} else {
// No devices usable (e.g. TCS board off)
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
2022-05-14 17:32:33 +02:00
#if OBSW_RTD_AUTO_MODE == 0
result = periodicReadReqHandling();
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-05-14 17:22:57 +02:00
return result;
}
// After requesting, 65 milliseconds delay required
TaskFactory::delayTask(65);
2022-05-14 17:32:33 +02:00
#endif
2022-05-14 17:22:57 +02:00
return periodicReadHandling();
}
2022-05-12 09:48:41 +02:00
bool Max31865RtdReader::rtdIsActive(uint8_t idx) {
2022-11-03 15:10:42 +01:00
if (rtds[idx]->on and rtds[idx]->db.active and rtds[idx]->db.configured) {
return true;
}
return false;
}
bool Max31865RtdReader::periodicInitHandling() {
using namespace MAX31865;
2022-08-24 17:27:47 +02:00
ReturnValue_t result = returnvalue::OK;
for (auto& rtd : rtds) {
if (rtd == nullptr) {
continue;
}
2023-02-12 20:01:20 +01:00
MutexGuard mg(readerMutex);
if (mg.getLockResult() != returnvalue::OK) {
sif::warning << "Max31865RtdReader::periodicInitHandling: Mutex lock failed" << std::endl;
return false;
}
2022-11-03 15:10:42 +01:00
if ((rtd->on or rtd->db.active) and not rtd->db.configured and rtd->cd.hasTimedOut()) {
2023-02-13 14:19:07 +01:00
ManualCsLockWrapper mg1(csLock, gpioIF, rtd->spiCookie, csTimeoutType, csTimeoutMs);
if (mg1.lockResult != returnvalue::OK or mg1.gpioResult != returnvalue::OK) {
sif::error << "Max31865RtdReader::periodicInitHandling: Manual CS lock failed" << std::endl;
2023-02-12 21:07:33 +01:00
continue;
}
2022-05-14 17:32:33 +02:00
result = writeCfgReg(rtd->spiCookie, BASE_CFG);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
handleSpiError(rtd, result, "writeCfgReg");
2023-02-12 21:07:33 +01:00
continue;
}
if (rtd->writeLowThreshold) {
result = writeLowThreshold(rtd->spiCookie, rtd->lowThreshold);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
handleSpiError(rtd, result, "writeLowThreshold");
2022-05-12 09:48:41 +02:00
}
}
if (rtd->writeHighThreshold) {
result = writeHighThreshold(rtd->spiCookie, rtd->highThreshold);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
handleSpiError(rtd, result, "writeHighThreshold");
2022-05-14 09:41:28 +02:00
}
}
result = clearFaultStatus(rtd->spiCookie);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
handleSpiError(rtd, result, "clearFaultStatus");
}
rtd->db.configured = true;
rtd->db.active = true;
}
}
bool someRtdUsable = false;
for (auto& rtd : rtds) {
if (rtd == nullptr) {
continue;
}
2022-05-12 09:48:41 +02:00
if (rtdIsActive(rtd->idx)) {
2022-05-14 17:32:33 +02:00
#if OBSW_RTD_AUTO_MODE == 0
result = writeBiasSel(Bias::ON, rtd->spiCookie, BASE_CFG);
#endif
2022-05-24 01:14:04 +02:00
someRtdUsable = true;
}
}
return someRtdUsable;
}
ReturnValue_t Max31865RtdReader::periodicReadReqHandling() {
using namespace MAX31865;
// Now request one shot config for all active RTDs
for (auto& rtd : rtds) {
if (rtd == nullptr) {
continue;
}
2023-02-12 20:01:20 +01:00
MutexGuard mg(readerMutex);
if (mg.getLockResult() != returnvalue::OK) {
sif::warning << "Max31865RtdReader::periodicReadReqHandling: Mutex lock failed" << std::endl;
return returnvalue::FAILED;
}
2022-05-12 09:48:41 +02:00
if (rtdIsActive(rtd->idx)) {
2022-05-14 17:32:33 +02:00
ReturnValue_t result = writeCfgReg(rtd->spiCookie, BASE_CFG | (1 << CfgBitPos::ONE_SHOT));
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-05-14 09:41:28 +02:00
handleSpiError(rtd, result, "writeCfgReg");
2022-05-14 11:34:25 +02:00
// Release mutex ASAP
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
2022-05-14 09:41:28 +02:00
}
}
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
ReturnValue_t Max31865RtdReader::periodicReadHandling() {
using namespace MAX31865;
2022-08-24 17:27:47 +02:00
auto result = returnvalue::OK;
// Now read the RTD values
for (auto& rtd : rtds) {
if (rtd == nullptr) {
continue;
}
2023-02-12 20:01:20 +01:00
MutexGuard mg(readerMutex);
if (mg.getLockResult() != returnvalue::OK) {
sif::warning << "Max31865RtdReader::periodicReadHandling: Mutex lock failed" << std::endl;
return returnvalue::FAILED;
}
2022-05-12 09:48:41 +02:00
if (rtdIsActive(rtd->idx)) {
2023-02-13 14:19:07 +01:00
ManualCsLockWrapper mg1(csLock, gpioIF, rtd->spiCookie, csTimeoutType, csTimeoutMs);
if (mg1.lockResult != returnvalue::OK or mg1.gpioResult != returnvalue::OK) {
2023-02-12 21:07:33 +01:00
sif::error << "Max31865RtdReader::periodicInitHandling: Manual CS lock failed" << std::endl;
continue;
}
uint16_t rtdVal = 0;
bool faultBitSet = false;
2023-01-19 16:42:47 +01:00
result = writeCfgReg(rtd->spiCookie, BASE_CFG);
if (result != returnvalue::OK) {
2023-01-20 10:44:44 +01:00
handleSpiError(rtd, result, "writeCfgReg");
2023-02-12 21:07:33 +01:00
continue;
2023-01-19 16:42:47 +01:00
}
result = readRtdVal(rtd->spiCookie, rtdVal, faultBitSet);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-05-12 11:27:30 +02:00
handleSpiError(rtd, result, "readRtdVal");
2023-02-12 21:07:33 +01:00
continue;
}
if (faultBitSet) {
rtd->db.faultBitSet = faultBitSet;
}
2022-05-14 09:41:28 +02:00
rtd->db.adcCode = rtdVal;
}
}
2022-05-14 17:32:33 +02:00
#if OBSW_RTD_AUTO_MODE == 0
for (auto& rtd : rtds) {
if (rtd == nullptr) {
continue;
}
2022-05-12 09:48:41 +02:00
// Even if a device was made inactive, turn off the bias here. If it was turned off, not
// necessary anymore..
if (rtd->on) {
2022-05-14 17:32:33 +02:00
result = writeBiasSel(Bias::OFF, rtd->spiCookie, BASE_CFG);
}
}
2022-05-14 17:32:33 +02:00
#endif
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
ReturnValue_t Max31865RtdReader::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);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
return result;
}
2022-05-12 09:48:41 +02:00
if (rtdCookie->idx > EiveMax31855::NUM_RTDS) {
throw std::invalid_argument("Invalid RTD index");
}
rtds[rtdCookie->idx] = rtdCookie;
2022-05-14 09:41:28 +02:00
MutexGuard mg(readerMutex);
if (dbLen == 0) {
dbLen = rtdCookie->db.getSerializedSize();
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* sendData,
size_t sendLen) {
if (cookie == nullptr) {
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
}
2022-05-12 11:27:30 +02:00
// Empty command.. don't fail for now
if (sendLen < 1) {
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
MutexGuard mg(readerMutex);
2022-08-24 17:27:47 +02:00
if (mg.getLockResult() != returnvalue::OK) {
2022-05-12 11:27:30 +02:00
sif::warning << "Max31865RtdReader::sendMessage: Mutex lock failed" << std::endl;
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
}
auto* rtdCookie = dynamic_cast<Max31865ReaderCookie*>(cookie);
uint8_t cmdRaw = sendData[0];
2022-05-12 11:27:30 +02:00
if (cmdRaw > EiveMax31855::RtdCommands::NUM_CMDS) {
sif::warning << "Max31865RtdReader::sendMessage: Invalid command" << std::endl;
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
}
2022-05-12 11:27:30 +02:00
2023-02-17 12:19:53 +01:00
auto thresholdHandler = [&]() {
2022-05-12 11:27:30 +02:00
rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2];
rtdCookie->highThreshold = (sendData[3] << 8) | sendData[4];
rtdCookie->writeLowThreshold = true;
rtdCookie->writeHighThreshold = true;
};
2022-05-12 09:48:41 +02:00
auto cmd = static_cast<EiveMax31855::RtdCommands>(sendData[0]);
switch (cmd) {
2022-05-12 09:48:41 +02:00
case (EiveMax31855::RtdCommands::ON): {
if (not rtdCookie->on) {
rtdCookie->cd.setTimeout(MAX31865::WARMUP_MS);
rtdCookie->cd.resetTimer();
rtdCookie->on = true;
2022-11-03 15:10:42 +01:00
rtdCookie->db.active = false;
rtdCookie->db.configured = false;
2022-05-12 11:27:30 +02:00
if (sendLen == 5) {
2023-02-17 12:19:53 +01:00
thresholdHandler();
2022-05-12 11:27:30 +02:00
}
2022-05-12 09:48:41 +02:00
}
break;
}
case (EiveMax31855::RtdCommands::ACTIVE): {
if (not rtdCookie->on) {
rtdCookie->cd.setTimeout(MAX31865::WARMUP_MS);
rtdCookie->cd.resetTimer();
rtdCookie->on = true;
2022-11-03 15:10:42 +01:00
rtdCookie->db.active = true;
rtdCookie->db.configured = false;
2022-05-12 09:48:41 +02:00
} else {
2022-11-03 15:10:42 +01:00
rtdCookie->db.active = true;
}
2022-05-12 11:27:30 +02:00
if (sendLen == 5) {
2023-02-17 12:19:53 +01:00
thresholdHandler();
2022-05-12 11:27:30 +02:00
}
break;
}
2022-05-12 09:48:41 +02:00
case (EiveMax31855::RtdCommands::OFF): {
rtdCookie->on = false;
2022-11-03 15:10:42 +01:00
rtdCookie->db.active = false;
rtdCookie->db.configured = false;
break;
}
2022-05-12 11:27:30 +02:00
case (EiveMax31855::RtdCommands::HIGH_TRESHOLD): {
if (sendLen == 3) {
rtdCookie->highThreshold = (sendData[1] << 8) | sendData[2];
rtdCookie->writeHighThreshold = true;
} else {
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
2022-05-12 11:27:30 +02:00
}
break;
}
case (EiveMax31855::RtdCommands::LOW_THRESHOLD): {
if (sendLen == 3) {
rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2];
rtdCookie->writeLowThreshold = true;
} else {
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
2022-05-12 11:27:30 +02:00
}
break;
}
2023-01-19 13:20:24 +01:00
case (EiveMax31855::RtdCommands::CFG): {
2023-01-20 10:44:44 +01:00
ReturnValue_t result = writeCfgReg(rtdCookie->spiCookie, BASE_CFG);
if (result != returnvalue::OK) {
handleSpiError(rtdCookie, result, "writeCfgReg");
}
break;
2023-01-19 13:20:24 +01:00
}
2022-05-12 09:48:41 +02:00
default: {
// TODO: Only implement if needed
break;
}
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
2022-08-24 17:27:47 +02:00
ReturnValue_t Max31865RtdReader::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
ReturnValue_t Max31865RtdReader::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
ReturnValue_t Max31865RtdReader::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
size_t* size) {
MutexGuard mg(readerMutex);
2022-08-24 17:27:47 +02:00
if (mg.getLockResult() != returnvalue::OK) {
// TODO: Emit warning
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
}
auto* rtdCookie = dynamic_cast<Max31865ReaderCookie*>(cookie);
2022-08-30 15:53:20 +02:00
if (rtdCookie == nullptr) {
2022-08-29 13:32:49 +02:00
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);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
// TODO: Emit warning
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
}
*buffer = reinterpret_cast<uint8_t*>(rtdCookie->exchangeBuf.data());
2022-05-14 09:41:28 +02:00
*size = serLen;
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
ReturnValue_t Max31865RtdReader::writeCfgReg(SpiCookie* cookie, uint8_t cfg) {
using namespace MAX31865;
2022-05-14 09:41:28 +02:00
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);
}
2022-05-12 11:27:30 +02:00
ReturnValue_t Max31865RtdReader::clearFaultStatus(SpiCookie* cookie) {
using namespace MAX31865;
2022-05-12 11:27:30 +02:00
// Read back the current configuration to avoid overwriting it when clearing te fault status
uint8_t currentCfg = 0;
auto result = readCfgReg(cookie, currentCfg);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-05-12 11:27:30 +02:00
return result;
}
// Clear bytes 5, 3 and 2 which need to be 0
currentCfg &= ~0x2C;
2022-05-14 11:34:25 +02:00
currentCfg |= (1 << CfgBitPos::FAULT_STATUS_CLEAR);
2022-05-12 11:27:30 +02:00
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);
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
2022-05-12 11:27:30 +02:00
cfg = replyPtr[0];
}
return result;
}
ReturnValue_t Max31865RtdReader::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 Max31865RtdReader::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 Max31865RtdReader::readLowThreshold(SpiCookie* cookie, uint16_t& lowThreshold) {
using namespace MAX31865;
uint8_t* replyPtr = nullptr;
auto result = readNFromReg(cookie, LOW_THRESHOLD, 2, &replyPtr);
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
2022-05-12 11:27:30 +02:00
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);
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
2022-05-12 11:27:30 +02:00
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) {
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
2022-05-12 11:27:30 +02:00
}
cmdBuf[0] = reg | WRITE_BIT;
for (size_t idx = 0; idx < n; idx++) {
cmdBuf[idx + 1] = cmd[idx];
}
2022-05-14 09:41:28 +02:00
return comIF->sendMessage(cookie, cmdBuf.data(), n + 1);
2022-05-12 11:27:30 +02:00
}
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);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
return result;
}
2022-05-12 11:27:30 +02:00
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;
}
2022-05-12 11:27:30 +02:00
ReturnValue_t Max31865RtdReader::readNFromReg(SpiCookie* cookie, uint8_t reg, size_t n,
uint8_t** reply) {
using namespace MAX31865;
2022-05-12 11:27:30 +02:00
if (n > 4) {
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
2022-05-12 11:27:30 +02:00
}
// Clear write bit in any case
reg &= ~WRITE_BIT;
cmdBuf[0] = reg;
std::memset(cmdBuf.data() + 1, 0, n);
2022-05-14 09:41:28 +02:00
ReturnValue_t result = comIF->sendMessage(cookie, cmdBuf.data(), n + 1);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
return returnvalue::FAILED;
}
2022-05-14 09:41:28 +02:00
size_t dummyLen = 0;
2022-05-14 09:41:28 +02:00
uint8_t* replyPtr = nullptr;
result = comIF->readReceivedMessage(cookie, &replyPtr, &dummyLen);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
return result;
}
2022-05-12 11:27:30 +02:00
if (reply != nullptr) {
2022-05-14 09:41:28 +02:00
*reply = replyPtr + 1;
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
2022-05-12 11:27:30 +02:00
}
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;
}
2022-05-14 11:34:25 +02:00
ReturnValue_t Max31865RtdReader::initialize() {
csLock = comIF->getCsMutex();
return SystemObject::initialize();
}