/**
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2024-12-01
 *
 * @attention
 *
 *  Copyright (C) 2024-2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes ***************************************************************/
#include "main.h"

/* Private includes *******************************************************/
#include "apm32f4xx_device_cfg.h"
#include <string.h>
#include <stdbool.h>
#include "log.h"

/* Private macro **********************************************************/

/* Define firmware to switch between 1st board or 2nd board */

/* Private typedef ********************************************************/
/**
 * @brief   LIN Message structure definition
 */
typedef struct
{
    uint8_t     ID;          /*!< Specifies the id */
    uint8_t     data[8];     /*!< Specifies the data */
    uint8_t     CheckSum;    /*!< Specifies the data */
} LIN_Message_T;

/* Private variables ******************************************************/
volatile LIN_Message_T MasterSend;
volatile LIN_Message_T MasterReceived;

volatile uint8_t MasterReceivedProces = 0;
volatile uint8_t MasterReceiveDataProces = 0;
volatile uint8_t MasterReceivedOverFlag = 0;

LIN_Message_T SlaveSend;
LIN_Message_T SlaveReceived;

volatile uint8_t SlaveReceivedProces = 0;
volatile uint8_t SlaveReceivedDataProces = 0;
volatile uint8_t SlaveReceivedflag = 0;
volatile uint8_t SlaveReceivedOverFlag = 0;

volatile uint32_t tick = 0;

static uint8_t USART2_ReceiveData = 0;
static uint8_t USART3_ReceiveData = 0;

/* Private function prototypes ********************************************/
void LIN_MasterSendSlave(void);
void LIN_MasterReadSlave(void);

uint8_t LIN_CheckPID(uint8_t id);
uint8_t LIN_Checksum(uint8_t id, uint8_t* data);

void LIN_SendHead(UART_HandleTypeDef* huart, LIN_Message_T LINMessage);
void LIN_SendAnswer(UART_HandleTypeDef* huart, LIN_Message_T LINMessage);

uint8_t LIN_SlaveReceivedProces(void);
uint8_t LIN_MasterReceivedProces(void);

/* External variables *****************************************************/
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;

/* External functions *****************************************************/

/**
 * @brief   Main program
 *
 * @param   None
 *
 * @retval  None
 */
int main(void)
{
    /* Device configuration */
    DAL_DeviceConfig();

    /* Output a message on Hyperterminal using printf function */
    LOG_Print("\r\n This example provides a basic communication USARTx LIN receive and transmit mode.\r\n");
    LOG_Print(">> SystemCoreClock: %d\r\n", SystemCoreClock);

    DAL_UART_Receive_IT(&huart2, &USART2_ReceiveData, 1);
    DAL_UART_Receive_IT(&huart3, &USART3_ReceiveData, 1);

    LIN_MasterSendSlave();
    LIN_MasterReadSlave();

    /* Infinite loop */
    while (1)
    {
        BOARD_LED_Toggle(LED2);
        DAL_Delay(500);
    }
}

/* Private functions ******************************************************/
/*!
 * @brief       The master sends data to the slave test function
 *
 * @param       None
 *
 * @retval      None
 *
 */

void LIN_MasterSendSlave(void)
{
    MasterSend.ID = 0x23;

    /* The following data is the data sent from the master to the slave. */
    MasterSend.data[0] = 0x11;
    MasterSend.data[1] = 0x22;
    MasterSend.data[2] = 0x33;
    MasterSend.data[3] = 0x44;
    MasterSend.data[4] = 0x55;
    MasterSend.data[5] = 0x66;
    MasterSend.data[6] = 0x77;
    MasterSend.data[7] = 0x88;

    LIN_SendHead(&huart2, MasterSend);
    LIN_SendAnswer(&huart2, MasterSend);
    
    while (SlaveReceivedOverFlag == 0);

    DAL_Delay(500);
    LOG_Print("\r\nThe master sends data to the slave.\r\n");
    LOG_Print("Master Send ID : 0x%X ,\r\n", MasterSend.ID);
    LOG_Print("Slave Received ID : 0x%X ,\r\n", SlaveReceived.ID&0x3f);

    LOG_Print("Master Send Data : ");

    for (uint8_t i=0; i<8; i++)
    {
        LOG_Print("0x%X ,", MasterSend.data[i]);
    }

    LOG_Print(" CheckSum : 0x%X \r\n",  LIN_Checksum(MasterSend.ID, (uint8_t*)MasterSend.data));

    LOG_Print("Slave Received Data : ");

    for (uint8_t i=0; i<8; i++)
    {
        LOG_Print("0x%X ,", SlaveReceived.data[i]);
    }

    LOG_Print(" CheckSum : 0x%X \r\n",  SlaveReceived.CheckSum);

    if (LIN_SlaveReceivedProces() == SUCCESS)
    {
        LOG_Print("The master sends data to the slave successfully.\r\n");
    }
    else
    {
        LOG_Print("The master fails to send data to the slave.\r\n");
    }
}

/*!
 * @brief       Master read slave data test function
 *
 * @param       None
 *
 * @retval      None
 *
 */
void LIN_MasterReadSlave(void)
{
    MasterSend.ID = 0x33;

    /* The following data is slave data read by master. */
    SlaveSend.data[0] = 0xFF;
    SlaveSend.data[1] = 0xDD;
    SlaveSend.data[2] = 0xCC;
    SlaveSend.data[3] = 0xBB;
    SlaveSend.data[4] = 0xAA;
    SlaveSend.data[5] = 0x99;
    SlaveSend.data[6] = 0x88;
    SlaveSend.data[7] = 0x77;

    LIN_SendHead(&huart2, MasterSend);

    while (MasterReceivedOverFlag == 0);

    LOG_Print("\r\nThe master reads the slave data.\r\n");
    LOG_Print("Master Send ID : 0x%X ,\r\n", MasterSend.ID);
    LOG_Print("Slave Received ID : 0x%X ,\r\n", SlaveReceived.ID&0x3f);

    LOG_Print("Slave Send Data : ");

    for (uint8_t i=0; i<8; i++)
    {
        LOG_Print("0x%X ,", SlaveSend.data[i]);
    }

    LOG_Print("CheckSum : 0x%X \r\n",  LIN_Checksum(MasterSend.ID, SlaveSend.data));

    LOG_Print("Master Received Data :");

    for (uint8_t i=0; i<8; i++)
    {
        LOG_Print("0x%X ,", MasterReceived.data[i]);
    }

    LOG_Print("CheckSum : 0x%X \r\n", MasterReceived.CheckSum);

    if (LIN_MasterReceivedProces() == SUCCESS)
    {
        LOG_Print("The master reads the slave data and the verification is successful.\r\n");
    }
    else
    {
        LOG_Print("The master reads the slave data and the verification is failed.\r\n");
    }
}

/*!
 * @brief       Transmits break characters
 *
 * @param       usart: Select the USART or the UART peripheral
 *
 * @retval      None
 *
 */
void LIN_SendBreak(UART_HandleTypeDef* huart)
{
    DAL_LIN_SendBreak(huart);
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       usart: Select the USART or the UART peripheral
 *
 * @retval      None
 *
 */
void LIN_SendSyncSegment(UART_HandleTypeDef* huart)
{
    uint8_t data = 0x55;
    DAL_UART_Transmit(huart, &data, 1, DAL_MAX_DELAY);
}

/*!
 * @brief       The master header frame sending function
 *
 * @param       usart: Select the USART or the UART peripheral
 *
 * @param       LINMessage: LIN Message strucstruct @LIN_Message_T
 *
 * @retval      None
 *
 */
void LIN_SendHead(UART_HandleTypeDef* huart, LIN_Message_T LINMessage)
{
    LIN_SendBreak(huart);
    LIN_SendSyncSegment(huart);

    uint8_t data = LIN_CheckPID(LINMessage.ID);
    DAL_UART_Transmit(huart, &data, 1, DAL_MAX_DELAY);
}

/*!
 * @brief       ID check
 *
 * @param       id: frame ID
 *
 * @retval      None
 *
 */
uint8_t LIN_CheckPID(uint8_t id)
{
    uint8_t returnpid ;
    uint8_t P0 ;
    uint8_t P1 ;

    P0 = (((id)^(id>>1)^(id>>2)^(id>>4))&0x01)<<6 ;
    P1 = ((~((id>>1)^(id>>3)^(id>>4)^(id>>5)))&0x01)<<7 ;

    returnpid = id|P0|P1 ;

    return returnpid ;
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       id: frame ID
 *
 * @param       data: frame data
 *
 * @retval      None
 *
 */
uint8_t LIN_Checksum(uint8_t id, uint8_t* data)
{
    uint8_t t ;
    uint16_t sum ;

    sum = data[0];

    for (t=1; t<8; t++)
    {
        sum += data[t];

        if (sum&0xff00)
        {
            sum&=0x00ff;
            sum+=1;
        }
    }

    /* In the case of diagnostic frames, the classic checksum is used. */
    if (id != 0x3C)
    {
        sum+=LIN_CheckPID(id);

        if (sum&0xff00)
        {
            sum&=0x00ff;
            sum+=1;
        }
    }

    sum = ~sum;
    return (uint8_t)sum ;
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       usart: Select the USART or the UART peripheral
 *
 * @param       data: frame data
 *
 * @retval      None
 *
 */
void LIN_TxData(UART_HandleTypeDef* huart, uint8_t* data)
{
    uint8_t t, txdata ;

    for (t=0; t<8; t++)
    {
        txdata = data[t];

        DAL_UART_Transmit(huart, &txdata, 1, DAL_MAX_DELAY);
    }
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       usart: Select the USART or the UART peripheral
 *
 * @param       LINMessage: LIN Message strucstruct @LIN_Message_T
 *
 * @retval      None
 *
 */
void LIN_SendAnswer(UART_HandleTypeDef* huart, LIN_Message_T LINMessage)
{
    uint8_t data = LIN_Checksum(LINMessage.ID, LINMessage.data);
    LIN_TxData(huart, LINMessage.data);
    
    DAL_UART_Transmit(huart, &data, 1, DAL_MAX_DELAY);
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       None
 *
 * @retval      ERROR/SUCCESS: data verify is failed/successful.
 *
 */
uint8_t LIN_SlaveReceivedProces(void)
{

    if (MasterSend.ID != (SlaveReceived.ID&0x3f))
    {
        return ERROR;
    }

    for (uint8_t i =0; i<8; i++)
    {
        if (MasterSend.data[i] != SlaveReceived.data[i])
        {
            return ERROR;
        }
    }

    if (LIN_Checksum(MasterSend.ID, (uint8_t*)MasterSend.data) != SlaveReceived.CheckSum)
    {
        return ERROR;
    }

    return SUCCESS;
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       None
 *
 * @retval      ERROR/SUCCESS: data verify is failed/successful.
 *
 */
uint8_t LIN_MasterReceivedProces(void)
{
    if (MasterSend.ID != (SlaveReceived.ID&0x3f))
    {
        return ERROR;
    }

    for (uint8_t i =0; i<8; i++)
    {
        if (SlaveSend.data[i] != MasterReceived.data[i])
        {
            return ERROR;
        }
    }

    uint8_t checksum = MasterReceived.CheckSum;
    if (LIN_Checksum(MasterSend.ID, SlaveSend.data) != checksum)
    {
        return ERROR;
    }

    return SUCCESS;
}

/*!
 * @brief       Transmits Sync Segment
 *
 * @param       None
 *
 * @retval      ERROR/SUCCESS: data verify is failed/successful.
 *
 */
void DAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2)
    {
        MasterReceivedOverFlag = 0;

        if (MasterReceivedProces == 0)
        {
            if (MasterReceiveDataProces<8)
            {
                MasterReceived.data[MasterReceiveDataProces] = USART2_ReceiveData ;
                MasterReceiveDataProces += 1 ;

                if (MasterReceiveDataProces == 8)
                {
                    MasterReceiveDataProces = 0 ;
                    MasterReceivedProces = 1;
                }
            }
        }
        else
        {
            MasterReceived.CheckSum = USART2_ReceiveData;
            MasterReceivedProces = 0;
            MasterReceivedOverFlag = 1;
        }

        DAL_UART_Receive_IT(&huart2, &USART2_ReceiveData, 1);
    }
    else if (huart->Instance == USART3)
    {
        uint8_t ReceiveID;

        SlaveReceivedOverFlag = 0;

        if (SlaveReceivedProces == 0)
        {
            /* Sync frame processing. */
            if (USART3_ReceiveData == 0x55)
            {
                SlaveReceivedProces = 1 ;
            }
        }
        else if (SlaveReceivedProces == 1)
        {
            SlaveReceived.ID = USART3_ReceiveData ;
            ReceiveID = SlaveReceived.ID&0x3f ;

            /* The master reads the slave data */
            if (ReceiveID == 0x33)
            {
                uint8_t data = LIN_Checksum(ReceiveID, SlaveSend.data);
                LIN_TxData(&huart3, SlaveSend.data);

                DAL_UART_Transmit(&huart3, &data, 1, DAL_MAX_DELAY);
                SlaveReceivedProces = 0 ;
            }

            SlaveReceivedflag = 1 ;
            SlaveReceivedProces = 2 ;
        }
        else if (SlaveReceivedProces == 2)
        {
            /* data frame processing */
            if (SlaveReceivedDataProces<8)
            {
                SlaveReceived.data[SlaveReceivedDataProces] = USART3_ReceiveData ;
                SlaveReceivedDataProces += 1 ;

                if (SlaveReceivedDataProces == 8)
                {
                    SlaveReceivedDataProces = 0 ;
                    SlaveReceivedProces = 3 ;
                    DAL_UART_Receive_IT(&huart3, &USART3_ReceiveData, 1);
                    return ;
                }
            }
        }
        else if (SlaveReceivedProces == 3)
        {
            /* Check frame processing */
            SlaveReceived.CheckSum = USART3_ReceiveData;
            SlaveReceivedProces = 0 ;
            SlaveReceivedOverFlag = 1;
        }

        DAL_UART_Receive_IT(&huart3, &USART3_ReceiveData, 1);
    }
}

