/**
  *****************************************************************************************************
  * @file    main.c
  * @author  Kiwi Software Team
  * @brief   This example code demonstrates how CAN works.
  *
  Copyright (c) 2024, Kiwi Instruments Co,. Ltd.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *
  *   3. Neither the name of the copyright holder nor the names of its contributors
  *      may be used to endorse or promote products derived from this software without
  *      specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************************************
  */

#include "main.h"
#include "kpm32xx_demoboard_ddl.h"


/**
  * This example demos the master board communicate with slave board with CAN.
  * If define MASTER_BOARD, it will run the master software, else run the slave software.
  */
#define MASTER_BOARD      1

uint32_t can_event = 0;


/* Set filer initial value */
CAN_Filter_T filter[CAN_MAX_FILTER_NUM] =
{
	{
		.acceptCode  = 0xfffff100,
		.maskCode    = 0xffffff00,
	},
	{
		.acceptCode  = 0x66fff124,
		.maskCode    = 0xffffffff,
	},
};


#if defined (__DEBUG_CONSOLE_PRINT__)
/**
  * @brief  This function is implemented for UART print PinMux configuration.
  * @param  None
  * @retval None
  */
void DebugConsole_PinMux_Config(void)
{
	/* SCI1 UART Tx */
	DDL_GPIO_Config2AltFunc(GPIOA, GPIO_PIN_7, GPIOA7_AF1_SCI1_TX_SDA_MOSI);
}
#endif


/**
  * @brief  This function is implemented for system clock configuration.
  *         where:
  *         System Clock source            = PLL (HIRC)
  *         System Clock frequency         = 100MHz
  * @param  None
  * @retval None
  */
void SystemClock_Config(void)
{
	RCC_PLLInit_T pllInitStruct    = {0};
	RCC_ClkInit_T rccClkInitStruct = {0};

	DDL_RCC_PllStructInit(&pllInitStruct);
	/* FCLK : 100MHz */
	pllInitStruct.pllPrescaler  = 4;
	if (DDL_RCC_PllConfig(&pllInitStruct) != DDL_OK)
	{
		while(1) ;
	}

	/* Select PLL as system clock source */
	rccClkInitStruct.sysClkSource  = RCC_SYSCLKSOURCE_PLLCLK;
	rccClkInitStruct.sysClkDivider = 0;
	if (DDL_RCC_ClkSrcConfig(&rccClkInitStruct) != DDL_OK)
	{
		while(1) ;
	}
}


/**
  * @brief  Process CAN event.
  * @param  None
  * @retval None
  */
static int CAN_Check_Event(void)
{
	uint32_t lostBit = 0;
	int ret = 0;
	uint8_t i = 0;
	CAN_RXHeader_T rxHeader;
	uint8_t rxData[8];
	CAN_ErrorInfo_T errInfo;

	while (1)
	{
		if (can_event & CAN_INTST_RECEIVE)
		{
			can_event &= ~CAN_INTST_RECEIVE;
			printf("	receive data success: ");
			DDL_CAN_ReadMessage(CAN, &rxHeader, rxData);
			if (rxHeader.idType == CAN_IDTYPE_STD)
				printf("ID(%x) \n", rxHeader.stdId);
			else
				printf("ID(%x) \n", rxHeader.extId);
			for (i = 0; i < rxHeader.dataLenth; i++)
			{
				printf("%x ", rxData[i]);
			}
			printf("\n");

			ret = 1;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_DATAOVERFLOW)
		{
			can_event &= ~CAN_INTST_DATAOVERFLOW;
			printf("	data overflow: ");
			DDL_CAN_ReadMessage(CAN, &rxHeader, rxData);
			for (i = 0; i < rxHeader.dataLenth; i++)
			{
				printf("%x ", rxData[i]);
			}
			printf("\n");

			ret = 2;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_TRANSMIT)
		{
			can_event &= ~CAN_INTST_TRANSMIT;
			printf("	data transmit success.\n");

			ret = 3;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_BUSERR)
		{
			can_event &= ~CAN_INTST_BUSERR;
			DDL_CAN_GetErrInfor(CAN, &errInfo);
			printf("	bus error happen.\n");
			printf("		rxErrCnt = %d.\n", errInfo.rxErrCnt);
			printf("		txErrCnt = %d.\n", errInfo.txErrCnt);
			printf("		errType = %d.\n", errInfo.errType);
			printf("		errDir = %d.\n", errInfo.errDir);
			printf("		errSegment = %d.\n", errInfo.errSegment);

			ret = -1;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_ERRPASSIVE)
		{
			can_event &= ~CAN_INTST_ERRPASSIVE;
			printf("	error passive event happen.\n");

			ret = -2;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_ERRWARNING)
		{
			can_event &= ~CAN_INTST_ERRWARNING;
			DDL_CAN_GetErrInfor(CAN, &errInfo);
			printf("	error warning event happen.\n");
			printf("		rxErrCnt = %d.\n", errInfo.rxErrCnt);
			printf("		txErrCnt = %d.\n", errInfo.txErrCnt);
			printf("		errType = %d.\n", errInfo.errType);
			printf("		errDir = %d.\n", errInfo.errDir);
			printf("		errSegment = %d.\n", errInfo.errSegment);

			/* if bus off, need to check device, then reconfigure work mode */
			if (IS_CAN_BUS_OFF(CAN))
			{
				printf("**********bus off, reconfigure mode**********\n");
				__DDL_CAN_CONFIGURE_MODE(CAN, CAN_MODE_NORMAL);
			}

			ret = -3;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_ARBITLOST)
		{
			can_event &= ~CAN_INTST_ARBITLOST;
			printf("	arbitration lost.\n");
			lostBit = DDL_CAN_GetArbitLostBit(CAN);
			if (lostBit == CAN_ARBITLOST_SRTR)
			{
				printf("	SRTR arbit lost.\n");
			}
			else if (lostBit == CAN_ARBITLOST_IDE)
			{
				printf("	IDE arbit lost.\n");
			}
			else if (lostBit == CAN_ARBITLOST_RTR)
			{
				printf("	RTR arbit lost.\n");
			}
			else
			{
				printf("	identifier arbit lost.\n");
			}

			ret = -4;
			if (can_event == 0)
			{
				break;
			}
		}

		if (can_event & CAN_INTST_ARBITLOST)
		{
			can_event &= ~CAN_INTST_ARBITLOST;
			printf("	device wake up!\n");

			ret = 0;
		}
	}

	return ret;
}


#if MASTER_BOARD
/**
  * @brief  Waiting data from CAN, if received success, send response.
  * @param  None
  * @retval None
  */
static void CAN_Transmit_Receive_Demo_Master(void)
{
	int ret = 0;
	int cnt = 0;
	CAN_TXHeader_T header;
	uint8_t txData[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};

	header.idType = CAN_IDTYPE_EXT;
	if (header.idType == CAN_IDTYPE_STD)
	{
		header.stdId = 0x100;
	}
	else
	{
		header.extId = 0x12345;
	}
	header.rtrType = CAN_RTRTYPE_NORMAL;
	header.dataLenth = 8;

	while (1)
	{
		ret = CAN_Check_Event();
		if (ret == 1)
		{
			DDL_CAN_SendNormalMessage(CAN, &header, (uint8_t *)(&txData[0]));
			if (CAN_Check_Event() == 3)
			{
				cnt++;
				if (cnt > 100)
				{
					cnt = 0;
					break;
				}
			}
		}
	}
	printf("CAN transmit and receive demo success.\n");
}
#else
/**
  * @brief  Send data to CAN and wait for response.
  * @param  None
  * @retval None
  */
static void CAN_Transmit_Receive_Demo_Slave(void)
{
	int ret = 0;
	int cnt = 0;
	CAN_TXHeader_T header;
	uint8_t txData[4] = {0x81, 0x72, 0x63, 0x54};

	header.idType = CAN_IDTYPE_EXT;
	if (header.idType == CAN_IDTYPE_STD)
	{
		header.stdId = 0x100;
	}
	else
	{
		header.extId = 0x1F081;
	}
	header.rtrType = CAN_RTRTYPE_NORMAL;
	header.dataLenth = 4;

	DDL_CAN_SendNormalMessage(CAN, &header, (uint8_t *)(&txData[0]));
	while (1)
	{
		if (CAN_Check_Event() == 3)
		{
			/* Send success */
		}

		/* wait for response */
		ret = CAN_Check_Event();
		if (ret == 1)
		{
			/* receive success */
			cnt++;
			if (cnt > 100)
			{
				cnt = 0;
				break;
			}

			DDL_CAN_SendNormalMessage(CAN, &header, (uint8_t *)(&txData[0]));
		}
	}
	printf("CAN transmit and receive demo success.\n");
}
#endif

/**
  * @brief  Config CAN pinmux.
  * @param  None
  * @retval None
  */
void CAN_Pinmux_Config(void)
{
	DDL_GPIO_Config2AltFunc(GPIOC, GPIO_PIN_3, GPIOC3_AF0_CAN_RX);
	DDL_GPIO_Config2AltFunc(GPIOC, GPIO_PIN_4, GPIOC4_AF0_CAN_TX);
	DDL_GPIO_Config2AltFunc(GPIOA, GPIO_PIN_8, GPIOA8_AF2_CAN_DBG);
}


/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
	CAN_Init_T pCANStruct;

	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	DDL_Init();
	SystemClock_Config();

	/* To make GPIO active */
	DDL_GPIO_Instance_Active();

#if defined (__DEBUG_CONSOLE_PRINT__)
	DebugConsole_PinMux_Config();
	DDL_SCI_Instance_Active(SCI1);
	DDL_SCIUart_DebugConsole_Init(SCI1);
#endif

    printf("CAN demo start:\n");

	CAN_Pinmux_Config();
	DDL_CAN_Instance_Active(CAN);
	DDL_CAN_StructInit(&pCANStruct);
	DDL_CAN_Init(CAN, &pCANStruct);
	DDL_CAN_ConfigFilter(CAN, &filter[0], 0);
	DDL_CAN_ConfigFilter(CAN, &filter[1], 1);

	DDL_CAN_IntEnable(CAN, CAN_IE_WAKEUP | CAN_IE_BUSERR | CAN_IE_ERRPASSIVE | \
			CAN_IE_ERRWARNING | CAN_IE_ARBITLOST | CAN_IE_DATAOVERRUN | CAN_IE_TRANSMIT | \
			CAN_IE_RECEIVE | CAN_IE_INTENABLE);
	NVIC_SetPriority(CAN_IRQn, 0);
	NVIC_EnableIRQ(CAN_IRQn);
	DDL_CAN_Start(CAN, &pCANStruct);

#if MASTER_BOARD
	CAN_Transmit_Receive_Demo_Master();
#else
	CAN_Transmit_Receive_Demo_Slave();
#endif

    printf("CAN demo end.\n");
}

