/************************************************************************** * * Copyright (c) 2026-2026 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file PinchValve.c * * @author (last) Varshini Nagabooshanam * @date (last) 08-Jul-2026 * * @author (original) Varshini Nagabooshanam * @date (original) 08-Jul-2026 * ***************************************************************************/ #include "PinchValve.h" #include "RotaryValve.h" #include "FpgaTD.h" #include "Timers.h" #include "Utilities.h" /** * @addtogroup PinchValve * @{ */ // ********** private definitions ********** #define PINCH_VALVE_FPGA_CMD_START_BIT 0x01 #define PINCH_VALVE_FPGA_CMD_WRITE_ONLY_BIT 0x02 #define PINCH_VALVE_FPGA_CMD_TX_WORDS_SHIFT 2 #define PINCH_VALVE_FPGA_CMD_RX_WORDS_SHIFT 5 #define PINCH_VALVE_FPGA_CMD_WORD_COUNT_MASK 0x03 #define PINCH_VALVE_SPI_CMD_DONE_MASK 0x01 #define PINCH_VALVE_ENABLE_BIT 0x01 #define PINCH_VALVE_RESET_BIT 0x02 #define PINCH_VALVE_EXECUTE_DELAY_MS 10 #define PINCH_VALVE_NO_WORDS 0 #define PINCH_VALVE_ONE_WORD 1 #define PINCH_VALVE_TWO_WORDS 2 #define PINCH_VALVE_THREE_WORDS 3 #define PINCH_VALVE_WRITE_READ_CMD FALSE #define PINCH_VALVE_WRITE_ONLY_CMD TRUE /// Pinch valve driver state machine states. typedef enum { PINCH_VALVE_IDLE_STATE = 0, ///< Waiting for a new pinch valve command. PINCH_VALVE_EXECUTE_STATE ///< Executing the active pinch valve command. } PINCH_VALVE_STATE_T; /// Pinch valve execute state machine steps. typedef enum { PINCH_VALVE_EXECUTE_STEP_WRITE_COMMAND = 0, ///< Write the PMD command to the FPGA. PINCH_VALVE_EXECUTE_STEP_WAIT_FOR_FPGA, ///< Wait for the FPGA to complete the command transaction. PINCH_VALVE_EXECUTE_STEP_READ_OUTPUT, ///< Read the command response from the FPGA. PINCH_VALVE_EXECUTE_STEP_GET_EVENT_STATUS, ///< Read the PMD event status. PINCH_VALVE_EXECUTE_STEP_GET_ACTIVITY_STATUS, ///< Read the PMD activity status. PINCH_VALVE_EXECUTE_STEP_GET_ACTUAL_POSITION, ///< Read the PMD actual position. PINCH_VALVE_EXECUTE_STEP_COMPLETE ///< Complete the current command and return to idle. } PINCH_VALVE_EXECUTE_STEP_T; /// Supported pinch valve command types. typedef enum { PINCH_VALVE_CMD_TYPE_NONE = 0, ///< No active command. PINCH_VALVE_CMD_TYPE_HOME, ///< Home the pinch valve. PINCH_VALVE_CMD_TYPE_POSITION, ///< Move the pinch valve to the requested position. PINCH_VALVE_CMD_TYPE_READ_STATUS ///< Read the pinch valve status. } PINCH_VALVE_CMD_TYPE_T; /// Pinch valve command execution results. typedef enum { PINCH_VALVE_CMD_RESULT_IDLE = 0, ///< No command is active. PINCH_VALVE_CMD_RESULT_BUSY, ///< Command is currently executing. PINCH_VALVE_CMD_RESULT_COMPLETE, ///< Command completed successfully. PINCH_VALVE_CMD_RESULT_ERROR ///< Command completed with an error. } PINCH_VALVE_CMD_RESULT_T; /// Pinch valve command information. typedef struct { VALVE_T valve; ///< Target rotary pinch valve (H1 or H19). PINCH_VALVE_CMD_TYPE_T cmdType; ///< Command type. U16 cmdHeader; ///< PMD command header. U16 inputWord1; ///< PMD command input word 1. U16 inputWord2; ///< PMD command input word 2. U16 inputWord3; ///< PMD command input word 3. U08 txWordCount; ///< Number of words transmitted to the PMD controller. U08 rxWordCount; ///< Number of words expected from the PMD controller. BOOL writeOnly; ///< TRUE if the command is write only. S32 targetPosition; ///< Requested target position. } PINCH_VALVE_COMMAND_T; /// Pinch valve command response. typedef struct { U16 outputWord1; ///< PMD output word 1. U16 outputWord2; ///< PMD output word 2. U16 outputWord3; ///< PMD output word 3. U08 spiCmdStatus; ///< FPGA SPI command status. U08 readCount; ///< FPGA read counter. U08 errorCount; ///< FPGA communication error counter. } PINCH_VALVE_RESPONSE_T; // ********** private data ********** static PINCH_VALVE_STATE_T pinchValveState; static PINCH_VALVE_EXECUTE_STEP_T executeStep; static PINCH_VALVE_COMMAND_T activeCommand; static PINCH_VALVE_RESPONSE_T lastResponse; static PINCH_VALVE_CMD_RESULT_T commandResult; static BOOL commandPending; static U32 executeStartTime; // ********** private function prototypes ********** static BOOL startPinchValveCommand( PINCH_VALVE_COMMAND_T command ); static void execIdleState( void ); static void execExecuteState( void ); static void writeCommandToFpga( const PINCH_VALVE_COMMAND_T *command ); static void readFpgaResponse( VALVE_T valve, PINCH_VALVE_RESPONSE_T *response ); static void sendStatusReadCommand( VALVE_T valve, U16 commandHeader ); static BOOL isFpgaCommandDone( VALVE_T valve ); static U08 buildFpgaCommandByte( U08 txWordCount, U08 rxWordCount, BOOL writeOnly ); static void setValveEnableReset( VALVE_T valve, U08 enableReset ); static void transitionToIdle( PINCH_VALVE_CMD_RESULT_T result ); /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ void initPinchValve( void ) { pinchValveState = PINCH_VALVE_IDLE_STATE; executeStep = PINCH_VALVE_EXECUTE_STEP_WRITE_COMMAND; commandResult = PINCH_VALVE_CMD_RESULT_IDLE; commandPending = FALSE; activeCommand.valve = PINCH_VALVE_H1; activeCommand.cmdType = PINCH_VALVE_CMD_TYPE_NONE; activeCommand.cmdHeader = 0; activeCommand.inputWord1 = 0U; activeCommand.inputWord2 = 0U; activeCommand.inputWord3 = 0U; activeCommand.txWordCount = 0U; activeCommand.rxWordCount = 0U; activeCommand.writeOnly = FALSE; activeCommand.targetPosition = 0; lastResponse.outputWord1 = 0U; lastResponse.outputWord2 = 0U; lastResponse.outputWord3 = 0U; lastResponse.spiCmdStatus = 0U; lastResponse.readCount = 0U; lastResponse.errorCount = 0U; setValveEnableReset( PINCH_VALVE_H1, 0U ); setValveEnableReset( PINCH_VALVE_H19, 0U ); } /*********************************************************************//** * @brief Executes the Magellan pinch valve state machine. * @details \b Inputs: commandPending, pinchValveState * @details \b Outputs: active command processing and response data * @return none *************************************************************************/ void execPinchValve( void ) { switch ( pinchValveState ) { case PINCH_VALVE_IDLE_STATE: execIdleState(); break; case PINCH_VALVE_EXECUTE_STATE: execExecuteState(); break; default: transitionToIdle( PINCH_VALVE_CMD_RESULT_ERROR ); break; } } /*********************************************************************//** * @brief Starts a pinch valve home command. * @param valve Pinch valve identifier. * @return TRUE when command was accepted, otherwise FALSE. *************************************************************************/ BOOL startPinchValveHome( VALVE_T valve ) { PINCH_VALVE_COMMAND_T command; command.valve = valve; command.cmdType = PINCH_VALVE_CMD_TYPE_HOME; command.cmdHeader = PINCH_VALVE_PMD_CMD_SET_OPERATING_MODE; command.inputWord1 = 0x0037U; command.inputWord2 = 0; command.inputWord3 = 0; command.txWordCount = PINCH_VALVE_ONE_WORD; command.rxWordCount = PINCH_VALVE_NO_WORDS; command.writeOnly = PINCH_VALVE_WRITE_ONLY_CMD; command.targetPosition = 0; return startPinchValveCommand( command ); } /*********************************************************************//** * @brief Starts a pinch valve move command. * @param valve Pinch valve identifier. * @param targetPosition Target motor position in counts. * @return TRUE when command was accepted, otherwise FALSE. *************************************************************************/ BOOL startPinchValveMove( VALVE_T valve, S32 targetPosition ) { PINCH_VALVE_COMMAND_T command; command.valve = valve; command.cmdType = PINCH_VALVE_CMD_TYPE_POSITION; command.cmdHeader = PINCH_VALVE_PMD_CMD_SET_POSITION; command.inputWord1 = (U16)( targetPosition & 0x0000FFFFL ); command.inputWord2 = (U16)( ( targetPosition >> 16 ) & 0x0000FFFFL ); command.inputWord3 = 0; command.txWordCount = PINCH_VALVE_TWO_WORDS; command.rxWordCount = PINCH_VALVE_NO_WORDS; command.writeOnly = PINCH_VALVE_WRITE_ONLY_CMD; command.targetPosition = targetPosition; return startPinchValveCommand( command ); } /*********************************************************************//** * @brief Starts a status read command for the selected pinch valve. * @param valve Pinch valve identifier. * @return TRUE when command was accepted, otherwise FALSE. *************************************************************************/ BOOL startPinchValveReadStatus( VALVE_T valve ) { PINCH_VALVE_COMMAND_T command; command.valve = valve; command.cmdType = PINCH_VALVE_CMD_TYPE_READ_STATUS; command.cmdHeader = PINCH_VALVE_PMD_CMD_GET_EVENT_STATUS; command.inputWord1 = 0U; command.inputWord2 = 0U; command.inputWord3 = 0U; command.txWordCount = PINCH_VALVE_NO_WORDS; command.rxWordCount = PINCH_VALVE_ONE_WORD; command.writeOnly = PINCH_VALVE_WRITE_READ_CMD; command.targetPosition = 0; return startPinchValveCommand( command ); } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ PINCH_VALVE_STATE_T getPinchValveState( void ) { return pinchValveState; } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ PINCH_VALVE_CMD_RESULT_T getPinchValveCommandResult( void ) { return commandResult; } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ void getPinchValveResponse( PINCH_VALVE_RESPONSE_T *response ) { if ( response != NULL ) { *response = lastResponse; } } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static BOOL startPinchValveCommand( PINCH_VALVE_COMMAND_T command ) { BOOL accepted = FALSE; if ( ( pinchValveState == PINCH_VALVE_IDLE_STATE ) && ( commandPending == FALSE ) ) { activeCommand = command; commandPending = TRUE; commandResult = PINCH_VALVE_CMD_RESULT_BUSY; accepted = TRUE; } return accepted; } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void execIdleState( void ) { if ( commandPending == TRUE ) { executeStep = PINCH_VALVE_EXECUTE_STEP_WRITE_COMMAND; pinchValveState = PINCH_VALVE_EXECUTE_STATE; } } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void execExecuteState( void ) { switch ( executeStep ) { case PINCH_VALVE_EXECUTE_STEP_WRITE_COMMAND: { // Step 1: write command header, input words, and FPGA command. writeCommandToFpga( &activeCommand ); executeStartTime = getMSTimerCount(); executeStep = PINCH_VALVE_EXECUTE_STEP_WAIT_FOR_FPGA; break; } case PINCH_VALVE_EXECUTE_STEP_WAIT_FOR_FPGA: { // Step 2: wait 10 ms before reading FPGA response. if ( didTimeout( executeStartTime, PINCH_VALVE_EXECUTE_DELAY_MS ) == TRUE ) { executeStep = PINCH_VALVE_EXECUTE_STEP_READ_OUTPUT; } break; } case PINCH_VALVE_EXECUTE_STEP_READ_OUTPUT: { //Step 3: read SPI status and output words. readFpgaResponse( activeCommand.valve, &lastResponse ); if ( isFpgaCommandDone( activeCommand.valve ) == TRUE ) { executeStep = PINCH_VALVE_EXECUTE_STEP_GET_EVENT_STATUS; } else { // FPGA is still busy. Wait before checking again. executeStartTime = getMSTimerCount(); executeStep = PINCH_VALVE_EXECUTE_STEP_WAIT_FOR_FPGA; } break; } case PINCH_VALVE_EXECUTE_STEP_GET_EVENT_STATUS: { // Step 4: request PMD event status. sendStatusReadCommand( activeCommand.valve, PINCH_VALVE_PMD_CMD_GET_EVENT_STATUS ); executeStartTime = getMSTimerCount(); executeStep = PINCH_VALVE_EXECUTE_STEP_GET_ACTIVITY_STATUS; break; } case PINCH_VALVE_EXECUTE_STEP_GET_ACTIVITY_STATUS: { // Step 5: request PMD activity status. if ( didTimeout( executeStartTime, PINCH_VALVE_EXECUTE_DELAY_MS ) == TRUE ) { readFpgaResponse( activeCommand.valve, &lastResponse ); sendStatusReadCommand( activeCommand.valve, PINCH_VALVE_PMD_CMD_GET_ACTIVITY_STATUS ); executeStartTime = getMSTimerCount(); executeStep = PINCH_VALVE_EXECUTE_STEP_GET_ACTUAL_POSITION; } break; } case PINCH_VALVE_EXECUTE_STEP_GET_ACTUAL_POSITION: { // Step 6: request actual position and read final output. if ( didTimeout( executeStartTime, PINCH_VALVE_EXECUTE_DELAY_MS ) == TRUE ) { readFpgaResponse( activeCommand.valve, &lastResponse ); sendStatusReadCommand( activeCommand.valve, PINCH_VALVE_PMD_CMD_GET_ACTUAL_POSITION ); executeStartTime = getMSTimerCount(); executeStep = PINCH_VALVE_EXECUTE_STEP_COMPLETE; } break; } case PINCH_VALVE_EXECUTE_STEP_COMPLETE: { // Step 7: read final response and return to Idle. if ( didTimeout( executeStartTime, PINCH_VALVE_EXECUTE_DELAY_MS ) == TRUE ) { readFpgaResponse( activeCommand.valve, &lastResponse ); transitionToIdle( PINCH_VALVE_CMD_RESULT_COMPLETE ); } break; } default: { transitionToIdle( PINCH_VALVE_CMD_RESULT_ERROR ); break; } } } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void writeCommandToFpga( const PINCH_VALVE_COMMAND_T *command ) { U08 fpgaCmd; fpgaCmd = buildFpgaCommandByte( command->txWordCount, command->rxWordCount, command->writeOnly ); if ( command->valve == PINCH_VALVE_H1 ) { setH1CmdHeader( command->cmdHeader ); setH1InputWord1( command->inputWord1 ); setH1InputWord2( command->inputWord2 ); setH1InputWord3( command->inputWord3 ); setH1FPGACmd( fpgaCmd ); } else { setH19CmdHeader( command->cmdHeader ); setH19InputWord1( command->inputWord1 ); setH19InputWord2( command->inputWord2 ); setH19InputWord3( command->inputWord3 ); setH19FPGACmd( fpgaCmd ); } } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void readFpgaResponse( VALVE_T valve, PINCH_VALVE_RESPONSE_T *response ) { if ( valve == PINCH_VALVE_H1 ) { response->spiCmdStatus = getH1SPICmdStatus(); response->readCount = getH1ReadCount(); response->errorCount = getH1ErrorCount(); response->outputWord1 = getH1OutputWord1(); response->outputWord2 = getH1OutputWord2(); response->outputWord3 = getH1OutputWord3(); } else { response->spiCmdStatus = getH19SPICmdStatus(); response->readCount = getH19ReadCount(); response->errorCount = getH19ErrorCount(); response->outputWord1 = getH19OutputWord1(); response->outputWord2 = getH19OutputWord2(); response->outputWord3 = getH19OutputWord3(); } } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void sendStatusReadCommand( VALVE_T valve, U16 commandHeader ) { PINCH_VALVE_COMMAND_T statusCommand; statusCommand.valve = valve; statusCommand.cmdType = PINCH_VALVE_CMD_TYPE_READ_STATUS; statusCommand.cmdHeader = commandHeader; statusCommand.inputWord1 = 0U; statusCommand.inputWord2 = 0U; statusCommand.inputWord3 = 0U; statusCommand.txWordCount = PINCH_VALVE_NO_WORDS; statusCommand.rxWordCount = PINCH_VALVE_ONE_WORD; statusCommand.writeOnly = PINCH_VALVE_WRITE_READ_CMD; statusCommand.targetPosition = 0; writeCommandToFpga( &statusCommand ); } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static BOOL isFpgaCommandDone( VALVE_T valve ) { U08 status; if ( valve == PINCH_VALVE_H1 ) { status = getH1SPICmdStatus(); } else { status = getH19SPICmdStatus(); } return ( ( status & PINCH_VALVE_SPI_CMD_DONE_MASK ) != 0U ); } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static U08 buildFpgaCommandByte( U08 txWordCount, U08 rxWordCount, BOOL writeOnly ) { U08 fpgaCmd = PINCH_VALVE_FPGA_CMD_START_BIT; txWordCount &= PINCH_VALVE_FPGA_CMD_WORD_COUNT_MASK; rxWordCount &= PINCH_VALVE_FPGA_CMD_WORD_COUNT_MASK; if ( writeOnly == TRUE ) { fpgaCmd |= PINCH_VALVE_FPGA_CMD_WRITE_ONLY_BIT; } fpgaCmd |= (U08)( txWordCount << PINCH_VALVE_FPGA_CMD_TX_WORDS_SHIFT ); fpgaCmd |= (U08)( rxWordCount << PINCH_VALVE_FPGA_CMD_RX_WORDS_SHIFT ); return fpgaCmd; } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void setValveEnableReset( VALVE_T valve, U08 enableReset ) { if ( valve == PINCH_VALVE_H1 ) { setH1EnableReset( enableReset ); } else { setH19EnableReset( enableReset ); } } /*********************************************************************//** * @brief Initializes the Magellan pinch valve driver. * @details \b Inputs: none * @details \b Outputs: Pinch valve private state data initialized. * @return none *************************************************************************/ static void transitionToIdle( PINCH_VALVE_CMD_RESULT_T result ) { commandPending = FALSE; commandResult = result; executeStep = PINCH_VALVE_EXECUTE_STEP_WRITE_COMMAND; activeCommand.cmdType = PINCH_VALVE_CMD_TYPE_NONE; pinchValveState = PINCH_VALVE_IDLE_STATE; } /** @} */