//
// Include Files
//
#include "driverlib.h"
#include "device.h"
#include "inc/stw_types.h"
#include "inc/stw_dataTypes.h"
#include <string.h>

//
// Defines.
//
#define NUM_OF_MSG                      (1U)
#define MCAN_STD_ID_FILTER_NUM          (1U)
#define MCAN_EXT_ID_FILTER_NUM          (0U)
#define MCAN_FIFO_0_NUM                 (0U)
#define MCAN_FIFO_0_ELEM_SIZE           (MCAN_ELEM_SIZE_64BYTES)
#define MCAN_FIFO_1_NUM                 (0U)
#define MCAN_FIFO_1_ELEM_SIZE           (MCAN_ELEM_SIZE_64BYTES)
#define MCAN_RX_BUFF_NUM                (10U)
#define MCAN_RX_BUFF_ELEM_SIZE          (MCAN_ELEM_SIZE_64BYTES)
#define MCAN_TX_BUFF_SIZE               (NUM_OF_MSG)
#define MCAN_TX_FQ_SIZE                 (0U)
#define MCAN_TX_BUFF_ELEM_SIZE          (MCAN_ELEM_SIZE_64BYTES)
#define MCAN_TX_EVENT_SIZE              (0U)

//
//  Defining Starting Addresses for Message RAM Sections,
//  (Calculated from Macros based on User defined configuration above)
//
#define MCAN_STD_ID_FILT_START_ADDR     (0x0U)
#define MCAN_EXT_ID_FILT_START_ADDR     (MCAN_STD_ID_FILT_START_ADDR + ((MCAN_STD_ID_FILTER_NUM * MCANSS_STD_ID_FILTER_SIZE_WORDS * 4U)))
#define MCAN_FIFO_0_START_ADDR          (MCAN_EXT_ID_FILT_START_ADDR + ((MCAN_EXT_ID_FILTER_NUM * MCANSS_EXT_ID_FILTER_SIZE_WORDS * 4U)))
#define MCAN_FIFO_1_START_ADDR          (MCAN_FIFO_0_START_ADDR + (MCAN_getMsgObjSize(MCAN_FIFO_0_ELEM_SIZE) * 4U * MCAN_FIFO_0_NUM))
#define MCAN_RX_BUFF_START_ADDR         (MCAN_FIFO_1_START_ADDR + (MCAN_getMsgObjSize(MCAN_FIFO_1_ELEM_SIZE) * 4U * MCAN_FIFO_1_NUM))
#define MCAN_TX_BUFF_START_ADDR         (MCAN_RX_BUFF_START_ADDR + (MCAN_getMsgObjSize(MCAN_RX_BUFF_ELEM_SIZE) * 4U * MCAN_RX_BUFF_NUM))
#define MCAN_TX_EVENT_START_ADDR        (MCAN_TX_BUFF_START_ADDR + (MCAN_getMsgObjSize(MCAN_TX_BUFF_ELEM_SIZE) * 4U * (MCAN_TX_BUFF_SIZE + MCAN_TX_FQ_SIZE)))

//Other
#define MCAN_BUS_OFF_CNTR_ms            (100)
#define MCAN_BUS_LOCK_ERR_CNTR_ms       (200)

#define MCAN_TX_MESSAGE_BUFFERS         (1)
#define MCAN_RX_MESSAGE_BUFFERS         (1)

#define MCAN_DATA_BUFFER_SIZE           (64U) //Need to be the same as ELEM SIZE
#define MCAN_DATA_ELEM_SIZE             (MCAN_ELEM_SIZE_64BYTES)

#ifdef IS_NODE_1
// CAN ID for TX
#define LL_MCAN_TX_ADDR 0x01

// CAN ID for RX
#define LL_MCAN_RX_ADDR 0x00
#else
// CAN ID for TX
#define LL_MCAN_TX_ADDR 0x00
// CAN ID for RX
#define LL_MCAN_RX_ADDR 0x01
#endif

//#define USE_2MBAUDPS
//#define USE_4MBAUDPS
#define USE_5MBAUDPS

//#define USE_1_BYTE_PAYLOAD
#define USE_64_BYTE_PAYLOAD

#define USE_MCAN_WITH_ISR

//*******************************************************************************************************************************************//
// Global Structs
//*******************************************************************************************************************************************//
typedef struct
{
    uint32_t mcan_err_cntr;
    uint16_t mcan_dlc_length;
    uint16_t mcan_rx_msg_cntr;
    uint16_t mcan_is_rx;
    uint32_t mcan_rx_id;
} MCAN_RX_Handler_t;

//*******************************************************************************************************************************************//
// Variables
//*******************************************************************************************************************************************//
MCAN_RxBufElement rxMsg;
MCAN_TxBufElement txMsg;
MCAN_ProtocolStatus protStatus;

bool send_test_msg = false;
bool send_continous_test_msg = false;

volatile MCAN_RX_Handler_t mcan_handler;

// Lookup table for CAN FD DLC to data length mapping
volatile uint16_t dlc_to_length[16] =
{
        0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64
};

uint8_t msg[64] = {0};

volatile uint32_t mcan_recovery_counter = 0;
volatile uint32_t mcan_bus_lock_counter = 0;
volatile uint32_t mcan_rx_message_counter = 0;
volatile uint32_t mcan_isr_cntr = 0;
volatile uint32_t intrStatus = 0;

//Counter for bus off recovery
uint32_t mcan_bus_off_recovery_counter = 0;

//Counter for TX messages
uint32_t mcan_tx_message_counter = 0;
uint32_t mcan_tx_status = 0;

uint32_t mcan_cccr_status = 0;

//Flag for bus off lock
bool bus_off_lock = true;

uint32_t time_cntr_ms = 0;

//*******************************************************************************************************************************************//
// Local ISR
//*******************************************************************************************************************************************//
__interrupt void MCANIntr1ISR(void);


//*******************************************************************************************************************************************//
// Local Function Prototype
//*******************************************************************************************************************************************//
/**
 * @brief: Sends a MCAN Message
 */
static void MCANTxMessage(uint16_t *data, uint16_t len);

/**
 * @brief: Process of the MCAN
 */
static void MCANProcess();

/**
 * @brief: Test message
 */
static void MCAN_SEND_TEST_MSG();

/**
 * @brief: Receives a MCAN Message
 */
static void MCANRxMessages();

/**
 * @brief: Configure MCAN
 */
static void MCANConfig(void);

/**
 * @brief: This function will configure X-BAR for MCAN interrupts.
 */
static void MCANIntrConfig(void);


//*******************************************************************************************************************************************//
// Global Functions
//*******************************************************************************************************************************************//
void main()
{

    //
    // Initialize device clock and peripherals
    //
    Device_init();

    //
    // Initialize GPIO and unlock the GPIO configuration registers
    //
    Device_initGPIO();


    // GPIO4->MCAN TX on EVAL Board Only
    GPIO_setPinConfig(GPIO_4_MCAN_TX);
    GPIO_setDirectionMode(4, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(4, GPIO_PIN_TYPE_STD);


    GPIO_setPinConfig(GPIO_5_MCAN_RX);
    GPIO_setDirectionMode(5, GPIO_DIR_MODE_IN);
    GPIO_setPadConfig(5, GPIO_PIN_TYPE_STD);

    //Configure the MCAN
    MCANConfig();

    //Buffer declaration
    uint16_t i = 0;
    for(i = 0; i < 64; i++)
    {
        msg[i] = i;
    }


    while(1)
    {
        MCANProcess();
    }

}

//*******************************************************************************************************************************************//
// Static Functions
//*******************************************************************************************************************************************//

/* Defined on top of this file */
static void MCANProcess()
{
    //Check for messages to receive:
    MCANRxMessages();

    /* Wait 1ms */
    DEVICE_DELAY_US(1000);

    /* Do something */
    if(send_test_msg)
    {
        send_test_msg = false;
        MCAN_SEND_TEST_MSG();
    }
    else if(send_continous_test_msg)
    {
        MCAN_SEND_TEST_MSG();
    }
}

/**
 * @brief: Defined on top of this file
 */
static void MCAN_SEND_TEST_MSG()
{
    //Send the message
#ifdef USE_1_BYTE_PAYLOAD
    MCANTxMessage(msg, 1);
#else
    MCANTxMessage(msg, 64);
#endif
}

/* Defined on top of this file */
static void MCANTxMessage(uint16_t *data, uint16_t len)
{
    //
    // Check for correct length
    //
    if(len > MCAN_DATA_BUFFER_SIZE)
    {
        return;
    }

    //Count tx
    mcan_tx_message_counter++;

    //
    //copy
    //
    memcpy(&txMsg.data[0], data, len);

    //
    // Write message to Message RAM.
    //
    MCAN_writeMsgRam(MCANA_DRIVER_BASE, MCAN_MEM_TYPE_BUF, 0U,
                     &txMsg);

    //
    // Add transmission request for Tx buffer 0
    //
    mcan_tx_status = MCAN_txBufAddReq(MCANA_DRIVER_BASE, 0U);

    //
    // Wait till the frame is successfully transmitted (and ACKnowledged)
    // "Tx Buffer Transmission Occurred" register is polled.
    //
    while(MCAN_getTxBufReqPend(MCANA_DRIVER_BASE))
    {
    }
}

/* Defined on top of this file */
static void MCANRxMessages()
{
    if(mcan_handler.mcan_is_rx == 1)
    {
        //Reset flag
        mcan_handler.mcan_is_rx = 0;

        //
        if(rxMsg.dlc < 16)
        {
            mcan_handler.mcan_dlc_length = dlc_to_length[rxMsg.dlc];

            //Number of MSG
            mcan_handler.mcan_rx_msg_cntr++;
        }
    }
}

/* Defined on top of this file */
static void MCANIntrConfig(void)
{
    Interrupt_initModule();
    Interrupt_initVectorTable();

    Interrupt_register(INT_MCANA_1,&MCANIntr1ISR);
    Interrupt_enable(INT_MCANA_1);

    Interrupt_enableGlobal();
}

static void MCANConfig(void)
{
    MCAN_InitParams initParams;
    MCAN_MsgRAMConfigParams    msgRAMConfigParams;
    MCAN_StdMsgIDFilterElement stdFiltelem;
    MCAN_BitTimingParams       bitTimes;

    //
    //  Initializing all structs to zero to prevent stray values
    //
    memset(&initParams, 0, sizeof(initParams));
    memset(&msgRAMConfigParams, 0, sizeof(msgRAMConfigParams));
    memset(&stdFiltelem, 0, sizeof(stdFiltelem));
    memset(&bitTimes, 0, sizeof(bitTimes));

    //
    // Configure the divisor for the MCAN bit-clock
    //
    SysCtl_setMCANClk(SYSCTL_MCANCLK_DIV_3); //SYSCLK = 120MHz, DIV2 = 60MHz, DIV3 = 40MHz

#ifdef USE_MCAN_WITH_ISR
    //
    // ISR Configuration.
    //
    MCANIntrConfig();
#endif

    //
    // Reset Buffer
    //
    uint16_t j = 0;


    //Write also for handler
    mcan_handler.mcan_rx_id = LL_MCAN_RX_ADDR;

    //
    // Initialize message to receive
    //
    rxMsg.id = LL_MCAN_RX_ADDR;
    rxMsg.rtr = 0U;
    rxMsg.xtd = 0U;
    rxMsg.esi = 0U;
    rxMsg.rxts = 0U;   // Rx Timestamp
    rxMsg.dlc = 0U;
    rxMsg.brs = 0U;
    rxMsg.fdf = 0U;
    rxMsg.fidx = 0U;   // Filter Index
                              // (of matching Rx acceptance filter element)
    rxMsg.anmf = 0U;   // Accepted Non-matching Frame

    j = 0;
    for(j = 0; j < MCAN_DATA_BUFFER_SIZE; j++)  // Initialize receive buffer to 0
    {
        rxMsg.data[j]  = 0;
    }

    //
    // Initialize message to transmit.
    //
    txMsg.id       = ((uint32_t)(LL_MCAN_TX_ADDR)) << 18U; // STDID[28:18] --> CAN Message Address = (0xADDR) << 18U
    txMsg.rtr      = 0U;   // RTR = 0 (Data frame)
    txMsg.xtd      = 0U;   // XTD = 0 (11-bit standard identifier)
    txMsg.esi      = 0U;
#ifdef USE_1_BYTE_PAYLOAD
    txMsg.dlc      = 1U; //15U;  // 64 bytes // 8U = 8Byte
#else
    txMsg.dlc      = 15U; //15U;  // 64 bytes // 8U = 8Byte
#endif

    txMsg.fdf      = 1U;   // Frame transmitted in CAN FD format
    txMsg.brs      = 1U;   // Bit-rate switching enabled
    txMsg.efc      = 1U;   // Store TX events
    txMsg.mm       = 0xAAU;

    j = 0;
    for(j = 0; j < MCAN_DATA_BUFFER_SIZE; j++)
    {
        txMsg.data[j]  = 0;
    }


    //
    // Configure MCAN initialization parameters
    //
    initParams.fdMode            = 0x1U; // FD operation enabled.
    initParams.brsEnable         = 0x1U; // Bit rate switching enabled

    //
    // Transmitter Delay Compensation parameters.
    //
    initParams.tdcConfig.tdcf    = 0xAU;
    initParams.tdcConfig.tdco    = 0x6U;

    //
    // Initialize Message RAM Sections Configuration Parameters
    //
    msgRAMConfigParams.txStartAddr          = MCAN_TX_BUFF_START_ADDR;
    // Tx Buffers Start Address.

    msgRAMConfigParams.txBufNum             = MCAN_TX_BUFF_SIZE;
    // Number of Dedicated Transmit Buffers.

    msgRAMConfigParams.txBufMode            = 0U;

    msgRAMConfigParams.txFIFOSize           = MCAN_TX_FQ_SIZE;
    // Number of Tx FIFO or Tx Queue Elements

    msgRAMConfigParams.txBufElemSize        = MCAN_TX_BUFF_ELEM_SIZE;
    // Tx Buffer Element Size.

    //
    // Initialize Message RAM Sections Configuration Parameters
    //
    msgRAMConfigParams.flssa                = MCAN_STD_ID_FILT_START_ADDR;
    // Standard ID Filter List Start Address.
    msgRAMConfigParams.lss                  = MCAN_STD_ID_FILTER_NUM;
    // List Size: Standard ID.
    msgRAMConfigParams.rxBufStartAddr       = MCAN_RX_BUFF_START_ADDR;
    // Rx Buffer Start Address.
    msgRAMConfigParams.rxBufElemSize        = MCAN_RX_BUFF_ELEM_SIZE;
    // Rx Buffer Element Size.

    //
    // Initialize bit timings. --> Speed up to 5Mbps --> See MCAN_BitTimingCalculator.xls --> See nominal and data bit timing --> two different sections in the .xls
    //
#ifdef USE_2MBAUDPS
    bitTimes.nomTimeSeg1        = 9U; // Nominal Time segment before SP
    bitTimes.nomTimeSeg2        = 8U; // Nominal Time segment after SP
    bitTimes.nomSynchJumpWidth  = 8U; // Nominal SJW
    bitTimes.nomRatePrescalar   = 1U; // Nominal Baud Rate Pre-scaler

    bitTimes.dataTimeSeg1       = 9U; // Data Time segment before SP
    bitTimes.dataTimeSeg2       = 8U; // Data Time segment after SP
    bitTimes.dataSynchJumpWidth = 8U; // Data S
    bitTimes.dataRatePrescalar  = 0U; // Data Baud Rate Pre-scaler
#endif

#ifdef USE_4MBAUDPS //Needs 2 termination resistors --> Propagation Delay of 200ns
    bitTimes.nomTimeSeg1        = 9U; // Nominal Time segment before SP
    bitTimes.nomTimeSeg2        = 8U; // Nominal Time segment after SP
    bitTimes.nomSynchJumpWidth  = 8U; // Nominal SJW
    bitTimes.nomRatePrescalar   = 1U; // Nominal Baud Rate Pre-scaler

    bitTimes.dataTimeSeg1       = 4U; // Data Time segment before SP
    bitTimes.dataTimeSeg2       = 3U; // Data Time segment after SP
    bitTimes.dataSynchJumpWidth = 3U; // Data S
    bitTimes.dataRatePrescalar  = 0U; // Data Baud Rate Pre-scaler
#endif

#ifdef USE_5MBAUDPS //Needs 2 termination resistors --> --> Propagation Delay of 200ns
    bitTimes.nomTimeSeg1        = 9U; // Nominal Time segment before SP
    bitTimes.nomTimeSeg2        = 8U; // Nominal Time segment after SP
    bitTimes.nomSynchJumpWidth  = 8U; // Nominal SJW
    bitTimes.nomRatePrescalar   = 1U; // Nominal Baud Rate Pre-scaler

    bitTimes.dataTimeSeg1       = 3U; // Data Time segment before SP
    bitTimes.dataTimeSeg2       = 2U; // Data Time segment after SP
    bitTimes.dataSynchJumpWidth = 2U; // Data S
    bitTimes.dataRatePrescalar  = 0U; // Data Baud Rate Pre-scaler
#endif

    //
    // Wait for memory initialization to happen.
    //
    while(FALSE == MCAN_isMemInitDone(MCANA_DRIVER_BASE))
    {
    }

    //
    // Put MCAN in SW initialization mode.
    //
    MCAN_setOpMode(MCANA_DRIVER_BASE, MCAN_OPERATION_MODE_SW_INIT);

    //
    // Wait till MCAN is not initialized.
    //
    while (MCAN_OPERATION_MODE_SW_INIT != MCAN_getOpMode(MCANA_DRIVER_BASE))
    {}

    //
    // Initialize MCAN module.
    //
    MCAN_init(MCANA_DRIVER_BASE, &initParams);

    //
    // Configure Bit timings.
    //
    MCAN_setBitTime(MCANA_DRIVER_BASE, &bitTimes);

    //
    // Configure Message RAM Sections
    //
    MCAN_msgRAMConfig(MCANA_DRIVER_BASE, &msgRAMConfigParams);

    //
    // Initialize Rx Buffer Configuration parameters.
    //
    stdFiltelem.sfec               = 0x7U; // Store into Rx Buffer
                                           // configuration of SFT[1:0] ignored

    //
    // Configuring SFID2[10:9] = 0 to store message in RX Buffer
    // SFID2[5:0] defines which RX Buffer element received message
    // will be stored into (among upto 64 buffer elements)
    // Frames matching different filter elements cannot be
    // configured to be stored in the same buffer element
    // i.e. SFID2[5:0] HAS to be distinct for every filter element
    //


    //
    // Configure Standard ID filter element 0
    //
    stdFiltelem.sfid2              = 0;//0x0U; // Standard Filter ID 0. --> Buffer NUmber
    stdFiltelem.sfid1              = LL_MCAN_RX_ADDR; //0x0U; // CAN Msg Addr

    MCAN_addStdMsgIDFilter(MCANA_DRIVER_BASE, 0U, &stdFiltelem);

    //
    // Take MCAN out of the SW initialization mode
    //
    MCAN_setOpMode(MCANA_DRIVER_BASE, MCAN_OPERATION_MODE_NORMAL);

    while (MCAN_OPERATION_MODE_NORMAL != MCAN_getOpMode(MCANA_DRIVER_BASE))
    {

    }

#ifdef USE_MCAN_WITH_ISR
    //
    // Enable Interrupts.
    //
    MCAN_enableIntr(MCANA_DRIVER_BASE, MCAN_INTR_MASK_ALL, 1U);

    //
    // Select Interrupt Line.
    //
    MCAN_selectIntrLine(MCANA_DRIVER_BASE, MCAN_INTR_MASK_ALL, MCAN_INTR_LINE_NUM_1);

    //
    // Enable Interrupt Line.
    //
    MCAN_enableIntrLine(MCANA_DRIVER_BASE, MCAN_INTR_LINE_NUM_1, 1U);
#endif
}

//*******************************************************************************************************************************************//
// ISR
//*******************************************************************************************************************************************//
/**
 * @brief: This is Interrupt Service Routine for MCAN interrupt 1.
 */
__interrupt void MCANIntr1ISR(void)
{

    MCAN_RxNewDataStatus newData;

    intrStatus = MCAN_getIntrStatus(MCANA_DRIVER_BASE);

    //
    // Clear the interrupt Status.
    //
    MCAN_clearIntrStatus(MCANA_DRIVER_BASE, intrStatus);

    //
    //  Clearing the interrupt lineNum
    //
    MCAN_clearInterrupt(MCANA_DRIVER_BASE, 0x2);

    //
    //  Check to see if the interrupt is caused by a message being
    //  received in dedicated RX Buffers
    //
    if((MCAN_IR_DRX_MASK & intrStatus) == MCAN_IR_DRX_MASK)
    {
        //
        // Read the NewData registers
        //
        MCAN_getNewDataStatus(MCANA_DRIVER_BASE, &newData);

        //  If message is received in buffer element 0
        if((newData.statusLow & (1UL << 0)) != 0)
        {
            MCAN_readMsgRam(MCANA_DRIVER_BASE, MCAN_MEM_TYPE_BUF, 0U, //0U for Buffer 0 read
                          0, &rxMsg);

            mcan_handler.mcan_is_rx = 1;

            mcan_rx_message_counter++;
        }
        //
        //  Clearing the NewData registers
        //
        MCAN_clearNewDataStatus(MCANA_DRIVER_BASE, &newData);
    }
    else if((MCAN_IR_TEFL_MASK & intrStatus) == MCAN_IR_TEFL_MASK)
    {
        //TX
    }
    else
    {
        mcan_handler.mcan_err_cntr++;
    }

    mcan_isr_cntr++;

    //
    // Acknowledge this interrupt located in group 9
    //
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}
