/************************************************************************** * * Copyright (c) 2019-2019 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 SystemComm.c * * @date 08-Oct-2019 * @author S. Nash * * @brief SystemComm service module. Provides system message communication \n * functionality. Messages can be queued for transmission. Incoming messages \n * are processed. * **************************************************************************/ #include "can.h" #include "Common.h" #include "Buttons.h" #include "MsgQueues.h" #include "SystemCommMessages.h" #include "SystemComm.h" // ***************************** TEST CODE ****************************** // TODO - remove later #if 1 #include "CPLD.h" #endif // ************************** END TEST CODE ****************************** // ********** private definitions ********** #define NUM_OF_CAN_OUT_BUFFERS 4 // # of CAN buffers for transmit #define NUM_OF_CAN_IN_BUFFERS 6 // # of CAN buffers for receiving // ********** private data ********** const COMM_BUFFER_T CAN_OUT_BUFFERS[NUM_OF_CAN_OUT_BUFFERS] = { COMM_BUFFER_OUT_CAN_HD_ALARM, COMM_BUFFER_OUT_CAN_HD_2_DG, COMM_BUFFER_OUT_CAN_HD_2_UI, COMM_BUFFER_OUT_CAN_HD_BROADCAST }; const COMM_BUFFER_T CAN_IN_BUFFERS[NUM_OF_CAN_IN_BUFFERS] = { COMM_BUFFER_IN_CAN_DG_ALARM, COMM_BUFFER_IN_CAN_UI_ALARM, COMM_BUFFER_IN_CAN_DG_2_HD, COMM_BUFFER_IN_CAN_DG_BROADCAST, COMM_BUFFER_IN_CAN_UI_2_HD, COMM_BUFFER_IN_CAN_UI_BROADCAST }; // ********** private function prototypes ********** static COMM_BUFFER_T findNextHighestPriorityCANPacketToTransmit( void ); static void transmitNextCANPacket( void ); static void transmitPendingUARTData( void ); static void processIncomingData( void ); static U32 parseMessageFromBuffer( U08 *data, U32 len ); static void consumeBufferPaddingBeforeSync( COMM_BUFFER_T buffer ); static void processReceivedMessages( void ); static void processReceivedMessage( MESSAGE_T *message ); /************************************************************************* * @brief initSystemComm * The initSystemComm function initializes the SystemComm module. * @details * Inputs : none * Outputs : SystemComm module initialized. * @param none * @return none *************************************************************************/ void initSystemComm( void ) { // currently nothing to initialize } /************************************************************************* * @brief execSystemCommRx * The execSystemCommRx function manages received data from other sub-systems. * @details * Inputs : none * Outputs : none * @param none * @return none *************************************************************************/ void execSystemCommRx( void ) { // parse messages from comm buffers and queue them processIncomingData(); // process received messages in the queue processReceivedMessages(); } /************************************************************************* * @brief execSystemCommTx * The execSystemCommTx function manages data to be transmitted to other \n * sub-systems. * @details * Inputs : none * Outputs : none * @param none * @return none *************************************************************************/ void execSystemCommTx( void ) { // TODO - check to see if CAN transmitter is idle first if ( 1 ) // for now, assume it's idle { transmitNextCANPacket(); } // TODO - check to see if UART transmitter is idle first if ( 1 ) // for now, assume it's idle { transmitPendingUARTData(); } } /************************************************************************* ********************** TRANSMIT SUPPORT FUNCTIONS ************************ *************************************************************************/ /************************************************************************* * @brief handleCANXmitCompleteInt * The handleCANXmitCompleteInt function handles a CAN Tx complete interrupt. \n * A search for the next highest priority pack to transmit is made and a \n * new packet transmit is initiated on the appropriate CAN message box if \n * a CAN packet is found. * @details * Inputs : Comm buffers * Outputs : new CAN packet transmission is initiated * @param none * @return none *************************************************************************/ void handleCANXmitCompleteInt( void ) { transmitNextCANPacket(); } /************************************************************************* * @brief findNextHighestPriorityCANPacketToTransmit * The findNextHighestPriorityCANPacketToTransmit function gets the next \n * 8 byte packet and initiates a CAN transmit on the appropriate CAN channel. \n * @details * Inputs : Output CAN Comm Buffer(s) * Outputs : none * @param msg : none * @return buffer with highest priority CAN packet to transmit, \n * COMM_BUFFER_NOT_USED_1 if not CAN packets pending transmit found *************************************************************************/ static COMM_BUFFER_T findNextHighestPriorityCANPacketToTransmit( void ) { COMM_BUFFER_T result = COMM_BUFFER_NOT_USED_1; U32 i; // search for next priority CAN packet to transmit for ( i = 0; i < NUM_OF_CAN_OUT_BUFFERS; i++ ) { if ( numberOfBytesInCommBuffer( CAN_OUT_BUFFERS[i] ) >= CAN_MESSAGE_CARGO_SIZE ) { result = CAN_OUT_BUFFERS[i]; break; // found highest priority packet to transmit - we're done } } return result; } /************************************************************************* * @brief transmitNextCANPacket * The transmitNextCANPacket function gets the next 8 byte packet and initiates \n * a CAN transmit on the appropriate CAN channel. * @details * Inputs : Output CAN Comm Buffers * Outputs : CAN packet transmit initiated. * @param msg : none * @return none *************************************************************************/ static void transmitNextCANPacket( void ) { COMM_BUFFER_T buffer = findNextHighestPriorityCANPacketToTransmit(); // if a buffer is found with a packet to transmit, get packet from buffer and transmit it if ( buffer != COMM_BUFFER_NOT_USED_1 ) { U08 data[CAN_MESSAGE_CARGO_SIZE]; U32 dataSize = getFromCommBuffer( buffer, data, CAN_MESSAGE_CARGO_SIZE ); CAN_MESSAGE_BOX_T mBox = buffer; // CAN message boxes and comm buffers are aligned if ( dataSize == CAN_MESSAGE_CARGO_SIZE ) { canTransmit( canREG1, mBox, data ); } } } /************************************************************************* * @brief transmitPendingUARTData * The transmitPendingUARTData function sets up and initiates a DMA transmit \n * of any data currently pending transmit via UART. * @details * Inputs : Output UART Comm Buffer(s) * Outputs : UART DMA transmit initiated. * @param msg : none * @return none *************************************************************************/ static void transmitPendingUARTData( void ) { // TODO - implement } /************************************************************************* ********************** RECEIVE SUPPORT FUNCTIONS ************************* *************************************************************************/ /************************************************************************* * @brief handleCANPacketReceivedInt * The handleCANPacketReceivedInt function handles a CAN Rx interrupt. \n * A new CAN packet is added to the appropriate comm buffer based on the \n * message box that it came in on. * @details * Inputs : none * Outputs : new CAN packet added to associated comm buffer * @param srcCANBox : CAN message box that the packet arrived in * @return none *************************************************************************/ void handleCANPacketReceivedInt( CAN_MESSAGE_BOX_T srcCANBox ) { U08 data[CAN_MESSAGE_CARGO_SIZE]; // TODO - get received packets from any/all CAN message boxes that have a new packet if ( FALSE != canIsRxMessageArrived( canREG1, srcCANBox ) ) // TODO - is this even necessary? when interrupt is calling, don't we already know it's arrived? { canGetData( canREG1, srcCANBox, data ); // TODO - select appropriate comm buffer based on the message box it came in on (s/b same #) addToCommBuffer( srcCANBox, data, CAN_MESSAGE_CARGO_SIZE ); } } /************************************************************************* * @brief processIncomingData * The processIncomingData function parses out messages from the Input \n * Comm Buffers and adds them to the Received Message Queue. * @details * Inputs : Input Comm Buffers * Outputs : Parsed message(s) added to Received Message Queue * @param msg : none * @return none *************************************************************************/ static void processIncomingData( void ) { U08 data[sizeof(MESSAGE_WRAPPER_T)+1]; U32 i; // TODO - remove this later - this should be called by CAN receive interrupt handler when implemented handleCANPacketReceivedInt( COMM_BUFFER_IN_CAN_UI_2_HD ); // queue any received CAN messages for ( i = 0; i < NUM_OF_CAN_IN_BUFFERS; i++ ) { U32 numOfBytesInBuffer; // since messages can have CAN padding left unconsumed by last get, get padding out of buffer consumeBufferPaddingBeforeSync( CAN_IN_BUFFERS[i] ); // do we have enough bytes in buffer for smallest message? numOfBytesInBuffer = numberOfBytesInCommBuffer( CAN_IN_BUFFERS[i] ); if ( numOfBytesInBuffer >= MESSAGE_OVERHEAD_SIZE ) { U32 bytesPeeked = peekFromCommBuffer( CAN_IN_BUFFERS[i], data, MIN(numOfBytesInBuffer,(sizeof(MESSAGE_WRAPPER_T)+1)) ); U32 msgSize = parseMessageFromBuffer( data, bytesPeeked ); if ( msgSize > 0 ) { msgSize = getFromCommBuffer( CAN_IN_BUFFERS[i], data, msgSize ); if ( msgSize > 0 ) { MESSAGE_WRAPPER_T rcvMsg; U08 *dataPtr = data; blankMessageInWrapper( &rcvMsg ); memcpy( &(rcvMsg.msg.hdr), dataPtr, sizeof(MESSAGE_HEADER_T) ); dataPtr += sizeof(MESSAGE_HEADER_T); memcpy( &(rcvMsg.msg.cargo), dataPtr, rcvMsg.msg.hdr.cargoLen ); dataPtr += rcvMsg.msg.hdr.cargoLen; rcvMsg.crc = *dataPtr; addToMsgQueue( MSG_Q_IN_CAN, &rcvMsg ); } } } } } /************************************************************************* * @brief consumeBufferPaddingBeforeSync * The consumeBufferPaddingBeforeSync function removes any bytes in a given \n * buffer that lie before a sync byte. * @details * Inputs : none * Outputs : none * @param msg : buffer : the comm buffer to process * @return none *************************************************************************/ static void consumeBufferPaddingBeforeSync( COMM_BUFFER_T buffer ) { U08 data; U32 numOfBytesInBuffer = numberOfBytesInCommBuffer( buffer ); // consume bytes out of buffer 1 at a time until we find the sync byte or it's empty while ( numOfBytesInBuffer > 0 ) { getFromCommBuffer( buffer, &data, 1 ); if ( MESSAGE_SYNC_BYTE == data ) { break; // we found a sync - we're done } numOfBytesInBuffer = numberOfBytesInCommBuffer( buffer ); } } /************************************************************************* * @brief parseMessageFromBuffer * The parseMessageFromBuffer function looks for a complete message in a \n * given comm buffer. If found, the given buffer is populated with the \n * message and its size is returned. * @details * Inputs : Comm Buffer * Outputs : none * @param msg : data : pointer to byte array to populate with a message if found * @return size of message, zero if no complete message found. *************************************************************************/ static U32 parseMessageFromBuffer( U08 *data, U32 len ) { U32 i; U32 cargoSize; U32 msgSize; U32 result = 0; for ( i = 0; i < len; i++ ) { // find sync byte if ( MESSAGE_SYNC_BYTE == data[i] ) { U32 pos = i + 1; // skip past sync byte implemented U32 remSize = len - pos; // if a minimum sized msg would fit in remaining if ( remSize >= MESSAGE_OVERHEAD_SIZE ) { cargoSize = data[pos+sizeof(U16)]; msgSize = MESSAGE_OVERHEAD_SIZE + cargoSize; if ( msgSize <= remSize ) { result = msgSize; // we found a complete message of this size } } break; } } return result; } /************************************************************************* * @brief processReceivedMessages * The processReceivedMessages function processes any messages in the \n * received message queues. * @details * Inputs : Received Message Queues * Outputs : Message(s) processed. * @param msg : none * @return none *************************************************************************/ static void processReceivedMessages( void ) { BOOL isThereMsgRcvd; MESSAGE_WRAPPER_T message; // see if any messages received isThereMsgRcvd = getFromMsgQueue( MSG_Q_IN_CAN, &message ); if ( TRUE == isThereMsgRcvd ) { // TODO - check CRC before processing a message if ( 1 ) { processReceivedMessage( &message.msg ); } else // CRC failed { // TODO - probably wouldn't want to fault on this. ignore? } } // TODO - process UART (script) messages too } /************************************************************************* * @brief processReceivedMessage * The processReceivedMessage function processes a given message. * @details * Inputs : none * Outputs : message processed * @param msg : * @return none *************************************************************************/ static void processReceivedMessage( MESSAGE_T *message ) { switch ( message->hdr.msgID ) { case MSG_ID_OFF_BUTTON_PRESS: handleOffButtonConfirmMsgFromUI( message ); // ***************************** TEST CODE ****************************** // TODO - remove later #if 1 setUserLED( TRUE ); #endif // ************************** END TEST CODE ****************************** break; default: // TODO - unrecognized message ID received - ignore break; } }