/********************************************************************************//*!
 * @file  sample_can_interface.c
 * @brief  CAN Controller Interface.
 * 
 * @details  This file contains the processing for the MCU's CAN controller.
 * 
 * @note  nothing. 
 * 
 * @date <b> History: \<Date\> \<Rev\> \<Keyword\> \<Details\> </b>
 * @date 2024/03/27  ---  #-----  新規作成.
 * 
 **//*******************************************************************************/
/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2022 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/***********************************************************************************
 *  includes
 ***********************************************************************************/
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#include "FreeRTOS.h"
#include "queue.h"

#include "fsl_debug_console.h"
#include "fsl_mcan.h"
#include "pin_mux.h"
#include "board.h"
#include "stdlib.h"
#include "event_groups.h"
#include "sample_can_interface.h"
#include "UARTCommandConsole.h"

/***********************************************************************************
 *  externs 
 ***********************************************************************************/
extern QueueHandle_t xQueueFPGAtoCAN0;
extern QueueHandle_t xQueueFPGAtoCAN1;
extern QueueHandle_t xQueueCAN0toFPGA;
extern QueueHandle_t xQueueCAN1toFPGA;

/***********************************************************************************
 *  proto types 
 ***********************************************************************************/
/* CAN test commands */
static BaseType_t prvCan0to1Command(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t prvCan1to0Command(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
/* CAN tasks - for MCU Loopback */
static void prvCan0_NodeA_Task(void *pvParameters);
static void prvCan0_NodeB_Task(void *pvParameters);
static void prvCan1_NodeA_Task(void *pvParameters);
static void prvCan1_NodeB_Task(void *pvParameters);
/* CAN tasks - for TXRX via FPGA */
static void prvCAN0InterfaceTask(void *pvParameters);
static void prvCAN1InterfaceTask(void *pvParameters);
/* CAN test callbacks */
static void vCan0_Callback(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData);
static void vCan1_Callback(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData);
void MCAN_MCULoopback_Init(void);

/***********************************************************************************
 *  definitions (define, enum, typedef, etc..) 
 ***********************************************************************************/
typedef void (*CanCallback_t)(CAN_Type*, mcan_handle_t*, status_t, uint32_t, void*);

/***********************************************************************************
 *  variables
 ***********************************************************************************/
// Data length bytes table
const uint32_t DATA_LENGTH_BYTES[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};

/* Event Group */
EventGroupHandle_t	xEventGroup[eIndexCanMAX][eIndexNodeMAX];		/* [2][2]: [CAN0 or CAN1][NodeA or NodeB] */
EventGroupHandle_t xEventGroupFpgaInterface;
EventGroupHandle_t xEventGroupCan0Interface;
EventGroupHandle_t xEventGroupCan1Interface;
EventGroupHandle_t xEventGroupSyncTasks;

/* Can0 variables */
volatile bool txComplete[eIndexCanMAX];
volatile bool rxComplete[eIndexCanMAX];
mcan_tx_buffer_frame_t txFrame[eIndexCanMAX];
mcan_rx_buffer_frame_t rxFrame[eIndexCanMAX];
uint8_t tx_data[eIndexCanMAX][CAN_DATASIZE];
uint8_t rx_data[eIndexCanMAX][CAN_DATASIZE];
mcan_handle_t mcanHandle[eIndexCanMAX];
mcan_buffer_transfer_t txXfer[eIndexCanMAX];
mcan_fifo_transfer_t rxXfer[eIndexCanMAX];
uint32_t canIdentifier[eIndexCanMAX];
uint32_t txIdentifier[eIndexCanMAX];
uint32_t rxIdentifier[eIndexCanMAX];
#ifndef MSG_RAM_BASE
/* The MCAN IP stipulates that the Message RAM start address must align with 0x10000, but in some devices, the single
 * available RAM block size is less than this value, so we must try to place the Message RAM variables at the start of
 * the RAM block to avoid memory overflow during link application. By default, the data section is before of the bss
 * section, so adds the initial value for Message RAM variables to confirm it be placed in the data section, and it must
 * be initialized with 0 before used. */
#if defined(__CC_ARM) || defined(__ARMCC_VERSION)
/* The KEIL --sort=algorithm only impacts code sections, and the data sections is sorted by name, so by specifying the
 * memory as .data to ensure that it is put in the first place of RAM. */
__attribute__((aligned(1U << CAN_MRBA_BA_SHIFT), section(".data"))) uint8_t msgRam_Can0[MSG_RAM_SIZE] = {1U};
__attribute__((aligned(1U << CAN_MRBA_BA_SHIFT), section(".data"))) uint8_t msgRam_Can1[MSG_RAM_SIZE] = {1U};
#else
SDK_ALIGN(uint8_t msgRam_Can0[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT) = {1U};
SDK_ALIGN(uint8_t msgRam_Can1[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT) = {1U};
#endif
#else
#define msgRam_Can0 MSG_RAM_BASE
#define msgRam_Can1 MSG_RAM_BASE
#endif

static char cNodeName[eIndexNodeMAX] = {'A', 'B'};
static CAN_Type* pCanBase[eIndexCanMAX] = {CAN0, CAN1};
static CanCallback_t pCanCallBack[eIndexCanMAX] = {vCan0_Callback, vCan1_Callback};
static uint8_t* pmsgRam[eIndexCanMAX] = {msgRam_Can0, msgRam_Can1};

/***********************************************************************************
 *  functions 
 ***********************************************************************************/
#if USE_MCU_CAN_UNITTEST
/* CAN0 to CAN1 loopback test command */
const CLI_Command_Definition_t xCan0to1 =
{
    "can01",                /* The command string to type. */
    "\r\n  \"can01\": CAN0 to CAN1 loopback test\r\n"
    "    < Usage: can01 {start/stop} >\r\n",
    prvCan0to1Command,      /* The function to run. */
    1                       /* No parameters are expected. */
};

/* CAN1 to CAN0 loopback test command */
const CLI_Command_Definition_t xCan1to0 =
{
    "can10",                /* The command string to type. */
    "\r\n  \"can10\": CAN1 to CAN0 loopback test\r\n"
    "    < Usage: can10 {start/stop} >\r\n",
    prvCan1to0Command,      /* The function to run. */
    1                       /* No parameters are expected. */
};

/*-----------------------------------------------------------*/
/* CAN0 to CAN1 loopback test command */
static BaseType_t prvCan0to1Command( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
    const char *pcParameter;
    BaseType_t lParameterStringLength;

    /* Remove compile time warnings about unused parameters, and check the
       write buffer is not NULL.  NOTE - for simplicity, this example assumes the
       write buffer length is adequate, so does not check for buffer overflows. */
    ( void ) pcCommandString;
    ( void ) xWriteBufferLen;
    configASSERT( pcWriteBuffer );

    /* Obtain the parameter string. */
    pcParameter = FreeRTOS_CLIGetParameter( pcCommandString,        /* The command string itself. */
                                            1,                      /* Return the first parameter. */
                                            &lParameterStringLength /* Store the parameter string length. */
                                          );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    /* There are only two valid parameter values. */
    if( strncmp( pcParameter, "start", strlen( "start" ) ) == 0 )
    {
        xEventGroupSetBits(xEventGroup[eIndexCan1][eIndexNodeB], B0);
        xEventGroupSetBits(xEventGroup[eIndexCan1][eIndexNodeB], B1);
        xEventGroupSetBits(xEventGroup[eIndexCan0][eIndexNodeA], B0);
        xEventGroupSetBits(xEventGroup[eIndexCan0][eIndexNodeA], B1);
    }
    else if( strncmp( pcParameter, "stop", strlen( "stop" ) ) == 0 )
    {
        xEventGroupClearBits(xEventGroup[eIndexCan0][eIndexNodeA], B0);
        xEventGroupClearBits(xEventGroup[eIndexCan0][eIndexNodeA], B1);
        xEventGroupClearBits(xEventGroup[eIndexCan1][eIndexNodeB], B0);
        xEventGroupClearBits(xEventGroup[eIndexCan1][eIndexNodeB], B1);
    }
    else
    {
        sprintf( pcWriteBuffer, "\r\nInvalid parameter!! < Usage: can01 {start/stop} >\r\n\r\n\0");
    }

    strncpy( pcWriteBuffer, "", xWriteBufferLen );
    
    /* There is no more data to return after this single string, so return pdFALSE. */
    return pdFALSE;
}

/*-----------------------------------------------------------*/
/* CAN1 to CAN0 loopback test command */
static BaseType_t prvCan1to0Command( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
    const char *pcParameter;
    BaseType_t lParameterStringLength;

    /* Remove compile time warnings about unused parameters, and check the
       write buffer is not NULL.  NOTE - for simplicity, this example assumes the
       write buffer length is adequate, so does not check for buffer overflows. */
    ( void ) pcCommandString;
    ( void ) xWriteBufferLen;
    configASSERT( pcWriteBuffer );

    /* Obtain the parameter string. */
    pcParameter = FreeRTOS_CLIGetParameter( pcCommandString,        /* The command string itself. */
                                            1,                      /* Return the first parameter. */
                                            &lParameterStringLength /* Store the parameter string length. */
                                          );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    /* There are only two valid parameter values. */
    if( strncmp( pcParameter, "start", strlen( "start" ) ) == 0 )
    {
        xEventGroupSetBits(xEventGroup[eIndexCan0][eIndexNodeB], B0);
        xEventGroupSetBits(xEventGroup[eIndexCan0][eIndexNodeB], B1);
        xEventGroupSetBits(xEventGroup[eIndexCan1][eIndexNodeA], B0);
        xEventGroupSetBits(xEventGroup[eIndexCan1][eIndexNodeA], B1);
    }
    else if( strncmp( pcParameter, "stop", strlen( "stop" ) ) == 0 )
    {
        xEventGroupClearBits(xEventGroup[eIndexCan1][eIndexNodeA], B0);
        xEventGroupClearBits(xEventGroup[eIndexCan1][eIndexNodeA], B1);
        xEventGroupClearBits(xEventGroup[eIndexCan0][eIndexNodeB], B0);
        xEventGroupClearBits(xEventGroup[eIndexCan0][eIndexNodeB], B1);
    }
    else
    {
        sprintf( pcWriteBuffer, "\r\nInvalid parameter!! < Usage: can10 {start/stop} >\r\n\r\n\0");
    }

    strncpy( pcWriteBuffer, "", xWriteBufferLen );

    /* There is no more data to return after this single string, so return pdFALSE. */
    return pdFALSE;
}
/*-----------------------------------------------------------*/
#endif	/* #if USE_MCU_CAN_UNITTEST */

/************************
 *  Callback Functions  *
 ************************/
/* CAN0 callback function */
static void vCan0_Callback(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
    switch (status)
    {
        case kStatus_MCAN_RxFifo0Idle:
        {
            rxComplete[eIndexCan0] = true;
        }
        break;

        case kStatus_MCAN_TxIdle:
        {
            txComplete[eIndexCan0] = true;
        }
        break;

        default:
            break;
    }
}
/*-----------------------------------------------------------*/

/* CAN1 callback function */
static void vCan1_Callback(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
    switch (status)
    {
        case kStatus_MCAN_RxFifo0Idle:
        {
            rxComplete[eIndexCan1] = true;
        }
        break;

        case kStatus_MCAN_TxIdle:
        {
            txComplete[eIndexCan1] = true;
        }
        break;

        default:
            break;
    }
}
/*-----------------------------------------------------------*/

#if USE_MCU_CAN_UNITTEST
static void prvCanX_NodeX_Task(int can0or1, int nodeAorB, void *pvParameters)
{
    EventBits_t event_bits;
    mcan_config_t mcanConfig;
    mcan_memory_config_t memoryConfig          = {0};
    mcan_frame_filter_config_t rxFilter        = {0};
    mcan_std_filter_element_config_t stdFilter = {0};
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
    mcan_frame_filter_config_t extRxFilter     = {0};
    mcan_ext_filter_element_config_t extFilter = {0};
#endif
    mcan_rx_fifo_config_t rxFifo0    = {0};
    mcan_tx_buffer_config_t txBuffer = {0};
    uint8_t numMessage = 0;
    uint8_t cnt        = 0;

    event_bits = xEventGroupWaitBits(xEventGroup[can0or1][nodeAorB],  /* The event group handle. */
                                     B0,                /* The bit pattern the event group is waiting for. */
                                     pdTRUE,            /* B0 and B1 will be not cleared automatically. */
                                     pdTRUE,            /* Don't wait for both bits, either bit unblock task. */
                                     portMAX_DELAY);    /* Block indefinitely to wait for the condition to be met. */
    if ((event_bits & B0) == B0)
    {
#if USE_DEBUG_PRINT
        vOutputPrintf("== prvCan%d_Node%c_Task() -- bit B0 is set.\r\n", can0or1, cNodeName[nodeAorB]);
#endif
    }
    vOutputPrintf("\r\n== prvCan%d_Node%c_Task() -- Start ==\r\n\r\n", can0or1, cNodeName[nodeAorB]);

	if(nodeAorB == eIndexNodeA)
	{
		txIdentifier[can0or1] = 0x321U;
		rxIdentifier[can0or1] = 0x123U;
	} else /* if(nodeAorB == eIndexNodeB) */
	{
		txIdentifier[can0or1] = 0x123U;
		rxIdentifier[can0or1] = 0x321U;
	}

    /* Get MCAN module default Configuration. */
    /*
     * mcanConfig.baudRate               = 500000U;
     * mcanConfig.baudRateFD             = 2000000U;
     * mcanConfig.enableCanfdNormal      = false;
     * mcanConfig.enableCanfdSwitch      = false;
     * mcanConfig.enableLoopBackInt      = false;
     * mcanConfig.enableLoopBackExt      = false;
     * mcanConfig.enableBusMon           = false;
     */
    MCAN_GetDefaultConfig(&mcanConfig);
#if (defined(USE_CANFD) && USE_CANFD)
    /* Enable Bit Rate Switch to make baudRateD make sense.*/
    mcanConfig.enableCanfdSwitch = true;
#endif

#if (defined(USE_IMPROVED_TIMING_CONFIG) && USE_IMPROVED_TIMING_CONFIG)
    mcan_timing_config_t timing_config;
    memset(&timing_config, 0, sizeof(timing_config));
#if (defined(USE_CANFD) && USE_CANFD)
    if (MCAN_FDCalculateImprovedTimingValues(mcanConfig.baudRateA, mcanConfig.baudRateD, MCAN_CLK_FREQ, &timing_config))
    {
        /* Update the improved timing configuration*/
        memcpy(&(mcanConfig.timingConfig), &timing_config, sizeof(mcan_timing_config_t));
    }
    else
    {
        vOutputPrintf("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
    }
#else
    if (MCAN_CalculateImprovedTimingValues(mcanConfig.baudRateA, MCAN_CLK_FREQ, &timing_config))
    {
        /* Update the improved timing configuration*/
        memcpy(&(mcanConfig.timingConfig), &timing_config, sizeof(mcan_timing_config_t));
    }
    else
    {
        vOutputPrintf("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
    }
#endif
#endif

    MCAN_Init(pCanBase[can0or1], &mcanConfig, MCAN_CLK_FREQ);

    memoryConfig.baseAddr = (uint32_t)pmsgRam[can0or1];
    /* STD filter config. */
    rxFilter.address          = STD_FILTER_OFS;
    rxFilter.idFormat         = kMCAN_FrameIDStandard;
    rxFilter.listSize         = 1U;
    rxFilter.nmFrame          = kMCAN_reject0;
    rxFilter.remFrame         = kMCAN_rejectFrame;
    memoryConfig.stdFilterCfg = &rxFilter;
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
    /* EXT filter config. */
    extRxFilter.address       = EXT_FILTER_OFS;
    extRxFilter.idFormat      = kMCAN_FrameIDExtend;
    extRxFilter.listSize      = 1U;
    extRxFilter.nmFrame       = kMCAN_reject0;
    extRxFilter.remFrame      = kMCAN_rejectFrame;
    memoryConfig.extFilterCfg = &extRxFilter;
#endif
    /* RX fifo0 config. */
    rxFifo0.address       = RX_FIFO0_OFS;
    rxFifo0.elementSize   = 1U;
    rxFifo0.watermark     = 0;
    rxFifo0.opmode        = kMCAN_FifoBlocking;
    rxFifo0.datafieldSize = kMCAN_8ByteDatafield;
#if (defined(USE_CANFD) && USE_CANFD)
    rxFifo0.datafieldSize = BYTES_IN_MB;
#endif
    memoryConfig.rxFifo0Cfg = &rxFifo0;
    /* TX buffer config. */
    txBuffer.address       = TX_BUFFER_OFS;
    txBuffer.dedicatedSize = 1U;
    txBuffer.fqSize        = 0;
    txBuffer.datafieldSize = kMCAN_8ByteDatafield;
#if (defined(USE_CANFD) && USE_CANFD)
    txBuffer.datafieldSize = BYTES_IN_MB;
#endif
    memoryConfig.txBufferCfg = &txBuffer;
    /* Set Message RAM config and clear memory to avoid BEU/BEC error. */
    memset((void *)pmsgRam[can0or1], 0, MSG_RAM_SIZE * sizeof(uint8_t));
    if (kStatus_Success != MCAN_SetMessageRamConfig(pCanBase[can0or1], &memoryConfig))
    {
        vOutputPrintf("MCAN Message RAM configuration failed, please check parameters!\r\n");
    }

    /* Filling Standard ID filter element with Classic filter mode, which only filter matching ID. */
    stdFilter.sfec  = kMCAN_storeinFifo0;
    stdFilter.sft   = kMCAN_classic;
    stdFilter.sfid1 = rxIdentifier[can0or1];
    stdFilter.sfid2 = 0x7FFU;
    MCAN_SetSTDFilterElement(pCanBase[can0or1], &rxFilter, &stdFilter, 0);
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
    /* Filling extended ID filter element with range filter (0x0 ~ 0x1FFFFFFF) mode, which make it actually accept all
     * extended frame. */
    extFilter.efec  = kMCAN_storeinFifo0;
    extFilter.eft   = kMCAN_range;
    extFilter.efid1 = 0x0U;
    extFilter.efid2 = 0x1FFFFFFFU;
    MCAN_SetEXTFilterElement(pCanBase[can0or1], &extRxFilter, &extFilter, 0);
#endif

    /* Create MCAN handle structure and set call back function. */
    MCAN_TransferCreateHandle(pCanBase[can0or1], &mcanHandle[can0or1], pCanCallBack[can0or1], NULL);

	if(nodeAorB == eIndexNodeA)
	{
		/* Config TX frame data. */
		memset(tx_data[can0or1], 0, sizeof(uint8_t) * CAN_DATASIZE);
		for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
		{
			tx_data[can0or1][cnt] = cnt;
		}
		#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
		txFrame[can0or1].xtd = kMCAN_FrameIDExtend;
		txFrame[can0or1].id  = 0x12345678;
		#else
		txFrame[can0or1].xtd = kMCAN_FrameIDStandard;
		txFrame[can0or1].id  = txIdentifier[can0or1] << STDID_OFFSET;
		#endif
		txFrame[can0or1].rtr  = kMCAN_FrameTypeData;
		txFrame[can0or1].fdf  = 0;
		txFrame[can0or1].brs  = 0;
		txFrame[can0or1].dlc  = 8U;
		txFrame[can0or1].data = tx_data[can0or1];
		txFrame[can0or1].size = CAN_DATASIZE;
		#if (defined(USE_CANFD) && USE_CANFD)
		txFrame[can0or1].fdf = 1;
		txFrame[can0or1].brs = 1;
		txFrame[can0or1].dlc = DLC;
		#endif
		txXfer[can0or1].frame     = &txFrame[can0or1];
		txXfer[can0or1].bufferIdx = 0;

		vOutputPrintf("CAN%d: Start transmission to CAN%d\r\n\r\n", can0or1, can0or1^1);
	} else /**/
	{
		vOutputPrintf("CAN%d: Wait data from CAN%d\r\n\r\n", can0or1, can0or1^1);
	}

    while (1)
    {
        event_bits = xEventGroupWaitBits(xEventGroup[can0or1][nodeAorB],  /* The event group handle. */
                                         B1,                /* The bit pattern the event group is waiting for. */
                                         pdTRUE,            /* B1 and B1 will be not cleared automatically. */
                                         pdTRUE,            /* Don't wait for both bits, either bit unblock task. */
                                         portMAX_DELAY);    /* Block indefinitely to wait for the condition to be met. */
        if ((event_bits & B1) == B1)
        {
#if USE_DEBUG_PRINT
            vOutputPrintf("== prvCan%d_Node%c_Task() -- bit B1 is set.\r\n", can0or1, cNodeName[nodeAorB]);
#endif
        }

		if(nodeAorB == eIndexNodeA)
		{
			tx_data[can0or1][0] = numMessage++;
			MCAN_TransferSendNonBlocking(pCanBase[can0or1], &mcanHandle[can0or1], &txXfer[can0or1]);

			while (!txComplete[can0or1])
			{
		        vTaskDelay(10/portTICK_PERIOD_MS);	/* wait 10ms */
			}
			txComplete[can0or1] = false;
			vOutputPrintf("CAN%d: Tx complete callback occurred!\r\n\r\n", can0or1);
		}

        /* Start receive data through Rx FIFO 0. */
        memset(rx_data[can0or1], 0, sizeof(uint8_t) * CAN_DATASIZE);
        /* the MCAN engine can't auto to get rx payload size, we need set it. */
        rxFrame[can0or1].size = CAN_DATASIZE;
        rxXfer[can0or1].frame = &rxFrame[can0or1];
        MCAN_TransferReceiveFifoNonBlocking(pCanBase[can0or1], 0, &mcanHandle[can0or1], &rxXfer[can0or1]);

        /* Wait until message received. */
        while (!rxComplete[can0or1])
        {
    		vTaskDelay(10/portTICK_PERIOD_MS);	/* wait 10ms */
        }
        rxComplete[can0or1] = false;
        vOutputPrintf("CAN%d: Rx complete callback occurred!\r\n\r\n", can0or1);

        /* After call the API of rMCAN_TransferReceiveFifoNonBlocking success, we can
         * only get a point (rxFrame_Can0.data) to the fifo reading entrance.
         * Copy the received frame data from the FIFO by the pointer(rxFrame_Can0.data). */
        memcpy(rx_data[can0or1], rxFrame[can0or1].data, rxFrame[can0or1].size);

#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
        vOutputPrintf("CAN%d: Received Extend Frame ID: 0x%x\r\n", can0or1, rxFrame[can0or1].id);
#else
        vOutputPrintf("CAN%d: Received Frame ID: 0x%x\r\n", can0or1, rxFrame[can0or1].id >> STDID_OFFSET);
#endif

        vOutputPrintf("CAN%d: Received Frame DATA:\r\n", can0or1);
        cnt = 0;
        while (cnt < rxFrame[can0or1].size)
        {
            vOutputPrintf("0x%x ", rx_data[can0or1][cnt++]);
        }
        vOutputPrintf("\r\n\r\n");

		if(nodeAorB == eIndexNodeB)
		{
			/* Copy received frame data to tx frame. */
			memcpy(tx_data[can0or1], rx_data[can0or1], CAN_DATASIZE);

			txFrame[can0or1].xtd = rxFrame[can0or1].xtd;
			txFrame[can0or1].rtr = rxFrame[can0or1].rtr;
			txFrame[can0or1].fdf = rxFrame[can0or1].fdf;
			txFrame[can0or1].brs = rxFrame[can0or1].brs;
			txFrame[can0or1].dlc = rxFrame[can0or1].dlc;
			#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
			txFrame[can0or1].id = 0x12345678;
			#else
			txFrame[can0or1].id = txIdentifier[can0or1] << STDID_OFFSET;
			#endif
			txFrame[can0or1].data     = tx_data[can0or1];
			txFrame[can0or1].size     = rxFrame[can0or1].size;
			txXfer[can0or1].frame     = &txFrame[can0or1];
			txXfer[can0or1].bufferIdx = 0;

			MCAN_TransferSendNonBlocking(pCanBase[can0or1], &mcanHandle[can0or1], &txXfer[can0or1]);

			while (!txComplete[can0or1])
			{
		        vTaskDelay(10/portTICK_PERIOD_MS);	/* wait 10ms */
			}
			txComplete[can0or1] = false;
			vOutputPrintf("CAN%d: Tx complete callback occurred!\r\n\r\n", can0or1);
		}

		vTaskDelay(10/portTICK_PERIOD_MS);	/* wait 10ms */
    }
    vOutputPrintf("\r\n\r\n== prvCan%d_Node%c_Task() -- Finish ==\r\n\r\n", can0or1, cNodeName[nodeAorB]);
}

/* CAN0 Node-A role task */
static void prvCan0_NodeA_Task(void *pvParameters)
{
	prvCanX_NodeX_Task(eIndexCan0, eIndexNodeA, pvParameters);
}

/* CAN0 Node-B role task */
static void prvCan0_NodeB_Task(void *pvParameters)
{
	prvCanX_NodeX_Task(eIndexCan0, eIndexNodeB, pvParameters);
}

/* CAN1 Node-A role task */
static void prvCan1_NodeA_Task(void *pvParameters)
{
	prvCanX_NodeX_Task(eIndexCan1, eIndexNodeA, pvParameters);
}

/* CAN1 Node-B role task */
static void prvCan1_NodeB_Task(void *pvParameters)
{
	prvCanX_NodeX_Task(eIndexCan1, eIndexNodeB, pvParameters);
}

void MCAN_MCULoopback_Init(void)
{
	/* Register all the command line commands defined immediately above. */
    FreeRTOS_CLIRegisterCommand( &xCan0to1 );   /* CAN0 to CAN1 loopback test command */
    FreeRTOS_CLIRegisterCommand( &xCan1to0 );   /* CAN1 to CAN0 loopback test command */

    xEventGroup[eIndexCan0][eIndexNodeA] = xEventGroupCreate();
    xEventGroup[eIndexCan0][eIndexNodeB] = xEventGroupCreate();
    xEventGroup[eIndexCan1][eIndexNodeA] = xEventGroupCreate();
    xEventGroup[eIndexCan1][eIndexNodeB] = xEventGroupCreate();

	for(int i = 0; i < eIndexCanMAX; i++)
	{
		txComplete[i] = false;
		rxComplete[i] = false;
	}

    /* CAN1 Rx role task create */
    if (xTaskCreate(prvCan1_NodeB_Task, "prvCan1_NodeB_Task",
        configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
    {
        vOutputPrintf("Task creation failed!.\r\n");
        while (1)
            ;
    }
    /* CAN0 Tx role task create */
    if (xTaskCreate(prvCan0_NodeA_Task, "prvCan0_NodeA_Task",
        configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
    {
        vOutputPrintf("Task creation failed!.\r\n");
        while (1)
            ;
    }
    /* CAN0 Rx role task create */
    if (xTaskCreate(prvCan0_NodeB_Task, "prvCan0_NodeB_Task",
        configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
    {
        vOutputPrintf("Task creation failed!.\r\n");
        while (1)
            ;
    }
    /* CAN1 Tx role task create */
    if (xTaskCreate(prvCan1_NodeA_Task, "prvCan1_NodeA_Task",
        configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
    {
        vOutputPrintf("Task creation failed!.\r\n");
        while (1)
            ;
    }

    return;
}

/*-----------------------------------------------------------*/
#else	/* #if USE_MCU_CAN_UNITTEST */

static void prvCanX_TXRX_Task(int can0or1, void *pvParameters)
{
    static uint32_t ulQueueValue[mainQUEUE_IFTASKS_BYTES/sizeof(uint32_t)];
    static TickType_t xNextWakeTime;
    QueueHandle_t* pxQueueFpgaToCanX;
    QueueHandle_t* pxQueueCanXToFpga;
    EventGroupHandle_t* pxEventGroupCanXInterface;
    EventBits_t uxBitsToSet;

    static EventBits_t event_bits;
    mcan_config_t mcanConfig;
    mcan_memory_config_t memoryConfig          = {0};
    mcan_frame_filter_config_t rxFilter        = {0};
    mcan_std_filter_element_config_t stdFilter = {0};
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
    mcan_frame_filter_config_t extRxFilter     = {0};
    mcan_ext_filter_element_config_t extFilter = {0};
#endif
    mcan_rx_fifo_config_t rxFifo0    = {0};
    mcan_tx_buffer_config_t txBuffer = {0};
    mcan_rx_buffer_frame_t *rxBufferFrame;
    mcan_tx_buffer_frame_t *txBufferFrame;
    size_t PayloadSize, trxSize;
    static uint32_t cnt        = 0;
    UBaseType_t uxNumberOfItems;
    status_t result = kStatus_Success;

    vOutputPrintf("\r\n== prvCan%d_TXRX_Task() -- Start ==\r\n", can0or1);

    /* Initialise xNextWakeTime - this only needs to be done once. */
    xNextWakeTime = xTaskGetTickCount();

    if(can0or1 == eIndexCan0)
    {
        pxQueueFpgaToCanX = &xQueueFPGAtoCAN0;
        pxQueueCanXToFpga = &xQueueCAN0toFPGA;
        pxEventGroupCanXInterface = &xEventGroupCan0Interface;
        uxBitsToSet = C0IFTASK_BIT;
    } else /* if(can0or1 == eIndexCan1) */
    {
        pxQueueFpgaToCanX = &xQueueFPGAtoCAN1;
        pxQueueCanXToFpga = &xQueueCAN1toFPGA;
        pxEventGroupCanXInterface = &xEventGroupCan1Interface;
        uxBitsToSet = C1IFTASK_BIT;
    }

    /* Get MCAN module default Configuration. */
    /*
     * mcanConfig.baudRate               = 500000U;
     * mcanConfig.baudRateFD             = 2000000U;
     * mcanConfig.enableCanfdNormal      = false;
     * mcanConfig.enableCanfdSwitch      = false;
     * mcanConfig.enableLoopBackInt      = false;
     * mcanConfig.enableLoopBackExt      = false;
     * mcanConfig.enableBusMon           = false;
     */
    MCAN_GetDefaultConfig(&mcanConfig);
#if (defined(USE_CANFD) && USE_CANFD)
    /* Enable Bit Rate Switch to make baudRateD make sense.*/
    mcanConfig.enableCanfdSwitch = true;
#endif

#if (defined(USE_IMPROVED_TIMING_CONFIG) && USE_IMPROVED_TIMING_CONFIG)
    mcan_timing_config_t timing_config;
    memset(&timing_config, 0, sizeof(timing_config));
#if (defined(USE_CANFD) && USE_CANFD)
    if (MCAN_FDCalculateImprovedTimingValues(mcanConfig.baudRateA, mcanConfig.baudRateD, MCAN_CLK_FREQ, &timing_config))
    {
        /* Update the improved timing configuration*/
        memcpy(&(mcanConfig.timingConfig), &timing_config, sizeof(mcan_timing_config_t));
    }
    else
    {
        vOutputPrintf("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
    }
#else
    if (MCAN_CalculateImprovedTimingValues(mcanConfig.baudRateA, MCAN_CLK_FREQ, &timing_config))
    {
        /* Update the improved timing configuration*/
        memcpy(&(mcanConfig.timingConfig), &timing_config, sizeof(mcan_timing_config_t));
    }
    else
    {
        vOutputPrintf("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
    }
#endif
#endif

    MCAN_Init(pCanBase[can0or1], &mcanConfig, MCAN_CLK_FREQ);

    memoryConfig.baseAddr = (uint32_t)pmsgRam[can0or1];
    /* STD filter config. */
    rxFilter.address          = STD_FILTER_OFS;
    rxFilter.idFormat         = kMCAN_FrameIDStandard;
    rxFilter.listSize         = 1U;
    rxFilter.nmFrame          = kMCAN_reject0;
    rxFilter.remFrame         = kMCAN_rejectFrame;
    memoryConfig.stdFilterCfg = &rxFilter;
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
    /* EXT filter config. */
    extRxFilter.address       = EXT_FILTER_OFS;
    extRxFilter.idFormat      = kMCAN_FrameIDExtend;
    extRxFilter.listSize      = 1U;
    extRxFilter.nmFrame       = kMCAN_reject0;
    extRxFilter.remFrame      = kMCAN_rejectFrame;
    memoryConfig.extFilterCfg = &extRxFilter;
#endif
    /* RX fifo0 config. */
    rxFifo0.address       = RX_FIFO0_OFS;
    rxFifo0.elementSize   = 1U;
    rxFifo0.watermark     = 0;
    rxFifo0.opmode        = kMCAN_FifoBlocking;
    rxFifo0.datafieldSize = kMCAN_8ByteDatafield;
#if (defined(USE_CANFD) && USE_CANFD)
    rxFifo0.datafieldSize = BYTES_IN_MB;
#endif
    memoryConfig.rxFifo0Cfg = &rxFifo0;
    /* TX buffer config. */
    txBuffer.address       = TX_BUFFER_OFS;
    txBuffer.dedicatedSize = 1U;
    txBuffer.fqSize        = 0;
    txBuffer.datafieldSize = kMCAN_8ByteDatafield;
#if (defined(USE_CANFD) && USE_CANFD)
    txBuffer.datafieldSize = BYTES_IN_MB;
#endif
    memoryConfig.txBufferCfg = &txBuffer;
    /* Set Message RAM config and clear memory to avoid BEU/BEC error. */
    memset((void *)pmsgRam[can0or1], 0, MSG_RAM_SIZE * sizeof(uint8_t));
    if (kStatus_Success != MCAN_SetMessageRamConfig(pCanBase[can0or1], &memoryConfig))
    {
        vOutputPrintf("MCAN Message RAM configuration failed, please check parameters!\r\n");
//        return -1;
    }

    /* Filling Standard ID filter element with Classic filter mode, which only filter matching ID. */
    stdFilter.sfec  = kMCAN_storeinFifo0;
    stdFilter.sft   = kMCAN_classic;
    stdFilter.sfid1 = canIdentifier[can0or1];
    stdFilter.sfid2 = 0x7FFU;
    MCAN_SetSTDFilterElement(pCanBase[can0or1], &rxFilter, &stdFilter, 0);
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
    /* Filling extended ID filter element with range filter (0x0 ~ 0x1FFFFFFF) mode, which make it actually accept all
     * extended frame. */
    extFilter.efec  = kMCAN_storeinFifo0;
    extFilter.eft   = kMCAN_range;
    extFilter.efid1 = 0x0U;
    extFilter.efid2 = 0x1FFFFFFFU;
    MCAN_SetEXTFilterElement(pCanBase[can0or1], &extRxFilter, &extFilter, 0);
#endif

    /* Create MCAN handle structure and set call back function. */
    MCAN_TransferCreateHandle(pCanBase[can0or1], &mcanHandle[can0or1], pCanCallBack[can0or1], NULL);

	/* Config TX frame data. */
	memset(tx_data[can0or1], 0, sizeof(uint8_t) * CAN_DATASIZE);
	#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
	txFrame[can0or1].xtd = kMCAN_FrameIDExtend;
	txFrame[can0or1].id  = canIdentifier[can0or1];
	#else
	txFrame[can0or1].xtd = kMCAN_FrameIDStandard;
	txFrame[can0or1].id  = canIdentifier[can0or1] << STDID_OFFSET;
	#endif
	txFrame[can0or1].rtr  = kMCAN_FrameTypeData;
	txFrame[can0or1].fdf  = 0;
	txFrame[can0or1].brs  = 0;
	txFrame[can0or1].dlc  = 8U;
	txFrame[can0or1].data = tx_data[can0or1];
	txFrame[can0or1].size = CAN_DATASIZE;
	#if (defined(USE_CANFD) && USE_CANFD)
	txFrame[can0or1].fdf = 1;
	txFrame[can0or1].brs = 1;
	txFrame[can0or1].dlc = DLC;
	#endif
	txXfer[can0or1].frame     = &txFrame[can0or1];
	txXfer[can0or1].bufferIdx = 0;

    /* Sets the initialization complete flag. */
    xEventGroupSetBits(*pxEventGroupCanXInterface, B0);
    vOutputPrintf("\r\n== prvCan%d_TXRX_Task() -- Init ==\r\n", can0or1);

    /* Synchronize the execution of each task: FPGA Interface, CAN0 Interface, CAN1 Interface */
    xEventGroupSync(xEventGroupSyncTasks, uxBitsToSet, ALL_TASKS_BITS, portMAX_DELAY);
    vOutputPrintf("\r\n== prvCan%d_TXRX_Task() -- Sync ==\r\n", can0or1);

    // Clear Rx Data buffer to zero. 
    memset(rx_data[can0or1], 0, sizeof(uint8_t) * CAN_DATASIZE);
    /* the MCAN engine can't auto to get rx payload size, we need set it. */
    rxFrame[can0or1].data = rx_data[can0or1];
    rxFrame[can0or1].size = CAN_DATASIZE;
    rxXfer[can0or1].frame = &rxFrame[can0or1];

    /* Receives a message from Rx FIFO using IRQ. Wait until message received. */
    result = MCAN_TransferReceiveFifoNonBlocking(pCanBase[can0or1], 0, &mcanHandle[can0or1], &rxXfer[can0or1]);
    if(result != kStatus_Success)
    {
        vOutputPrintf( "C%dIf_Task - MCAN_TransferReceiveFifoNonBlocking failed. result=%d\r\n", can0or1, result );
    }
    
    for (;;)
    {
        /* Place this task in the blocked state until it is time to run again.
        The block time is specified in ticks, the constant used converts ticks
        to ms.  While in the Blocked state this task will not consume any CPU
        time. */
        vTaskDelayUntil( &xNextWakeTime, mainCANXIF_TASK_PERIOD_MS );
        
        /*----------------------------- CAN TX Process ---------------------------*/
        // Check for queue notifications from the FPGA interface task. 
        if( uxNumberOfItems = uxQueueMessagesWaiting( *pxQueueFpgaToCanX ) )
        {
#if USE_DEBUG_PRINT_L2
            vOutputPrintf( "C%dIf_Task - Number of messages FpgaToC%d Queue=%d\r\n", can0or1, can0or1, uxNumberOfItems );
#endif
            //if ( xQueueReceive( *pxQueueFpgaToCanX, &ulQueueValue, portMAX_DELAY ) != pdPASS )
            if ( xQueueReceive( *pxQueueFpgaToCanX, &ulQueueValue, 0 ) != pdPASS )
            { 
                vOutputPrintf( "C%dIf_Task - Failed to receive message!\r\n", can0or1);
            }
#if USE_DEBUG_PRINT_L1
            vOutputPrintf( "C%dIf_Task - Q_Recv(FpgaToC%d)\r\n", can0or1, can0or1);
#endif

            txBufferFrame = (mcan_tx_buffer_frame_t*)ulQueueValue;
            PayloadSize = DATA_LENGTH_BYTES[txBufferFrame->dlc];
            trxSize = CAN_FRAMEHEADER_BYTES + PayloadSize;

            memcpy( &txFrame[can0or1], &ulQueueValue[0], CAN_FRAMEHEADER_BYTES);
#if 0
            /* Config TX frame data. */
            memset(tx_data[can0or1], 0, sizeof(uint8_t) * CAN_DATASIZE);
            #if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
            txFrame[can0or1].xtd = kMCAN_FrameIDExtend;
            txFrame[can0or1].id  = 0x12345678;
            #else
            txFrame[can0or1].xtd = kMCAN_FrameIDStandard;
            txFrame[can0or1].id  = canIdentifier[can0or1] << STDID_OFFSET;
            #endif
            txFrame[can0or1].rtr  = kMCAN_FrameTypeData;
            txFrame[can0or1].fdf  = 0;
            txFrame[can0or1].brs  = 0;
            txFrame[can0or1].dlc  = 8U;
            txFrame[can0or1].data = tx_data[can0or1];
            txFrame[can0or1].size = CAN_DATASIZE;
            #if (defined(USE_CANFD) && USE_CANFD)
            txFrame[can0or1].fdf = 1;
            txFrame[can0or1].brs = 1;
            txFrame[can0or1].dlc = DLC;
            #endif
#endif
            txXfer[can0or1].frame     = &txFrame[can0or1];
            txXfer[can0or1].bufferIdx = 0;

            // Copies the data read into ulQueueValue to the TX Frame data buffer. 
	        txFrame[can0or1].data = tx_data[can0or1];
            txFrame[can0or1].size = PayloadSize;
            memcpy( tx_data[can0or1], &ulQueueValue[CAN_FRAMEHEADER_BYTES/sizeof(uint32_t)], txFrame[can0or1].size );
#if USE_DEBUG_PRINT_L2
            vOutputPrintf( "C%dIf_Task - Q_Recv(FpgaToC%d) Data:\r\n", can0or1, can0or1);
            for ( cnt = 0; cnt < trxSize/sizeof(uint32_t); cnt++ )
            {
                vOutputPrintf( "0x%08x\r\n", ulQueueValue[cnt] );
            }
            vOutputPrintf( "\r\n" );
#endif
            vOutputPrintf( "C%dIf_Task - Tx Frame Data:\r\n", can0or1);
            for ( cnt = 0; cnt < txFrame[can0or1].size; cnt++ )
            {
                vOutputPrintf( "0x%02x ", tx_data[can0or1][cnt] );
            }
            vOutputPrintf( "\r\n\r\n" );

            // Performs transmission to the CAN controller. (non-blocking) 
            result = MCAN_TransferSendNonBlocking(pCanBase[can0or1], &mcanHandle[can0or1], &txXfer[can0or1]);
            if(result != kStatus_Success)
            {
                vOutputPrintf( "C%dIf_Task - MCAN_TransferSendNonBlocking failed. result=%d\r\n", can0or1, result );
            }
        }

		vTaskDelay(10/portTICK_PERIOD_MS);	/* wait 10ms */

        /* Send processing completion check: When the completion of sending is recognized, 
        　　the completion flag is cleared and the message is output. */
        if( txComplete[can0or1] )
        {
            txComplete[can0or1] = false;
            vOutputPrintf("CAN%d: Tx complete IRQ occurred!\r\n\r\n", can0or1);
        }

        /*----------------------------- CAN RX Process ---------------------------*/
        /* Receive processing completion check: When receiving is complete, clear the completion flag 
        　  and execute the receive data processing. */ 
        if( rxComplete[can0or1] )
        {
            rxComplete[can0or1] = false;
            vOutputPrintf("CAN%d: Rx complete IRQ occurred!\r\n\r\n", can0or1);

            rxBufferFrame = (mcan_rx_buffer_frame_t*)&rxFrame[can0or1];
            PayloadSize = DATA_LENGTH_BYTES[rxBufferFrame->dlc];
            trxSize = CAN_FRAMEHEADER_BYTES + PayloadSize;

            // @Note: It is better to add a condition to receive only when the CAN-ID is matched. 
            //if(rxFrame[can0or1].id == canIdentifier[can0or1] << STDID_OFFSET)
            //{

            /* After call the API of MCAN_TransferReceiveFifoNonBlocking success, we can
            * only get a point (rxFrame[can0or1].data) to the fifo reading entrance.
            * Copy the received frame data from the FIFO by the pointer(rxFrame[can0or1].data). */
            memcpy(rx_data[can0or1], rxBufferFrame->data, rxBufferFrame->size);

            // Display received CAN Frame ID. 
#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
            vOutputPrintf( "C%dIf_Task - Rx Extend Frame ID: 0x%x\r\n", can0or1, rxBufferFrame->id );
#else
            vOutputPrintf( "C%dIf_Task - Rx Frame ID: 0x%x\r\n", can0or1, rxBufferFrame->id >> STDID_OFFSET );
#endif
            // Display received CAN Frame Data. 
            vOutputPrintf( "C%dIf_Task - Rx Frame Data:\r\n", can0or1);
            for ( cnt = 0; cnt < rxBufferFrame->size; cnt++ )
            {
                vOutputPrintf( "0x%02x ", rx_data[can0or1][cnt] );
            }
            vOutputPrintf( "\r\n\r\n" );

            // Load the received data into the xQueueCan0ToFpga queue with the same structure. 
            // rxFrame [can0or1] Copy the frame to ulQueueValue. 
            memcpy(ulQueueValue, rxBufferFrame, CAN_FRAMEHEADER_BYTES);
            memcpy(&ulQueueValue[CAN_FRAMEHEADER_BYTES/sizeof(uint32_t)], rx_data[can0or1], rxBufferFrame->size);
#if USE_DEBUG_PRINT_L2
            vOutputPrintf( "C%dIf_Task - Q_Send(C%dToFpga) Data:\r\n", can0or1, can0or1 );
            for ( cnt = 0; cnt < (CAN_FRAMEHEADER_BYTES + rxBufferFrame->size)/sizeof(uint32_t); cnt++ )
            {
                vOutputPrintf( "0x%08x\r\n", ulQueueValue[cnt] );
            }
            vOutputPrintf( "\r\n" );
#endif
            // Send the frame set to ulQueueValue to the xQueueCan0ToFpga queue. 
            if ( xQueueSend( *pxQueueCanXToFpga, &ulQueueValue, 0 ) != pdPASS )
            { 
                vOutputPrintf( "C%dIf_Task - Failed to send message!\r\n", can0or1);
            }
#if USE_DEBUG_PRINT_L1
            vOutputPrintf( "C%dIf_Task - Q_Send(C%dToFpga)\r\n", can0or1, can0or1);
#endif

            // @Note: It is better to add a condition to receive only when the CAN-ID is matched. 
            //} else 
            //{
        	//	vTaskDelay(10/portTICK_PERIOD_MS);	/* wait 10ms */
            //}

            // Clears the received data buffer. 
            memset(rx_data[can0or1], 0, sizeof(uint8_t) * CAN_DATASIZE);
            /* the MCAN engine can't auto to get rx payload size, we need set it. */
            rxFrame[can0or1].data = rx_data[can0or1];
            rxFrame[can0or1].size = CAN_DATASIZE;
            rxXfer[can0or1].frame = &rxFrame[can0or1];

            /* Receives a message from Rx FIFO using IRQ. Wait until message received. */
            result = MCAN_TransferReceiveFifoNonBlocking(pCanBase[can0or1], 0, &mcanHandle[can0or1], &rxXfer[can0or1]);
            if(result != kStatus_Success)
            {
                vOutputPrintf( "C%dIf_Task - MCAN_TransferReceiveFifoNonBlocking failed. result=%d\r\n", can0or1, result );
            }
        }
    }
    vOutputPrintf("\r\n\r\n== prvCan%d_TXRX_Task() -- Finish ==\r\n\r\n", can0or1);
}

/* CAN0 TXRX task */
static void prvCAN0InterfaceTask(void *pvParameters)
{
	prvCanX_TXRX_Task(eIndexCan0, pvParameters);
}

/* CAN1 TXRX task */
static void prvCAN1InterfaceTask(void *pvParameters)
{
	prvCanX_TXRX_Task(eIndexCan1, pvParameters);
}

/*-----------------------------------------------------------*/

void MCAN_TXRXviaFPGA_Init(void)
{
	/* Register all the command line commands defined immediately above. */
    //FreeRTOS_CLIRegisterCommand( &xCan0to1 );   /* CAN0 to CAN1 loopback test command */
    //FreeRTOS_CLIRegisterCommand( &xCan1to0 );   /* CAN1 to CAN0 loopback test command */

    /* タスクの状態確認用： B0..初期化完了 */
    xEventGroupFpgaInterface = xEventGroupCreate();
    xEventGroupCan0Interface = xEventGroupCreate();
    xEventGroupCan1Interface = xEventGroupCreate();
    xEventGroupSyncTasks = xEventGroupCreate();
    
    /* Clear the initialization complete flag. */
    xEventGroupClearBits(xEventGroupFpgaInterface, B0);
    xEventGroupClearBits(xEventGroupCan0Interface, B0);
    xEventGroupClearBits(xEventGroupCan1Interface, B0);

#if (defined(USE_EXT_FILTER) && USE_EXT_FILTER)
	canIdentifier[eIndexCan0] = 0x87654321U;
	canIdentifier[eIndexCan1] = 0x12345678U;
#else
	canIdentifier[eIndexCan0] = 0x321U;
	canIdentifier[eIndexCan1] = 0x123U;
#endif

	for(int i = 0; i < eIndexCanMAX; i++)
	{
		txComplete[i] = false;
		rxComplete[i] = false;
	}

    if (xTaskCreate(prvCAN0InterfaceTask, "CAN0", configMINIMAL_STACK_SIZE * 4, NULL, mainCAN0IF_TASK_PRIORITY, NULL) != pdPASS)
    {
    	vOutputString("Task creation failed!.\r\n");
        while (1)
            ;
    }

    if (xTaskCreate(prvCAN1InterfaceTask, "CAN1", configMINIMAL_STACK_SIZE * 4, NULL, mainCAN1IF_TASK_PRIORITY, NULL) != pdPASS)
    {
    	vOutputString("Task creation failed!.\r\n");
        while (1)
            ;
    }

    return;
}
#endif	/* #if USE_MCU_CAN_UNITTEST */
/***********************************************************************************
 * end of file 
 ***********************************************************************************/
