/************************************************************************** * * 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 "SystemComm.h" // ***************************** TEST CODE ****************************** // TODO - remove later #if 1 #include "CPLD.h" #endif // ************************** END TEST CODE ****************************** // ********** private definitions ********** #define MESSAGE_SYNC_BYTE 0xA5 #define CAN_MESSAGE_CARGO_SIZE 8 typedef enum Msg_IDs { MSG_ID_TEST = 0, MSG_ID_OFF_BUTTON_PRESS, NUM_OF_MSG_IDS } MSG_ID_T; #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 U32 serializeMessage( MESSAGE_T msg, U08 *data ); static void processIncomingData( void ); static U32 parseMessageFromBuffer( U08 *data, U32 len ); static void consumeBufferDataBeforeSync( 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 } /************************************************************************* * @brief serializeMessage * The serializeMessage function serializes a given message into a given \n * array of bytes. A sync byte is inserted at the beginning of the message \n * and an 8-bit CRC is appended to the end of the message. The given array \n * must be large enough to hold the message + 1 sync byte and 1 CRC byte and \n * up to 7 CAN padding bytes. * @details * Inputs : none * Outputs : given data array populated with serialized message data. * @param msg : message to serialize * @param data : byte array to populate with message data * @return size (in bytes) of serialized message populated in given data array. *************************************************************************/ static U32 serializeMessage( MESSAGE_T msg, U08 *data ) { U32 msgSize = 0; U32 sizeMod, sizePad; U32 i; // prefix data with message sync byte //data[msgSize++] = MESSAGE_SYNC_BYTE; // serialize message header data memcpy( &data[msgSize], &(msg.hdr), sizeof(MESSAGE_HEADER_T) ); msgSize += sizeof(MESSAGE_HEADER_T); // serialize message cargo (only used bytes per cargoLen field) memcpy( &data[msgSize], &(msg.cargo), msg.hdr.cargoLen ); msgSize += msg.hdr.cargoLen; // TODO - calculate 8-bit CRC data[msgSize++] = 0; // TODO - s/b 8-bit CRC when calc is available // pad with zero bytes to get length a multiple of CAN_MESSAGE_CARGO_SIZE (8) sizeMod = msgSize % CAN_MESSAGE_CARGO_SIZE; sizePad = ( sizeMod == 0 ? 0 : CAN_MESSAGE_CARGO_SIZE - sizeMod ); for ( i = 0; i < sizePad; i++ ) { data[msgSize++] = 0; } return msgSize; } /************************************************************************* ********************** 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; //consumeBufferDataBeforeSync( CAN_IN_BUFFERS[i] ); // TODO - call when sync implemented numOfBytesInBuffer = numberOfBytesInCommBuffer( CAN_IN_BUFFERS[i] ); if ( numOfBytesInBuffer >= CAN_MESSAGE_CARGO_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 ); } } // MESSAGE_WRAPPER_T rcvMsg; // U08 *dataPtr = data; // // getFromCommBuffer( CAN_IN_BUFFERS[i], data, CAN_MESSAGE_CARGO_SIZE ); // 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 consumeBufferDataBeforeSync * The consumeBufferDataBeforeSync 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 consumeBufferDataBeforeSync( COMM_BUFFER_T buffer ) { // TODO - implement } /************************************************************************* * @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] ) if (1) // TODO - find sync byte when implemented { U32 pos = i; // + 1; // TODO - add one when sync byte implemented U32 remSize = len - pos; U32 minSize = sizeof(MESSAGE_HEADER_T) + sizeof(U08); // if a minimum sized msg would fit in remaining if ( remSize >= minSize ) { cargoSize = data[pos+sizeof(U16)]; msgSize = minSize + cargoSize; if ( msgSize <= remSize ) { result = msgSize; } } 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: userConfirmOffButton( message->cargo[0] ); // ***************************** TEST CODE ****************************** // TODO - remove later #if 1 setUserLED( TRUE ); #endif // ************************** END TEST CODE ****************************** break; default: // TODO - unrecognized message ID received - ignore break; } } /************************************************************************* **************** SUITE OF SEND MESSAGE FUNCTIONS BELOW ******************* *************************************************************************/ /************************************************************************* * @brief sendOffButtonMsgToUI * The sendOffButtonMsgToUI function constructs an off button msg to the UI \n * and queues the msg for transmit on the appropriate CAN channel. * @details * Inputs : none * Outputs : Off button msg constructed and queued. * @param none * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ BOOL sendOffButtonMsgToUI( void ) { BOOL result; MESSAGE_T msg; U32 msgSize; U08 data[sizeof(MESSAGE_WRAPPER_T)+1+CAN_MESSAGE_CARGO_SIZE]; // must hold full (wrapped) message + sync + any CAN padding U08 *dataPtr = data; // create a message record blankMessage( &msg ); msg.hdr.msgID = MSG_ID_OFF_BUTTON_PRESS; msg.hdr.cargoLen = 0; // serialize the message (w/ sync, CRC, and appropriate CAN padding) msgSize = serializeMessage( msg, data ); // add serialized message data to appropriate comm buffer result = addToCommBuffer( COMM_BUFFER_OUT_CAN_HD_2_UI, dataPtr, msgSize ); return result; }