/* USER CODE BEGIN Header */
/* you can remove the copyright */

/*
 *  Copyright 2020-2023 Yuntu Microelectronics Co., Ltd.
 *  All rights reserved.
 * 
 *  SPDX-License-Identifier: BSD-3-Clause
 * 
 * @file main.c
 * @brief 
 * 
 */

/* USER CODE END Header */
#include "sdk_project_config.h"
/* Includes ------------------------------------------------------------------*/

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "printf.h"
#include "flexcan_hw_access.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* CAN FD mode switch: uncomment for CAN FD, comment out for classic CAN */
#define CAN_FD_MODE

#ifdef CAN_FD_MODE
    /* CAN FD configuration */
    #define CAN_PAYLOAD_SIZE    FLEXCAN_PAYLOAD_SIZE_64  /* or 16/32/64 */
    #define CAN_DATA_LEN        (64U)
    #define CAN_FD_ENABLE       true
    #define CAN_BRS_ENABLE      true
#else
    /* Classic CAN configuration */
    #define CAN_PAYLOAD_SIZE    FLEXCAN_PAYLOAD_SIZE_8
    #define CAN_DATA_LEN        (8U)
    #define CAN_FD_ENABLE       false
    #define CAN_BRS_ENABLE      false
#endif
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* The instance of CAN0 silk-screen on EVB */
#define CAN0_INST (0U)
/* The instance of CAN1 silk-screen on EVB */
#define CAN1_INST (1U)

#define RX_STD_MSG_ID  (0x02UL)
#define RX_STD_MAILBOX (0x00UL)
#define RX_EXTMSG_ID   (0x70012UL)
#define RX_EXT_MAILBOX (0x02UL)
/* If self-test, TX ID need match RX ID */
#define TX_MSG_ID      (0x03UL)
#define TX_MAILBOX     (0x01UL)
#define TX_EXTMSG_ID   (0x70012UL)
#define TX_EXT_MAILBOX (0x03UL)
#define LED0_TOGGLE()  (PINS_DRV_TogglePins(GPIOE, (pins_channel_type_t)1 << 16U))
#define LED1_TOGGLE()  (PINS_DRV_TogglePins(GPIOA, (pins_channel_type_t)1 << 20U))

#define TEST_COUNT (10U)
/* Message Buffer size in words (arbitration 2 words + payload)
 * Classic CAN (8B): 4 words;  CAN FD 64B: 18 words */
#ifdef CAN_FD_MODE
    #define MB_SIZE_WORDS   (18U)
#else
    #define MB_SIZE_WORDS   (4U)
#endif
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* CAN0 configuration */
/* Define receive buffer */
flexcan_msgbuff_t rxStdMsg1;
flexcan_msgbuff_t rxExtMsg1;
/* Prepare message to be sent */
flexcan_msgbuff_t txMsg1 = {
    .cs = 0U,
    .msgId = TX_MSG_ID,
    .data[0] = 0x00,
    .dataLen = 64U,
};

/* CAN1 configuration */
/* Define receive buffer */
flexcan_msgbuff_t rxStdMsg2;
flexcan_msgbuff_t rxExtMsg2;
/* Prepare message to be sent */
flexcan_msgbuff_t txMsg2 = {
    .cs = 0U,
    .msgId = TX_MSG_ID,
    .data[0] = 0x80,
    .dataLen = 1U,
};

volatile bool canError = false;

static CAN_Type * const flexcanBase[] = CAN_BASE_PTRS;
/* USER CODE END PV */

/* Private function declare --------------------------------------------------*/
/* USER CODE BEGIN PFDC */
void Can_Init(uint8_t instance)
{
    DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
    CAN_Type * base = flexcanBase[instance];
    base->MCR |= CAN_MCR_MDIS_MASK; /* MDIS=1: Disable module before selecting clock */
    base->CTRL1 &= ~CAN_CTRL1_CLKSRC_MASK; /* CLKSRC=0: Clock Source = oscillator */
    base->MCR &= ~CAN_MCR_MDIS_MASK; /* MDIS=0; Enable module config. (Sets FRZ, HALT) */

    while (!((base->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT))
    {
    }
    /* Wait for FRZACK=1 on freeze mode entry/exit */

    /* === SDK-style hardware init (soft reset + abort enable) === */
    base->MCR = (base->MCR & ~CAN_MCR_SOFTRST_MASK) | CAN_MCR_SOFTRST(1U);
    while (((base->MCR & CAN_MCR_SOFTRST_MASK) >> CAN_MCR_SOFTRST_SHIFT) != 0U) {}
    base->MCR = (base->MCR & ~CAN_MCR_AEN_MASK) | CAN_MCR_AEN(1U);

    /* Clear all MBs */
    for (uint32_t i = 0; i < 128 * 4; i++)
    {
        base->RAM[i] = 0;
    }

    /* Disable all MB interrupts and clear flags */
    base->IMASK1 = 0x0U;
    base->IFLAG1 = 0xFFFFFFFFU;

    /* Clear all error interrupt flags */
    base->ESR1 = 0x3B0006U;

#ifdef CAN_FD_MODE
    /* === Init CANFD === */
    /* 1. Enable FD. This MCU uses BTE + enhanced timing registers in the SDK. */
    base->MCR = (base->MCR & ~CAN_MCR_FDEN_MASK) | CAN_MCR_FDEN(1U);
    base->CTRL2 |= CAN_CTRL2_BTE(1U);
    base->ETDC &= ~(CAN_ETDC_ETDCEN_MASK | CAN_ETDC_ETDCOFF_MASK);

    /* 2. Configure CTRL1 (loopback and other non-timing bits) */
    base->CTRL1 = (base->CTRL1 & ~(CAN_CTRL1_LPB_MASK | CAN_CTRL1_LOM_MASK));

    /* 3. Configure ENCBT/EPRS arbitration timing, same as flexcanInitConfig1.bitrate */
    base->ENCBT = CAN_ENCBT_NTSEG1(4U + 7U + 1U)
                | CAN_ENCBT_NTSEG2(1U)
                | CAN_ENCBT_NRJW(1U);
    base->EPRS = (base->EPRS & ~CAN_EPRS_ENPRESDIV_MASK)
               | CAN_EPRS_ENPRESDIV(2U);

    /* 4. Configure EDCBT/EPRS data timing, same as flexcanInitConfig1.bitrate_cbt */
    base->EDCBT = CAN_EDCBT_DTSEG1(6U + 2U)
                | CAN_EDCBT_DTSEG2(1U)
                | CAN_EDCBT_DRJW(1U);
    base->EPRS = (base->EPRS & ~CAN_EPRS_EDPRESDIV_MASK)
               | CAN_EPRS_EDPRESDIV(0U);

    /* 5. Configure Message Buffer Size */
    base->FDCTRL = (base->FDCTRL & ~(CAN_FDCTRL_MBDSR0_MASK | CAN_FDCTRL_MBDSR1_MASK |
                                     CAN_FDCTRL_MBDSR2_MASK | CAN_FDCTRL_MBDSR3_MASK))
                 | CAN_FDCTRL_MBDSR0((uint32_t)CAN_PAYLOAD_SIZE)
                 | CAN_FDCTRL_MBDSR1((uint32_t)CAN_PAYLOAD_SIZE)
                 | CAN_FDCTRL_MBDSR2((uint32_t)CAN_PAYLOAD_SIZE)
                 | CAN_FDCTRL_MBDSR3((uint32_t)CAN_PAYLOAD_SIZE);

    /* 6. Enable ISO CAN FD (Stuff Bit Count, ISO) */
    base->CTRL2 |= CAN_CTRL2_ISOCANFDEN_MASK;

    /* 7. Enable FDRATE for BRS data phase timing */
    base->FDCTRL |= CAN_FDCTRL_FDRATE_MASK;

#else
    base->CTRL1 =  0x02DBC406; /* Configure for 500 KHz bit time for 24MHz oscillator */

    /* Time quanta freq = 16 time quanta x 500 KHz bit time= 8MHz */
    /* PRESDIV + 1 = Fclksrc/Ftq = 24 MHz/8 MHz = 3，->PRESDIV = 2*/
    /* PSEG2 = 3 */
    /* PSEG1 = 3 */
    /* PROPSEG = 6 */
    /* RJW: since Phase_Seg1 = PSEG1 + 1 = 4, so RJW ≤4，so set RJW = 3. */
    /* SMP = 0: use one bit per CAN sample */

    /* Payload size is set to default value(8 bytes) here */
    if (instance < 2)
    {
        /* CAN0 and CAN1 have 128 MBs */
        for (uint16_t i = 0; i < 128 * 4; i++)
        { 
            /* CAN0: clear 128 MBs */
            base->RAM[i] = 0; /* Clear msg buf word */
        }
    }
    else if (1 < instance && instance < 5)
    {
        /* CAN3、CAN4 and CAN5 have 64 MBs */
        for (uint8_t i = 0; i < 64 * 4; i++)
        { 
            /* Clear 64 MBs */
            base->RAM[i] = 0; /* Clear msg buf word */
        }
    }
    else
    {
        /* Other CAN controllers only have 32 MBs */
        for (uint8_t i = 0; i < 32 * 4; i++)
        { 
            /* Clear 32 MBs */
            base->RAM[i] = 0; /* Clear msg buf word */
        }
    }
#endif
    /* Clear RXMGMASK,RX14MASK,RX15MASK,RXFGMASK Regions*/
    base->RXMGMASK = 0xFFFFFFFFU;
    base->RX14MASK = 0xFFFFFFFFU;
    base->RX15MASK = 0xFFFFFFFFU;
    base->RXFGMASK = 0xFFFFFFFFU;

    for (uint32_t i = 0U; i < (512U / MB_SIZE_WORDS); i++)
    {
        base->RXIMR[i] = 0xFFFFFFFFU;
    }
        
    base->MCR = (base->MCR & ~(CAN_MCR_MAXMB_MASK | CAN_MCR_FRZ_MASK | CAN_MCR_HALT_MASK))
              | CAN_MCR_MAXMB((512U / MB_SIZE_WORDS) - 1U)
              | CAN_MCR_SRXDIS_MASK; /* Preserve FDEN/AEN, set MAXMB for actual MB count, SRXDIS=1, exit freeze */
				
    // base->IMASK1 = 0x00000003;
        
    while ((base->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT)
    {
    }
    /* Wait for FRZACK to clear (not in freeze mode) */
    while ((base->MCR & CAN_MCR_NOTRDY_MASK) >> CAN_MCR_NOTRDY_SHIFT)
    {
    }
    /* Wait for NOTRDY to clear (module ready)  */
}

/* DLC encoding: converts data length to 4-bit DLC value */
static inline uint8_t Can_DataLenToDLC(uint8_t len)
{
    if (len <= 8U)  return len;
    if (len <= 12U) return 9U;
    if (len <= 16U) return 10U;
    if (len <= 20U) return 11U;
    if (len <= 24U) return 12U;
    if (len <= 32U) return 13U;
    if (len <= 48U) return 14U;
    return 15U;
}

/* DLC decoding: converts 4-bit DLC value to actual data length (CAN FD aware) */
static inline uint8_t Can_DLCToDataLen(uint8_t dlc)
{
    if (dlc <= 8U)  return dlc;
    if (dlc == 9U)  return 12U;
    if (dlc == 10U) return 16U;
    if (dlc == 11U) return 20U;
    if (dlc == 12U) return 24U;
    if (dlc == 13U) return 32U;
    if (dlc == 14U) return 48U;
    return 64U;
}

void Can_SendFrame(uint8_t instance, flexcan_msgbuff_t frame, uint8_t mb_idx)
{
    CAN_Type * base = flexcanBase[instance];
    uint32_t offset = (uint32_t)mb_idx * MB_SIZE_WORDS;

    base->IFLAG1 = 1UL << mb_idx; /* Clear IRQ flag */

    /* Write data words to RAM in native register order */
    for (uint8_t i = 0; i < frame.dataLen; i += 4U)
    {
        uint32_t word = ((uint32_t)frame.data[i] << 24)
                      | ((uint32_t)frame.data[i + 1U] << 16)
                      | ((uint32_t)frame.data[i + 2U] << 8)
                      |  (uint32_t)frame.data[i + 3U];
        base->RAM[offset + 2U + (i / 4U)] = word;
    }

    /* Set message ID - support both standard and extended */
    if ((frame.cs & CAN_CS_IDE_MASK) != 0U)
    {
        /* Extended ID (29-bit) */
        base->RAM[offset + 1U] = frame.msgId & (CAN_ID_STD_MASK | CAN_ID_EXT_MASK);
    }
    else
    {
        /* Standard ID (11-bit) */
        base->RAM[offset + 1U] = (frame.msgId << CAN_ID_STD_SHIFT) & CAN_ID_STD_MASK;
    }

    /* Build CS register */
    uint32_t cs = ((uint32_t)Can_DataLenToDLC(frame.dataLen) << CAN_CS_DLC_SHIFT) & CAN_CS_DLC_MASK;

    /* Set IDE + SRR for extended ID frames */
    if ((frame.cs & CAN_CS_IDE_MASK) != 0U)
    {
        cs |= CAN_CS_IDE_MASK | CAN_CS_SRR_MASK;
    }

#ifdef CAN_FD_MODE
    if (CAN_FD_ENABLE)
    {
        cs |= CAN_MB_EDL_MASK;       /* FD frame flag */
        if (CAN_BRS_ENABLE)
        {
            cs |= CAN_MB_BRS_MASK;   /* Bit rate switch */
        }
    }
#endif

    cs |= (FLEXCAN_TX_DATA << CAN_CS_CODE_SHIFT) & CAN_CS_CODE_MASK;
    base->RAM[offset + 0U] = cs;
}

void Can_ConfigRxMb(uint8_t instance, uint8_t mb_idx, uint32_t msg_id, bool is_ext_id)
{
    CAN_Type * base = flexcanBase[instance];
    uint32_t offset = (uint32_t)mb_idx * MB_SIZE_WORDS;

    base->IFLAG1 = 1UL << mb_idx; /* Clear any pending IRQ */

    /* Match SDK: clean up the arbitration field area before configuring RX MB */
    base->RAM[offset + 0U] = 0U;
    base->RAM[offset + 1U] = 0U;

    uint32_t cs = 0U;

    /* Set message ID */
    if (is_ext_id)
    {
        base->RAM[offset + 1U] = msg_id & (CAN_ID_STD_MASK | CAN_ID_EXT_MASK);
        cs |= CAN_CS_IDE_MASK; /* SDK sets IDE and clears SRR for extended RX MB */
    }
    else
    {
        base->RAM[offset + 1U] = (msg_id << CAN_ID_STD_SHIFT) & CAN_ID_STD_MASK;
    }

    /* Activate mailbox for reception (CODE = EMPTY = 0x4) */
    cs |= (FLEXCAN_RX_EMPTY << CAN_CS_CODE_SHIFT) & CAN_CS_CODE_MASK;
    base->RAM[offset + 0U] = cs;
    base->IMASK1 |= 1UL << mb_idx; /* Enable MB interrupt */
}

status_t Can_ReceiveFrame(uint8_t instance, uint8_t mb_idx, flexcan_msgbuff_t *frame)
{
    CAN_Type * base = flexcanBase[instance];
    uint32_t offset = (uint32_t)mb_idx * MB_SIZE_WORDS;

    /* Poll the MB flag first. Do not read the MB while it is still EMPTY,
     * otherwise the RX MB can be locked before a frame arrives. */
    if ((base->IFLAG1 & (1UL << mb_idx)) == 0U)
    {
        return STATUS_ERROR;
    }

    uint32_t cs = base->RAM[offset + 0U];
    uint32_t code = (cs & CAN_CS_CODE_MASK) >> CAN_CS_CODE_SHIFT;

    /* Check if mailbox has received data */
    if ((code != (uint32_t)FLEXCAN_RX_FULL) && (code != (uint32_t)FLEXCAN_RX_OVERRUN))
    {
        return STATUS_ERROR; /* No data available */
    }

    /* Read CS and get DLC (decode DLC to actual length for CAN FD) */
    frame->cs = cs;
    frame->dataLen = Can_DLCToDataLen((uint8_t)((cs & CAN_CS_DLC_MASK) >> CAN_CS_DLC_SHIFT));
    if (frame->dataLen > CAN_DATA_LEN)
    {
        frame->dataLen = CAN_DATA_LEN;
    }

    /* Read message ID */
    if ((cs & CAN_CS_IDE_MASK) != 0U)
    {
        frame->msgId = base->RAM[offset + 1U] & (CAN_ID_STD_MASK | CAN_ID_EXT_MASK);
    }
    else
    {
        frame->msgId = (base->RAM[offset + 1U] & CAN_ID_STD_MASK) >> CAN_ID_STD_SHIFT;
    }

    /* Read data words */
    for (uint8_t i = 0; i < frame->dataLen; i += 4U)
    {
        uint32_t word = base->RAM[offset + 2U + (i / 4U)];
        frame->data[i]      = (uint8_t)(word >> 24);
        frame->data[i + 1U] = (uint8_t)(word >> 16);
        frame->data[i + 2U] = (uint8_t)(word >> 8);
        frame->data[i + 3U] = (uint8_t)(word);
    }

    /* Unlock Rx mailbox by reading TIMER (required by FlexCAN) */
    (void)base->TIMER;

    base->IFLAG1 = 1UL << mb_idx; /* Clear received MB flag */

    /* Re-arm: preserve IDE and set CODE back to EMPTY (0x4) for next reception */
    uint32_t rearm_cs = (FLEXCAN_RX_EMPTY << CAN_CS_CODE_SHIFT) & CAN_CS_CODE_MASK;
    rearm_cs |= cs & CAN_CS_IDE_MASK;
    base->RAM[offset + 0U] = rearm_cs;

    return STATUS_SUCCESS;
}
/* USER CODE END PFDC */
static void Board_Init(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */


/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{
    /* USER CODE BEGIN 1 */
    /* USER CODE END 1 */ 
    Board_Init();
    /* USER CODE BEGIN 2 */
    Can_Init(1);
    /* Configure RX mailboxes (register operation) */
    Can_ConfigRxMb(1, RX_STD_MAILBOX, RX_STD_MSG_ID, false);
    Can_ConfigRxMb(1, RX_EXT_MAILBOX, RX_EXTMSG_ID, true);
    /* Fill TX data buffer */
    txMsg1.dataLen = CAN_DATA_LEN;
    for (uint8_t i = 0; i < CAN_DATA_LEN; i++)
    {
        txMsg1.data[i] = i % 256;
    }

    uint32_t txCounter = 0;
    while (1)
    {
        /* Check and receive standard ID frame */
        if (Can_ReceiveFrame(1, RX_STD_MAILBOX, &rxStdMsg1) == STATUS_SUCCESS)
        {
            PRINTF("RX_STD: ID=0x%03X, len=%u, data[0]=0x%02X\r\n",
                                 rxStdMsg1.msgId, rxStdMsg1.dataLen, rxStdMsg1.data[0]);
        }

        /* Check and receive extended ID frame */
        if (Can_ReceiveFrame(1, RX_EXT_MAILBOX, &rxExtMsg1) == STATUS_SUCCESS)
        {
            PRINTF("RX_EXT: ID=0x%05X, len=%u, data[0]=0x%02X\r\n",
                                 rxExtMsg1.msgId, rxExtMsg1.dataLen, rxExtMsg1.data[0]);
        }
        
        /* Send periodic message */
        txMsg1.data[0] = (uint8_t)(txCounter++);
        Can_SendFrame(1, txMsg1, TX_MAILBOX);
        OSIF_TimeDelay(300);
    }
    /* USER CODE END 2 */
}

static void Board_Init(void)
{
    CLOCK_SYS_Init(g_clockManConfigsArr,CLOCK_MANAGER_CONFIG_CNT,g_clockManCallbacksArr,CLOCK_MANAGER_CALLBACK_CNT);
    if(STATUS_SUCCESS != CLOCK_SYS_UpdateConfiguration(CLOCK_MANAGER_ACTIVE_INDEX,CLOCK_MANAGER_POLICY_AGREEMENT))
    {
        /* USER CODE BEGIN ERROR_HANDLER 1 */
        SystemSoftwareReset();
        /* USER CODE END ERROR_HANDLER 1 */
    }
    PINS_DRV_Init(NUM_OF_CONFIGURED_PINS0,g_pin_mux_InitConfigArr0);
    UTILITY_PRINT_Init();
    // FLASH_ECC_Fault_Init(&flash_ecc_fault_config0,&flash_ecc_fault_config0_State);
}

/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
