/******************************************************************************
 * @file     main.c
 * @version  V3.00
 * $Revision: 2 $
 * $Date: 14/07/31 10:14a $
 * @brief    Demonstrate ADC paly 16kHz speech from MIC
 *
 * @note
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "N572.h"

#define BUF_NUM 			(2)	
#define BUF_SIZE			(8)
#define TOTAL_SIZE			(BUF_SIZE*BUF_NUM)
#define SAMPLE_RATE  		(16000)

#define MIC_PINMASK		(BIT12|BIT13)

#define MIC_MULTIFUN	(SYS_GPA_MFP_PA12MFP_ADC4|SYS_GPA_MFP_PA13MFP_ADC5)
						 
void ADC_MicToSpk(void);
void ADC_Monitor(void);
void ADC_SingleMode(void);
void ADC_SingleCycleMode(void);

volatile static uint8_t	s_u8WriteIdx, s_u8ReadIdx;	


__align(4) int16_t g_ai16ADCData[TOTAL_SIZE];

//----------------------------------------------------------------------------------------------------------
// Variables and function define for DC removing
static  int16_t  s_i16RemoveDCSampleX = 0;
static  int16_t  s_i16RemoveDCSampleY = 0;

void RemoveDCInitial(void)
{
	s_i16RemoveDCSampleX = 0;
	s_i16RemoveDCSampleY = 0; 
}

//-----------------------------------------------------------------------------------------------------------
// transfer function of this high pass filter is: (7/8) * (1-Z^(-1)) / (1- (15/16)Z^(-1))
// input PCM dynamic range limit to 13 bits (Q4.12) to avoid overflow in opeation.
// output PCM dynamic range is 13 bits PCM, Q4.12 format
// it's very easy to revise this filter for 16 bits PCM input if needed.
//-----------------------------------------------------------------------------------------------------------
void RemoveDCProcess(int16_t *pi16EncodePcmBuf, uint32_t u32PcmBufCount)
{
	 int16_t i16Index; 
	 int16_t i16SampleX;
	 int16_t i16SampleY;
	 int32_t i32Sample;

	i16Index = 0;
	i16SampleX = s_i16RemoveDCSampleX;
	i16SampleY = s_i16RemoveDCSampleY;
	do
	{
		i32Sample  = (((int32_t) i16SampleY) - (i16SampleY >> 4)) - i16SampleX;  //Q1.15 format, (15/16)Y(Z^-1) - X(Z^-1)
//		i16SampleX = (INT16) ((((INT32) pi16EncodePcmBuf[i16Index]) * 7)); // 7/8 << 3, Q4.12 ---> Q1.15 format
		i16SampleX = (int16_t) ((((int32_t) pi16EncodePcmBuf[i16Index]) * 7)) >> 1; // 7/8 << 2, Q4.12 ---> Q2.14 format
		i32Sample += i16SampleX;

		i16SampleY  = ((int16_t) (i32Sample));
//		pi16EncodePcmBuf[i16Index] = (i16SampleY >> 3); // back to Q4.12 format. 13bit
		pi16EncodePcmBuf[i16Index] = (i16SampleY >> 2); // back to Q4.12 format. 13bit
	}
	while ((i16Index += 1) < u32PcmBufCount);
	s_i16RemoveDCSampleX = i16SampleX;
	s_i16RemoveDCSampleY = i16SampleY;

}

void ADC_IRQHandler()
{
    uint8_t	u8i,u8Temp;
	
	ADC_CLR_INT_FLAG(ADC, ADC_ADF_INT);
	
	u8Temp = s_u8WriteIdx;
	for (u8i=0;u8i<8;u8i++)	
		g_ai16ADCData[s_u8WriteIdx++] = (int16_t)ADC_GET_CONVERSION_DATA(ADC, u8i);
	
	RemoveDCProcess(&g_ai16ADCData[u8Temp], 8);
	
	if (s_u8WriteIdx>=TOTAL_SIZE)
		s_u8WriteIdx = 0;
}

void SYS_Init(void)
{

	/* Unlock protected registers */
    SYS_UnlockReg();
	
	/* Procedure to configure the wait state of SDRAM */
	SYS_SET_RAM_WAIT_STATE(SYS_IPRST0_RAMWS_ZERO_WAIT);
	/* Set flash access with 1 wait state, if system clock is greater than 24MHz*/
	SYS_SET_CPU_WAIT_STATE(SYS_IPRST0_CPUWS_1HCLK_WAIT);
	/* Enable external 32K oscillator */
	CLK_EnableXtalRC(CLK_PWRCTL_XTL32K_EN_Msk);
	/* Enable PLL1 & PLL2 */
	CLK_SetPLL1Mode(PLL_NORMAL_MODE);
	CLK_SetPLL2Mode(PLL_NORMAL_MODE);
#if defined(__N572F072__) 
	/* Select PLL1 as PLL2 input source, and PLL2 output 48MHz*/
	CLK_SetPLL2(PLL2_SRC_PLL1_FOUT, PLL2_48M);
	CLK_EnableLDO30();	
#endif
	
	/* Switch HCLK clock source to PLL2, system clock output 48MHz */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL2_FOUT,CLK_CLKDIV_HCLK(1));

    /* Lock protected registers */
    SYS_LockReg();
}

void ADC_Init(void)
{

	uint32_t u32Div;
	
	#if	(__CHIP_SERIES__ ==	__N572F065_SERIES__)
	
	// Configure and enable external LDO for ADC reference
	GPIO_SET_OUT_DATA(PA, GPIO_GET_OUT_DATA(PA)|(BIT15));
	GPIO_SetMode(PA, BIT14, GPIO_MODE_INPUT);
	GPIO_SetMode(PA, BIT15, GPIO_MODE_OUTPUT);
#endif
	
	// ------------------------------------------------------------------------------------------------------
	// Set PGC input pairs(GPA12&GPA13)	as GPIO	output low (discharge) to meet the PGC start-up	condition.
	// ------------------------------------------------------------------------------------------------------
	GPIO_SET_OUT_DATA(PA, GPIO_GET_OUT_DATA(PA)&~MIC_PINMASK);
	GPIO_SetMode(PA, MIC_PINMASK, GPIO_MODE_OUTPUT);
	SYS->GPA_MFP  = (SYS->GPA_MFP & ~MIC_MULTIFUN);
	
	// ------------------------------------------------------------------------------------------------------
	// Because GPIOA PIN8~PIN11 are ADC input too, these pins' voltage level will affect the MIC	result.
	// Therefore, set them to be output pins and set output	value to 0.
	// This can avoid these pins voltage evel to affect the MIC result!
	// ------------------------------------------------------------------------------------------------------
	GPIO_SET_OUT_DATA(PA, GPIO_GET_OUT_DATA(PA)&~(BIT8|BIT9|BIT10|BIT11));
	GPIO_SetMode(PA, BIT8|BIT9|BIT10|BIT11,	GPIO_MODE_OUTPUT);
	
	
	/* Enable multiple function for ADC channel */
	SYS->GPA_MFP  = (SYS->GPA_MFP & ~MIC_MULTIFUN ) | MIC_MULTIFUN;
		
	/* Set GPIO as input mode */
	GPIO_SetMode(PA, MIC_PINMASK, GPIO_MODE_INPUT);
	
	/* Enable ADC APB bus and reset IP */
	CLK_EnableModuleClock(ADC_MODULE);
	SYS_ResetModule(ADC_RST);
	
	ADC_Open(ADC);
	ADC_POWER_ON(ADC);
	
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	ADC_SetRegulatorRC(ADC, ADC_CTRS_R10K, ADC_FWU_R8K);
	ADC_EnableRegulator(ADC);
	TIMER_Delay(TIMER0, 100000);
	ADC_SetRegulatorRC(ADC, ADC_CTRS_R600K, ADC_FWU_R400K);
	
	/* ADC clock init*/
	/* sample rate = ADC clock source/25/divisor*/
	u32Div = SystemCoreClock/25/SAMPLE_RATE;
	CLK_SetModuleClock(ADC_MODULE, CLK_CLKSEL1_ADCSEL_HCLK, CLK_CLKDIV_ADC(u32Div));
#else
	/* ADC clock init*/
	/* sample rate = ADC clock source/25/divisor*/
	u32Div = SystemCoreClock/25/SAMPLE_RATE;
	CLK_SetModuleClock(ADC_MODULE, CLK_CLKSEL1_ADCSEL_PLL2_FOUT, CLK_CLKDIV_ADC(u32Div));
#endif	
	
	
	ADC_STOP_CONV(ADC);

#if (__CHIP_SERIES__ == __N572F072_SERIES__)	
	ADC_SetPGAGain1dB(ADC, 40);
	ADC_SetPGAGain2dB(ADC, 0);
#else
	ADC_SetPGAGain1dB(ADC, 14);
	ADC_SetPGAGain2dB(ADC, 20);
#endif
	/* set operation mode*/
	ADC_SET_OPERATION(ADC, ADC_OPERATION_MODE_CONTINUOUS);
	/* set conversion result format */
	ADC_SET_OUTFORMAT(ADC, ADC_OUT_FORMAT_2COMPLEMENT);
	/* If using microphone, user must select CH4&CH5 path for MIC on N572 EVB board.*/
	ADC_SET_SCAN_SEQUENCE(ADC, ADC_CH_45_MASK, ADC_CH_45_MASK, ADC_CH_45_MASK, ADC_CH_45_MASK,
						  ADC_CH_45_MASK, ADC_CH_45_MASK, ADC_CH_45_MASK, ADC_CH_45_MASK);
	
	/* Enable ADC interrupt */
	ADC_EnableInt(ADC, ADC_ADF_INT);
	
	ADC_START_CONV(ADC);
	ADC_AnalysisCalibration(ADC);
	ADC_STOP_CONV(ADC);
}

void APU_Init(void)
{
	uint8_t u8Loop;
	
	/* Enable ADC APB bus and reset IP */
	CLK_EnableModuleClock(APU_MODULE);
	SYS_ResetModule(APU_RST);
	/* Timer0 is used as the channel 0 sample rate control timer */
	CLK_EnableModuleClock(TMR0_MODULE);
	CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HCLK, MODULE_NoMsk);
	SYS_ResetModule(TMR0_RST);
	
	APU_Open(APU);
	APU_SetSampleRate(APU, 12000);
	APU_SET_BYPASSPA(APU, APU_NONBYPASS_PA);
	APU_DISABLE_PA(APU);
	APU_SetVolGaindB(APU, -18);
	for (u8Loop = 0; u8Loop< 8 ;u8Loop++)
		APU_SET_CH0DATA(APU, u8Loop, 0);
	APU_ENABLE_DAC(APU);
	APU_SET_INT_THD(APU,7);
	APU_ENABLE_INT(APU);
	APU_START_PLAY(APU);
	
	while(APU_GET_INT_FLAG(APU) == 0);
	
	APU_CLR_INT_FLAG(APU);
	APU_STOP_PLAY(APU);
	
	APU_SetVolGaindB(APU, -6);
	APU_SetSampleRate(APU, SAMPLE_RATE);
	APU_SET_INT_THD(APU,3);
	APU_ENABLE_PA(APU);							 	
}

int main(void)
{
			
	/* Lock protected registers */
    if(SYS->REGLCTL == 1) // In end of main function, program issued CPU reset and write-protection will be disabled.
        SYS_LockReg();
			
    /* Init System, IP clock and multi-function I/O */
    SYS_Init(); //In the end of SYS_Init() will issue SYS_LockReg() to lock protected register. If user want to write protected register, please issue SYS_UnlockReg() to unlock protected register.
	
	/* Init ADC IP, clock and dc calibration */
	ADC_Init();
	
	/* Init APU for Mic playback */
	APU_Init();
	
	while (1)
    {
		printf("\n\nCPU @ %dHz\n", SystemCoreClock);
		
		printf("\n\n");
		printf("+----------------------------------------------------------------------+\n");
		printf("|                       ADC Driver Sample Code                         |\n");
		printf("+----------------------------------------------------------------------+\n");
		printf(" APU Playing Sample Rate : %dHz                                         \n", APU_GetSampleRate(APU));
		printf(" PGA Gain1 Value: %ddB                                                  \n", ADC_GetPGAGain1dB(ADC));		
		printf(" PGA Gain2 Value: %ddB                                                  \n", ADC_GetPGAGain2dB(ADC));		
		
		ADC_MicToSpk()                                                                                                                             ;
	}
	//while(1);
}

void ADC_MicToSpk(void)
{
	//uint16_t u16i;
	 	 
	printf("Microphone to Speaker test \n");
	
	s_u8WriteIdx = 0;
	s_u8ReadIdx = 8;
	

	/* Clear ADC of NVIC interrupt */
	NVIC_ClearPendingIRQ(ADC_IRQn);
	/* Enable NVIC interrupt for ADC*/
	NVIC_EnableIRQ(ADC_IRQn);
	
	ADC_START_CONV(ADC);
	
	APU_START_PLAY(APU);
	while(1)
	{
		/* Occurs APU interrupt*/
		
		if (APU_GET_INT_FLAG(APU))
		{
			NVIC_DisableIRQ(ADC_IRQn);
			APU_CLR_INT_FLAG(APU);
			
			if (APU_GET_INT_THD(APU) == 3)
			{		
				/* output (channel0+channel1)is up to 13 bits from ADC 12 bits*/
				APU_SET_CH0DATA(APU, 0, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_CH0DATA(APU, 1, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_CH0DATA(APU, 2, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_CH0DATA(APU, 3, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_INT_THD(APU, 7);
			}
			else
			{
				APU_SET_CH0DATA(APU, 4, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_CH0DATA(APU, 5, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_CH0DATA(APU, 6, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_CH0DATA(APU, 7, g_ai16ADCData[s_u8ReadIdx++]<<1);
				APU_SET_INT_THD(APU, 3);
			}
			if (s_u8ReadIdx>=TOTAL_SIZE)
				s_u8ReadIdx = 0;
			NVIC_EnableIRQ(ADC_IRQn);
		}
	}
	/*APU_STOP_PLAY(APU);
	ADC_STOP_CONV(ADC);
	NVIC_DisableIRQ(ADC_IRQn);
	
	for (u16i = 0; u16i< 8 ;u16i++)
		APU_SET_CH0DATA(APU, u16i, 0);
	
	memset(g_ai16ADCData, 0, sizeof(g_ai16ADCData));*/
}


/*** (C) COPYRIGHT 2014 Nuvoton Technology Corp. ***/
