/*
 * @history     2021_01_29  [SDK3.2] GPIOサンプルの説明で例に挙げているGPIOポートを修正 (#2779)
 */
/*
* This program was created by AXELL CORPORATION.
* Copyright (C) 2017-2021 AXELL CORPORATION, all rights reserved.
*/
#include "sample_common.h"
#include "com.h"
#include "gpio/gpiomgr.h"

/* プロトタイプ */
static int32_t Gpio_init_module(void);
static int32_t Gpio_term_module(void);
static void Gpio_SampleHelp(void);
static void Gpio_SetPort(void);
static void Gpio_GetPort(void);
static void Gpio_DetectEdge(void);
static void Gpio_HwInit(uint8_t ch, uint8_t target, uint8_t dir);
static int32_t Gpio_WaitEdgeDetect(uint8_t ch, uint32_t target);
static void Gpio_CallbackEvent(uint8_t ch, uint32_t status);

enum {	/* 入出力方向 */
	GPIO_SMP_DIR_IN=0,
	GPIO_SMP_DIR_OUT,
};

/* Macro */
#define	EPRINT(FMT, ...)		sys_print(0, FMT,##__VA_ARGS__)
#define	PRINT(FMT, ...)			sys_print(0, FMT"\r\n",##__VA_ARGS__)
#define GPIO_ERROR(str)			sys_print(1, " ERROR:%s[%d] *%s*\r\n",__FUNCTION__, __LINE__, str)

static void (*func[])() = {
	Gpio_SampleHelp,
	Gpio_SetPort,
	Gpio_GetPort,
	Gpio_DetectEdge,
};

static uint32_t	GpioDetectEdge[4]={0};

/**
 * @brief		メイン処理
 * @param[in]	param	制御パラメータ
 * @return		なし
 * @note		なし
 */
void GPIO_main(uint8_t param)
{
	uint8_t		mode = 0;
	char		input[2+2];

	((void)param);

	Gpio_SampleHelp();	/* Help表示 */

	Gpio_init_module();

	while (mode != 0xFF) {
		EPRINT(" -- Choose sample number (00h<HELP> to FFh): ");
		COM_GetNum_Wait(input, sizeof(input));
		mode = (ASCtoBIN(input[0]) << 4)
			 | (ASCtoBIN(input[1]) << 0);
		if(0xFF == mode) {
			break;
		}
		if(mode >= (sizeof(func)/sizeof(void*))) {
			func[0](); /* Help */
		}
		else {
			func[mode]();	/* Sample func実行 */
		}
	}

	Gpio_term_module();

	return;
}

/**
 * @brief		サブ処理
 * @param[in]	param	制御パラメータ
 * @return		なし
 * @note		なし
 */
void GPIO_sub(uint32_t param)
{
	((void)param);
	/* 処理なし */
	return;
}

/**
 * @brief		モジュール初期化
 * @param[in]	なし
 * @retval		AG903_ENONE		正常終了
 * @note		なし
 */
static int32_t Gpio_init_module(void)
{
	static _Bool	GpioInit = false;
	int32_t 		result;

	if(false == GpioInit) {
		result = AG903_GPIOMgrInit(); /* Managaer 初期化 */
		if(AG903_ENONE != result) {
			GPIO_ERROR("Init");
		}
		else {
			GpioInit = true;
		}
	}

	/* タクトスイッチ接続 */
	Board_ConnectTactSwitchs(true);

	return AG903_ENONE;
}

/**
 * @brief		モジュール終了
 * @param[in]	なし
 * @retval		AG903_ENONE		正常終了
 * @note		なし
 */
static int32_t Gpio_term_module(void)
{
	/* タクトスイッチ未接続 */
	Board_ConnectTactSwitchs(false);

	return AG903_ENONE;
}

/**
 * @brief		ヘルプ表示
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Gpio_SampleHelp(void)
{
	PRINT("\t# 00 ... Help");
	PRINT("\t# 01 ... Set Port");
	PRINT("\t# 02 ... Get Port");
	PRINT("\t# 03 ... Detect Edge");
	PRINT("\t# FF ... Exit Sample");
	
	return;
}

/**
 * @brief		Port Set処理
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Gpio_SetPort(void)
{
	uint32_t	dir;
	char		chara[6];
	uint8_t		ch;
	uint8_t 	target;
	uint8_t		gpio;
	uint8_t		data;
	
	PRINT(" Set Port");
	PRINT(" input GPIO NO.xxx + DATA(1bit) ex.0571");
	EPRINT(" ");
	
	/* GPIO番号入力待ち */
	COM_GetNum_Wait(chara, 6);  /* 6 = 3(GPIO NO.)+1(DATA)+CRLF */
	gpio  = (ASCtoBIN(chara[0])*100);
	gpio += (ASCtoBIN(chara[1])*10);
	gpio += (ASCtoBIN(chara[2]));
	data  = (ASCtoBIN(chara[3]));
	ch    = (gpio>>5);
	target = (gpio&0x1F);
	
	/* HW設定 */
	Gpio_HwInit(ch, target, GPIO_SMP_DIR_OUT);
	
	/* GPIO入出力設定 (出力) */
	AG903_GPIOMgrGetDirection(ch, &dir);
	dir |= (1<<target);
	AG903_GPIOMgrSetDirection(ch, dir);
	
	/* GPIOデータ書込み */
	AG903_GPIOMgrSetPortData(ch, (data<<target), (1<<target));
	
	return;
}

/**
 * @brief		Port Get処理
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Gpio_GetPort(void)
{
	uint32_t	dir;
	uint32_t	data;
	char		chara[6];
	uint8_t		ch;
	uint8_t 	target;
	uint8_t		gpio;
	
	PRINT(" Get Port");
	PRINT(" input GPIO NO.xxx ex.057");
	EPRINT(" ");
	
	/* GPIO番号入力待ち */
	COM_GetNum_Wait(chara, 5);  /* 5 = 3(GPIO NO.)+CRLF */
	gpio  = (ASCtoBIN(chara[0])*100);
	gpio += (ASCtoBIN(chara[1])*10);
	gpio += (ASCtoBIN(chara[2]));
	ch = (gpio>>5);
	target = (gpio&0x1F);
	
	/* HW設定 */
	Gpio_HwInit(ch, target, GPIO_SMP_DIR_IN);

	/* GPIO入出力設定 (入力) */
	AG903_GPIOMgrGetDirection(ch, &dir);
	dir &= ~(1<<target);
	AG903_GPIOMgrSetDirection(ch, dir);
	
	/* GPIOデータ読込み */
	AG903_GPIOMgrGetPortData(ch, &data);
	data >>= target;
	data  &= 0x01;
	
	PRINT(" GPIO_%03d = %d", gpio, data);
	
	return;
}

/**
 * @brief		エッジ検出処理
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Gpio_DetectEdge(void)
{
	uint32_t	dir;
	char		chara[6];
	uint8_t		ch;
	uint8_t 	target;
	uint8_t		gpio;
	uint8_t		polarity;
	
	PRINT(" Detect Edge");
	PRINT(" input GPIO NO.xxx + Edge Polarity ex.1041");
	EPRINT(" ");
	
	/* GPIO番号入力待ち */
	COM_GetNum_Wait(chara, 6);  /* 6 = 3(GPIO NO.)+1(Edge Polarity)CRLF */
	gpio  = (ASCtoBIN(chara[0])*100);
	gpio += (ASCtoBIN(chara[1])*10);
	gpio += (ASCtoBIN(chara[2]));
	polarity = (ASCtoBIN(chara[3]));
	ch = (gpio>>5);
	target = (gpio&0x1F);
	
	/* HW設定 */
	Gpio_HwInit(ch, target, GPIO_SMP_DIR_IN);

	/* GPIO入出力設定 (入力) */
	AG903_GPIOMgrGetDirection(ch, &dir);
	dir &= ~(1<<target);
	AG903_GPIOMgrSetDirection(ch, dir);

	/* コールバック設定 */
	AG903_GPIOMgrSetCallback(ch, (1<<target), Gpio_CallbackEvent);

	/* エッジ検出設定 */
	GpioDetectEdge[ch] &= ~(1<<target);
	AG903_GPIOMgrClearEdgeDetect(ch, (1<<target));		 /* ステータスクリア */
	if(0==polarity) {
		AG903_GPIOMgrSetNegativeDetect(ch, (1<<target)); /* 立ち下がり */
	}
	else {
		AG903_GPIOMgrSetPositiveDetect(ch, (1<<target)); /* 立ち上がり */
	}
	AG903_GPIOMgrSetMask(ch, ~(1<<target));				 /* マスク解除 */
	
	/* エッジ検出待ち〜終了 */
	PRINT(" Wait Edge");
	Gpio_WaitEdgeDetect(ch, (1<<target));
	PRINT(" Detect Edge");
	AG903_GPIOMgrSetMask(ch, (1<<target));				/* マスク設定 */
	
	return;
}

/**
 * @brief		HW設定
 * @param[in]	dir		入出力方向
 * @return		なし
 * @note		使用するHW環境(基板,FPGA ver.,,,)によって異なる可能性があります.
 * @note		使用するGPIOは予めAG903_SSCPrmSetPinGpioEnable()でGPIO選択しておくこと。
 */
static void Gpio_HwInit(uint8_t ch, uint8_t target, uint8_t dir)
{
	uint8_t		gpio;

	gpio  = (ch<<5)+target;
	if((104<=gpio) || (107>=gpio)) {	/* GPIO104〜107 = DVX_GPIO [DB51903M] */
		if(GPIO_SMP_DIR_IN == dir) {
			Board_SelectBlpConnection(0, (0x00|AG903_BLP_CONNECT_GPIO));	/* GPIO IN */
		}
		else {
			Board_SelectBlpConnection(0, (0xF0|AG903_BLP_CONNECT_GPIO));	/* GPIO OUT */
		}
	}
	
	return;
}

/**
 * @brief		エッジ検出待ち
 * @param[in]	ch		Port CH
 * @param[in]	target	対象bit
 * @return		なし
 * @note		なし
 */
static int32_t Gpio_WaitEdgeDetect(uint8_t ch, uint32_t target)
{
	int32_t		retval = AG903_ENONE;
	
	while(1) {
		if(GpioDetectEdge[ch] & target) {
			break;
		}
		sys_dlytsk(10);
	}
	
	return retval;
}

/**
 * @brief	コールバック (GPIOイベント).
 * @param[in]	portch	Port Ch.
 * @param[in]	status	エッジ検出有無.
 * @return	なし.
 * @note	なし.
 */
static void Gpio_CallbackEvent(uint8_t ch, uint32_t status)
{
	GpioDetectEdge[ch] |= status;
	return;
}

