#include "PayloadPcduHandler.h" #include #ifdef FSFW_OSAL_LINUX #include #include #include #include #include #endif #include "devices/gpioIds.h" PayloadPcduHandler::PayloadPcduHandler(object_id_t objectId, object_id_t comIF, CookieIF* cookie, GpioIF* gpioIF, SdCardMountedIF* sdcMan, bool periodicPrintout) : DeviceHandlerBase(objectId, comIF, cookie), adcSet(this), periodicPrintout(periodicPrintout), gpioIF(gpioIF), sdcMan(sdcMan) {} void PayloadPcduHandler::doStartUp() { if ((state != States::PCDU_OFF) and (state != States::ON_TRANS_SSR)) { // Config error sif::error << "PayloadPcduHandler::doStartUp: Invalid state" << std::endl; } if (state == States::PCDU_OFF) { // Switch on relays here gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT0); gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT1); state = States::ON_TRANS_SSR; transitionOk = true; } if (state == States::ON_TRANS_SSR) { // If necessary, check whether a certain amount of time has elapsed if (transitionOk) { transitionOk = false; state = States::ON_TRANS_ADC_CLOSE_ZERO; // We are now in ON mode startTransition(MODE_NORMAL, 0); adcCountdown.setTimeout(50); adcCountdown.resetTimer(); adcState = AdcStates::BOOT_DELAY; // The ADC can now be read. If the values are not close to zero, we should not allow // transition monMode = MonitoringMode::CLOSE_TO_ZERO; } } } void PayloadPcduHandler::stateMachineToNormal() { using namespace plpcdu; if (adcState == AdcStates::BOOT_DELAY) { if (adcCountdown.hasTimedOut()) { adcState = AdcStates::SEND_SETUP; adcCmdExecuted = false; } } if (adcState == AdcStates::SEND_SETUP) { if (adcCmdExecuted) { adcState = AdcStates::NORMAL; setMode(MODE_NORMAL, NORMAL_ADC_ONLY); adcCountdown.setTimeout(100); adcCountdown.resetTimer(); adcCmdExecuted = false; } } if (submode == plpcdu::NORMAL_ALL_ON) { if (state == States::ON_TRANS_ADC_CLOSE_ZERO) { if (not commandExecuted) { float waitTime = SSR_TO_DRO_WAIT_TIME; params.getValue(PlPcduParameter::SSR_TO_DRO_WAIT_TIME_K, waitTime); countdown.setTimeout(std::round(waitTime * 1000)); countdown.resetTimer(); commandExecuted = true; // TODO: For now, skip ADC check transitionOk = true; } // ADC values are ok, 5 seconds have elapsed if (transitionOk and countdown.hasTimedOut()) { state = States::ON_TRANS_DRO; // Now start monitoring for negative voltages instead monMode = MonitoringMode::NEGATIVE; commandExecuted = false; transitionOk = false; } } if (state == States::ON_TRANS_DRO) { if (not commandExecuted) { float waitTime = DRO_TO_X8_WAIT_TIME; params.getValue(PlPcduParameter::DRO_TO_X8_WAIT_TIME_K, waitTime); countdown.setTimeout(std::round(waitTime * 1000)); countdown.resetTimer(); #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Enabling PL PCDU DRO module" << std::endl; #endif // Switch on DRO and start monitoring for negative voltages gpioIF->pullHigh(gpioIds::PLPCDU_ENB_DRO); adcCountdown.setTimeout(100); adcCountdown.resetTimer(); commandExecuted = true; } // ADC values are ok, 5 seconds have elapsed if (transitionOk and countdown.hasTimedOut()) { state = States::ON_TRANS_X8; commandExecuted = false; transitionOk = false; } } if (state == States::ON_TRANS_X8) { if (not commandExecuted) { float waitTime = X8_TO_TX_WAIT_TIME; params.getValue(PlPcduParameter::X8_TO_TX_WAIT_TIME_K, waitTime); countdown.setTimeout(std::round(waitTime * 1000)); countdown.resetTimer(); #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Enabling PL PCDU X8 module" << std::endl; #endif // Switch on X8 gpioIF->pullHigh(gpioIds::PLPCDU_ENB_X8); adcCountdown.setTimeout(100); adcCountdown.resetTimer(); commandExecuted = true; } // ADC values are ok, 5 seconds have elapsed if (transitionOk and countdown.hasTimedOut()) { state = States::ON_TRANS_TX; commandExecuted = false; transitionOk = false; } } if (state == States::ON_TRANS_TX) { if (not commandExecuted) { float waitTime = TX_TO_MPA_WAIT_TIME; params.getValue(PlPcduParameter::TX_TO_MPA_WAIT_TIME_K, waitTime); countdown.setTimeout(std::round(waitTime * 1000)); countdown.resetTimer(); #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Enabling PL PCDU TX module" << std::endl; #endif // Switch on TX gpioIF->pullHigh(gpioIds::PLPCDU_ENB_TX); // Wait for 100 ms before checking ADC values adcCountdown.setTimeout(100); adcCountdown.resetTimer(); commandExecuted = true; } // ADC values are ok, 5 seconds have elapsed if (transitionOk and countdown.hasTimedOut()) { state = States::ON_TRANS_MPA; commandExecuted = false; transitionOk = false; } } if (state == States::ON_TRANS_MPA) { if (not commandExecuted) { float waitTime = MPA_TO_HPA_WAIT_TIME; params.getValue(PlPcduParameter::MPA_TO_HPA_WAIT_TIME_K, waitTime); countdown.setTimeout(std::round(waitTime * 1000)); countdown.resetTimer(); #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Enabling PL PCDU MPA module" << std::endl; #endif // Switch on MPA gpioIF->pullHigh(gpioIds::PLPCDU_ENB_MPA); // Wait for 100 ms before checking ADC values adcCountdown.setTimeout(100); adcCountdown.resetTimer(); commandExecuted = true; } // ADC values are ok, 5 seconds have elapsed if (transitionOk and countdown.hasTimedOut()) { state = States::ON_TRANS_HPA; commandExecuted = false; transitionOk = false; } } if (state == States::ON_TRANS_HPA) { if (not commandExecuted) { // Switch on HPA gpioIF->pullHigh(gpioIds::PLPCDU_ENB_HPA); #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Enabling PL PCDU HPA module" << std::endl; #endif commandExecuted = true; } // ADC values are ok, 5 seconds have elapsed if (transitionOk and countdown.hasTimedOut()) { state = States::PCDU_ON; setMode(MODE_NORMAL, plpcdu::NORMAL_ALL_ON); countdown.resetTimer(); commandExecuted = false; transitionOk = false; } } } } void PayloadPcduHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) { if (mode == _MODE_TO_NORMAL) { stateMachineToNormal(); } } void PayloadPcduHandler::doShutDown() { transitionBackToOff(); } ReturnValue_t PayloadPcduHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { switch (adcState) { case (AdcStates::SEND_SETUP): { *id = plpcdu::SETUP_CMD; return buildCommandFromCommand(*id, nullptr, 0); } case (AdcStates::NORMAL): { *id = plpcdu::READ_WITH_TEMP_EXT; return buildCommandFromCommand(*id, nullptr, 0); } default: { break; } } return NOTHING_TO_SEND; } ReturnValue_t PayloadPcduHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { if (adcState == AdcStates::SEND_SETUP) { *id = plpcdu::SETUP_CMD; return buildCommandFromCommand(*id, nullptr, 0); } if (mode == _MODE_TO_NORMAL) { return buildNormalDeviceCommand(id); } return NOTHING_TO_SEND; } void PayloadPcduHandler::fillCommandAndReplyMap() { insertInCommandAndReplyMap(plpcdu::READ_CMD, 2, &adcSet); insertInCommandAndReplyMap(plpcdu::READ_TEMP_EXT, 1, &adcSet); insertInCommandAndReplyMap(plpcdu::READ_WITH_TEMP_EXT, 1, &adcSet); insertInCommandAndReplyMap(plpcdu::SETUP_CMD, 1); } ReturnValue_t PayloadPcduHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData, size_t commandDataLen) { switch (deviceCommand) { case (plpcdu::SETUP_CMD): { cmdBuf[0] = plpcdu::SETUP_BYTE; rawPacket = cmdBuf.data(); rawPacketLen = 1; break; } case (plpcdu::READ_CMD): { max1227::prepareExternallyClockedRead0ToN(cmdBuf.data(), plpcdu::CHANNEL_N, rawPacketLen); rawPacket = cmdBuf.data(); break; } case (plpcdu::READ_TEMP_EXT): { max1227::prepareExternallyClockedTemperatureRead(cmdBuf.data(), rawPacketLen); rawPacket = cmdBuf.data(); break; } case (plpcdu::READ_WITH_TEMP_EXT): { size_t sz = 0; max1227::prepareExternallyClockedRead0ToN(cmdBuf.data(), plpcdu::CHANNEL_N, sz); max1227::prepareExternallyClockedTemperatureRead(cmdBuf.data() + sz, sz); rawPacketLen = sz; rawPacket = cmdBuf.data(); break; } default: { return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; } } return RETURN_OK; } ReturnValue_t PayloadPcduHandler::scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId, size_t* foundLen) { // SPI is full duplex *foundId = getPendingCommand(); *foundLen = remainingSize; return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t PayloadPcduHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) { using namespace plpcdu; switch (id) { case (SETUP_CMD): { if (mode == _MODE_TO_NORMAL) { adcCmdExecuted = true; } break; } case (READ_TEMP_EXT): { uint8_t tempStartIdx = TEMP_REPLY_SIZE - 2; adcSet.tempC.value = max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]); break; } case (READ_CMD): { PoolReadGuard pg(&adcSet); if (pg.getReadResult() != HasReturnvaluesIF::RETURN_OK) { return pg.getReadResult(); } handleExtConvRead(packet); checkAdcValues(); adcSet.setValidity(true, true); handlePrintout(); break; } case (READ_WITH_TEMP_EXT): { PoolReadGuard pg(&adcSet); if (pg.getReadResult() != HasReturnvaluesIF::RETURN_OK) { return pg.getReadResult(); } handleExtConvRead(packet); uint8_t tempStartIdx = ADC_REPLY_SIZE + TEMP_REPLY_SIZE - 2; adcSet.tempC.value = max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]); checkAdcValues(); adcSet.setValidity(true, true); handlePrintout(); break; } default: { break; } } return HasReturnvaluesIF::RETURN_OK; } uint32_t PayloadPcduHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { // 20 minutes transition delay is allowed return 20 * 60 * 60; } ReturnValue_t PayloadPcduHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, LocalDataPoolManager& poolManager) { localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::CHANNEL_VEC, &channelValues); localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::PROCESSED_VEC, &processedValues); localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::TEMP, &tempC); poolManager.subscribeForPeriodicPacket(adcSet.getSid(), false, 0.1, true); return HasReturnvaluesIF::RETURN_OK; } void PayloadPcduHandler::setToGoToNormalModeImmediately(bool enable) { this->goToNormalMode = enable; } void PayloadPcduHandler::handleExtConvRead(const uint8_t* bufStart) { for (uint8_t idx = 0; idx < 12; idx++) { adcSet.channels[idx] = bufStart[idx * 2 + 1] << 8 | bufStart[idx * 2 + 2]; } } void PayloadPcduHandler::handlePrintout() { using namespace plpcdu; if (periodicPrintout) { if (opDivider.checkAndIncrement()) { sif::info << "PL PCDU ADC hex [" << std::setfill('0') << std::hex; for (uint8_t idx = 0; idx < 12; idx++) { sif::info << std::setw(3) << adcSet.channels[idx]; if (idx < 11) { sif::info << ","; } } sif::info << "] | T[C] " << std::dec << adcSet.tempC.value << std::endl; sif::info << "Neg V: " << adcSet.processed[U_NEG_V_FB] << std::endl; sif::info << "I HPA [mA]: " << adcSet.processed[I_HPA] << std::endl; sif::info << "U HPA [V]: " << adcSet.processed[U_HPA_DIV_6] << std::endl; sif::info << "I MPA [mA]: " << adcSet.processed[I_MPA] << std::endl; sif::info << "U MPA [V]: " << adcSet.processed[U_MPA_DIV_6] << std::endl; sif::info << "I TX [mA]: " << adcSet.processed[I_TX] << std::endl; sif::info << "U TX [V]: " << adcSet.processed[U_TX_DIV_6] << std::endl; sif::info << "I X8 [mA]: " << adcSet.processed[I_X8] << std::endl; sif::info << "U X8 [V]: " << adcSet.processed[U_X8_DIV_6] << std::endl; sif::info << "I DRO [mA]: " << adcSet.processed[I_DRO] << std::endl; sif::info << "U DRO [V]: " << adcSet.processed[U_DRO_DIV_6] << std::endl; } } } void PayloadPcduHandler::enablePeriodicPrintout(bool enable, uint8_t divider) { this->periodicPrintout = enable; opDivider.setDivider(divider); } void PayloadPcduHandler::transitionBackToOff() { States currentState = state; gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA); gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA); gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8); gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO); gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0); gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1); state = States::PCDU_OFF; adcState = AdcStates::OFF; setMode(MODE_OFF); // Notify FDIR triggerEvent(TRANSITION_BACK_TO_OFF, static_cast(currentState)); } void PayloadPcduHandler::checkAdcValues() { using namespace plpcdu; checkJsonFileInit(); adcSet.processed[U_BAT_DIV_6] = static_cast(adcSet.channels[0]) * VOLTAGE_DIV / MAX122X_BIT * MAX122X_VREF; adcSet.processed[U_NEG_V_FB] = V_POS - VOLTAGE_DIV_U_NEG * (V_POS - static_cast(adcSet.channels[1]) / MAX122X_BIT * MAX122X_VREF); adcSet.processed[I_HPA] = static_cast(adcSet.channels[2]) * SCALE_CURRENT_HPA * 1000.0; adcSet.processed[U_HPA_DIV_6] = static_cast(adcSet.channels[3]) * SCALE_VOLTAGE; adcSet.processed[I_MPA] = static_cast(adcSet.channels[4]) * SCALE_CURRENT_MPA * 1000.0; adcSet.processed[U_MPA_DIV_6] = static_cast(adcSet.channels[5]) * SCALE_VOLTAGE; adcSet.processed[I_TX] = static_cast(adcSet.channels[6]) * SCALE_CURRENT_TX * 1000.0; adcSet.processed[U_TX_DIV_6] = static_cast(adcSet.channels[7]) * SCALE_VOLTAGE; adcSet.processed[I_X8] = static_cast(adcSet.channels[8]) * SCALE_CURRENT_X8 * 1000.0; adcSet.processed[U_X8_DIV_6] = static_cast(adcSet.channels[9]) * SCALE_VOLTAGE; adcSet.processed[I_DRO] = static_cast(adcSet.channels[10]) * SCALE_CURRENT_DRO * 1000.0; adcSet.processed[U_DRO_DIV_6] = static_cast(adcSet.channels[11]) * SCALE_VOLTAGE; float lowerBound = 0.0; float upperBound = 0.0; bool adcTransition = false; adcTransition = state == States::ON_TRANS_DRO and adcCountdown.isBusy(); // Now check against voltage and current limits, depending on state if (state >= States::ON_TRANS_DRO and not adcTransition) { params.getValue(PlPcduParameter::NEG_V_LOWER_BOUND_K, lowerBound); params.getValue(PlPcduParameter::NEG_V_UPPER_BOUND_K, upperBound); if (not checkVoltage(adcSet.processed[U_NEG_V_FB], lowerBound, upperBound, NEG_V_OUT_OF_BOUNDS)) { return; } params.getValue(PlPcduParameter::DRO_U_LOWER_BOUND_K, lowerBound); params.getValue(PlPcduParameter::DRO_U_UPPER_BOUND_K, upperBound); if (not checkVoltage(adcSet.processed[U_DRO_DIV_6], lowerBound, upperBound, U_DRO_OUT_OF_BOUNDS)) { return; } params.getValue(PlPcduParameter::DRO_I_UPPER_BOUND_K, upperBound); if (not checkCurrent(adcSet.processed[I_DRO], upperBound, I_DRO_OUT_OF_BOUNDS)) { return; } } adcTransition = state == States::ON_TRANS_X8 and adcCountdown.isBusy(); if (state >= States::ON_TRANS_X8 and not adcTransition) { params.getValue(PlPcduParameter::X8_U_LOWER_BOUND_K, lowerBound); params.getValue(PlPcduParameter::X8_U_UPPER_BOUND_K, upperBound); if (not checkVoltage(adcSet.processed[U_X8_DIV_6], lowerBound, upperBound, U_X8_OUT_OF_BOUNDS)) { return; } params.getValue(PlPcduParameter::X8_I_UPPER_BOUND_K, upperBound); if (not checkCurrent(adcSet.processed[I_X8], upperBound, I_X8_OUT_OF_BOUNDS)) { return; } } adcTransition = state == States::ON_TRANS_TX and adcCountdown.isBusy(); if (state >= States::ON_TRANS_TX and not adcTransition) { params.getValue(PlPcduParameter::TX_U_LOWER_BOUND_K, lowerBound); params.getValue(PlPcduParameter::TX_U_UPPER_BOUND_K, upperBound); if (not checkVoltage(adcSet.processed[U_TX_DIV_6], lowerBound, upperBound, U_TX_OUT_OF_BOUNDS)) { return; } params.getValue(PlPcduParameter::TX_I_UPPER_BOUND_K, upperBound); if (not checkCurrent(adcSet.processed[I_TX], upperBound, I_TX_OUT_OF_BOUNDS)) { return; } } adcTransition = state == States::ON_TRANS_MPA and adcCountdown.isBusy(); if (state >= States::ON_TRANS_MPA and not adcTransition) { params.getValue(PlPcduParameter::MPA_U_LOWER_BOUND_K, lowerBound); params.getValue(PlPcduParameter::MPA_U_UPPER_BOUND_K, upperBound); if (not checkVoltage(adcSet.processed[U_MPA_DIV_6], lowerBound, upperBound, U_MPA_OUT_OF_BOUNDS)) { return; } params.getValue(PlPcduParameter::MPA_I_UPPER_BOUND_K, upperBound); if (not checkCurrent(adcSet.processed[I_MPA], upperBound, I_MPA_OUT_OF_BOUNDS)) { return; } } adcTransition = state == States::ON_TRANS_HPA and adcCountdown.isBusy(); if (state >= States::ON_TRANS_HPA and not adcTransition) { params.getValue(PlPcduParameter::HPA_U_LOWER_BOUND_K, lowerBound); params.getValue(PlPcduParameter::HPA_U_UPPER_BOUND_K, upperBound); if (not checkVoltage(adcSet.processed[U_HPA_DIV_6], lowerBound, upperBound, U_HPA_OUT_OF_BOUNDS)) { return; } params.getValue(PlPcduParameter::HPA_I_UPPER_BOUND_K, upperBound); if (not checkCurrent(adcSet.processed[I_HPA], upperBound, I_HPA_OUT_OF_BOUNDS)) { return; } } transitionOk = true; } void PayloadPcduHandler::checkJsonFileInit() { if (not jsonFileInitComplete) { sd::SdCard prefSd; sdcMan->getPreferredSdCard(prefSd); if (sdcMan->isSdCardMounted(prefSd)) { params.initialize(sdcMan->getCurrentMountPrefix(prefSd)); jsonFileInitComplete = true; } } } bool PayloadPcduHandler::checkVoltage(float val, float lowerBound, float upperBound, Event event) { bool tooLarge = false; if (val < lowerBound or val > upperBound) { if (val > upperBound) { tooLarge = true; } else { tooLarge = false; } uint32_t p2 = 0; serializeFloat(p2, val); triggerEvent(event, tooLarge, p2); transitionOk = false; transitionBackToOff(); return false; } return true; } bool PayloadPcduHandler::checkCurrent(float val, float upperBound, Event event) { if (val > upperBound) { uint32_t p2 = 0; serializeFloat(p2, val); triggerEvent(event, true, p2); transitionOk = false; transitionBackToOff(); return false; } return true; } ReturnValue_t PayloadPcduHandler::isModeCombinationValid(Mode_t mode, Submode_t submode) { if (mode == MODE_NORMAL and submode <= 1) { return HasReturnvaluesIF::RETURN_OK; } return DeviceHandlerBase::isModeCombinationValid(mode, submode); } ReturnValue_t PayloadPcduHandler::serializeFloat(uint32_t& param, float val) { size_t dummy = 0; return SerializeAdapter::serialize(&val, reinterpret_cast(¶m), &dummy, 4, SerializeIF::Endianness::NETWORK); } #ifdef FSFW_OSAL_LINUX ReturnValue_t PayloadPcduHandler::extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie, const uint8_t* sendData, size_t sendLen, void* args) { auto handler = reinterpret_cast(args); if (handler == nullptr) { sif::error << "GyroADIS16507Handler::spiSendCallback: Passed handler pointer is invalid!" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } DeviceCommandId_t currentCommand = handler->getPendingCommand(); switch (currentCommand) { case (plpcdu::READ_WITH_TEMP_EXT): { return transferAsTwo(comIf, cookie, sendData, sendLen, false); } case (plpcdu::READ_TEMP_EXT): { return transferAsTwo(comIf, cookie, sendData, sendLen, true); } default: { return comIf->performRegularSendOperation(cookie, sendData, sendLen); } } return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t PayloadPcduHandler::transferAsTwo(SpiComIF* comIf, SpiCookie* cookie, const uint8_t* sendData, size_t sendLen, bool tempOnly) { ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; int retval = 0; // Prepare transfer int fileDescriptor = 0; std::string device = cookie->getSpiDevice(); UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage"); if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) { return SpiComIF::OPENING_FILE_FAILED; } spi::SpiModes spiMode = spi::SpiModes::MODE_0; uint32_t spiSpeed = 0; cookie->getSpiParameters(spiMode, spiSpeed, nullptr); comIf->setSpiSpeedAndMode(fileDescriptor, spiMode, spiSpeed); cookie->assignWriteBuffer(sendData); size_t transferLen = plpcdu::TEMP_REPLY_SIZE; if (not tempOnly) { transferLen += plpcdu::ADC_REPLY_SIZE; } cookie->setTransferSize(transferLen); gpioId_t gpioId = cookie->getChipSelectPin(); GpioIF* gpioIF = comIf->getGpioInterface(); MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; uint32_t timeoutMs = 0; MutexIF* mutex = comIf->getMutex(&timeoutType, &timeoutMs); if (mutex == nullptr or gpioIF == nullptr) { #if OBSW_VERBOSE_LEVEL >= 1 sif::warning << "GyroADIS16507Handler::spiSendCallback: " "Mutex or GPIO interface invalid" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; #endif } if (gpioId != gpio::NO_GPIO) { result = mutex->lockMutex(timeoutType, timeoutMs); if (result != RETURN_OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "SpiComIF::sendMessage: Failed to lock mutex" << std::endl; #endif return result; } } spi_ioc_transfer* transferStruct = cookie->getTransferStructHandle(); uint64_t origTx = transferStruct->tx_buf; uint64_t origRx = transferStruct->rx_buf; if (tempOnly) { transferLen = 1; } else { transferLen = plpcdu::ADC_REPLY_SIZE + 1; } transferStruct->len = transferLen; // Pull SPI CS low. For now, no support for active high given if (gpioId != gpio::NO_GPIO) { gpioIF->pullLow(gpioId); } // Execute transfer // Initiate a full duplex SPI transfer. retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie->getTransferStructHandle()); if (retval < 0) { utility::handleIoctlError("SpiComIF::sendMessage: ioctl error."); result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED; } #if FSFW_HAL_SPI_WIRETAPPING == 1 comIf->performSpiWiretapping(cookie); #endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */ if (gpioId != gpio::NO_GPIO) { gpioIF->pullHigh(gpioId); } transferStruct->tx_buf += transferLen; transferStruct->rx_buf += transferLen; transferStruct->len = plpcdu::TEMP_REPLY_SIZE - 1; if (gpioId != gpio::NO_GPIO) { gpioIF->pullLow(gpioId); } // Execute transfer // Initiate a full duplex SPI transfer. retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie->getTransferStructHandle()); if (retval < 0) { utility::handleIoctlError("SpiComIF::sendMessage: ioctl error."); result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED; } #if FSFW_HAL_SPI_WIRETAPPING == 1 comIf->performSpiWiretapping(cookie); #endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */ if (gpioId != gpio::NO_GPIO) { gpioIF->pullHigh(gpioId); } transferStruct->tx_buf = origTx; transferStruct->rx_buf = origRx; if (gpioId != gpio::NO_GPIO) { mutex->unlockMutex(); } return HasReturnvaluesIF::RETURN_OK; } #endif