
#include	"config.h"
#include	"OLED128x64.h"
#include 	<math.h>

/*************	功能说明	**************

使用STC8A8K64S4A12系列MCU做64点FFT分析, 128x64 OLED显示频谱.

本程序优化计算变量, 使用整数运算, 加快速度.

ADC使用9位结果(+-255), 正弦表 余弦表放大127倍, 频谱最大幅度为255.

分析一次要8.5ms, 刷新一次要18.2ms, 刷新率为55HZ.

******************************************/


//---------------------------------------------------------------------


/*************	本地常量声明	**************/

#define  channel  0			  //设置AD通道为 P1.0, 取值0~14

#define ADC_START	(1<<6)	/* 自动清0 */
#define ADC_FLAG	(1<<5)	/* 软件清0 */

#define	ADC_SPEED	0		/* 0~15, ADC转换时间(CPU时钟数) = (n+1)*32  ADCCFG */
#define	RES_FMT		(1<<5)	/* ADC结果格式 0: 左对齐, ADC_RES: D11 D10 D9 D8 D7 D6 D5 D4, ADC_RESL: D3 D2 D1 D0 0 0 0 0 */
							/* ADCCFG      1: 右对齐, ADC_RES: 0 0 0 0 D11 D10 D9 D8, ADC_RESL: D7 D6 D5 D4 D3 D2 D1 D0 */


/*************	本地变量声明	**************/

#define	SAMPLE_RATE	25600	//定义采样率
#define LENGTH 		64		//定义点数

u8	xdata	FFT_tmp[8][128];

u8	ADC_Count=0;
int	xdata	adc_sample[LENGTH];	// ADC采样缓冲
int xdata	FFT_Real[LENGTH];	// fft的实部
int xdata	FFT_Image[LENGTH];	// fft的虚部 
u16	xdata 	FFT_A[LENGTH/2];		// 频率的幅度

bit	B_ADC_OK;

/*************	本地函数声明	**************/


//计算余弦用的表，放大了127倍(64点FFT时用)
int code COS_TABLE[LENGTH/2]=
{
 127, 126, 125, 122, 117, 112, 106,  98,  90,  81,   71,   60,   49,  37,   25,   12,
   0, -12, -25, -37, -49, -60, -71, -81, -90, -98, -106, -112, -117, -122, -125, -126
};

//计算正弦用的表，放大了127倍(64点FFT时用)
int code SIN_TABLE[LENGTH/2]=
{
   0,  12,  25,  37,  49,  60,  71, 81, 90, 98, 106, 112, 117, 122, 125, 126,
 127, 126, 125, 122, 117, 112, 106, 98, 90, 81,  71,  60,  49,  37,  25,  12
};


//采样存储序列表(倒序)	FFT 64点反序十进制:
u8 code LIST_TAB[LENGTH] = {
   0,  32,  16,  48,   8,  40,  24,  56,   4,  36,  20,  52,  12,  44,  28,  60,
   2,  34,  18,  50,  10,  42,  26,  58,   6,  38,  22,  54,  14,  46,  30,  62,
   1,  33,  17,  49,   9,  41,  25,  57,   5,  37,  21,  53,  13,  45,  29,  61,
   3,  35,  19,  51,  11,  43,  27,  59,   7,  39,  23,  55,  15,  47,  31,  63
};


/********************************************************************
函数功能：进行FFT运算。
入口参数：pIn：输入数据的缓冲区；
          pRe：保存实部的缓冲区；
		  pIm：保存虚部的缓冲区。
返    回：无。
备    注：在调用该函数之前应该调用FftInput函数对数据进行倒序处理。
********************************************************************/

void FftExe(int *pIn, int *pRe, int *pIm)
{
	u8	i,j;
	u8	BlockSize;	//64点的可以使用8位变量, 最大值是128
	int	tr,ti;
	u8	OffSet1,OffSet2;
	long	c,s;		//必须是long型, 否则溢出

	//先计算2点的
	for(j=0; j<LENGTH; j+=2)
	{
		tr       = pIn[j+1];
		pRe[j+1] = (pIn[j] - tr);
		pIm[j+1] = 0;
		pRe[j]   = (pIn[j] + tr);
		pIm[j]   = 0;
	}

	for(BlockSize=4; BlockSize<=LENGTH; BlockSize<<=1) //再一层层计算
	{
		for(j=0; j<LENGTH; j+=BlockSize)
		{
			for(i=0; i<BlockSize/2; i++)
			{
				OffSet1 = LENGTH/BlockSize * i;
				c = (long)COS_TABLE[OffSet1];
				s = (long)SIN_TABLE[OffSet1];

				OffSet1 = i + j;
				OffSet2 = OffSet1 + BlockSize/2;
				tr = (c*pRe[OffSet2] + s*pIm[OffSet2]) >> 7;
				ti = (c*pIm[OffSet2] - s*pRe[OffSet2]) >> 7;

				pRe[OffSet2] = (pRe[OffSet1] - tr) >> 1;	//对结果归一化处理，则每次运算要除以2
				pIm[OffSet2] = (pIm[OffSet1] - ti) >> 1;
				pRe[OffSet1] = (pRe[OffSet1] + tr) >> 1;
				pIm[OffSet1] = (pIm[OffSet1] + ti) >> 1;
			}
		}
	}
	pRe[0] = pRe[0] >> 1;
	pIm[0] = pIm[0] >> 1;
}





u8 code FFT_Dot[8] = {	0x80,	0xc0,	0xe0,	0xf0,	0xf8,	0xfc,	0xfe,	0xff};


void Show_OLED(void)
{

	u8	xdata *xp;
	u8	i,j,k,b,p;
	u16	TEMP1;	

	xp = &FFT_tmp[0][0];
	for(TEMP1=0; TEMP1<1024; TEMP1++)	{*xp = 0;	xp++;}	//清除显存

	for(j=0; j<LENGTH/2; j++)											 
	{																			  
	//	TEMP1 = FFT_A[j]/4;		//求幅值	输入+-256
	//	if(TEMP1 >= 64)	TEMP1 = 63;
	//	i = (u8)TEMP1;
		i = FFT_A[j]/2;		//求幅值	输入+-256
		if(i >= 64)	i = 63;

		p = i / 8;	k = i & 7;
		p = 7 - p;
		if(p != 7)
		{
			for(b=7; b>p; b--)	FFT_tmp[b][j<<1] = 0xff;
		}
		FFT_tmp[p][j<<1] = FFT_Dot[k];
	}
	
	for(j=0; j<8; j++)	ShowFFT(j, &FFT_tmp[j][0]);	//显示到OLED屏
}




void ADC_int (void) interrupt 5
{
P36 = 1;
	ADC_CONTR &= ~ADC_FLAG;
	adc_sample[LIST_TAB[ADC_Count]] = (int)(( (u16)ADC_RES * 256 + (u16)ADC_RESL ) >> 3) - 256;	// 按LIST_TAB表里的顺序，进行存储 采样值,,
	if(++ADC_Count >= LENGTH)		//ADC结束, 128点
	{
		ADC_Count = 0;
		TR0 = 0;		//关闭定时器
		B_ADC_OK = 1;
	}
P36 = 0;
}


//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2016-5-12
//========================================================================
void timer0_int (void) interrupt TIMER0_VECTOR
{
	ADC_CONTR = 0x80 | ADC_START | channel;		 //开始AD采集

}


void Init(void)
{  
//	u8	xdata *p;
	
//	for(p=0; p<8192; p++)	*p = 0;

	P1n_pure_input(0x01);	//设置要做ADC的IO做高阻输入
	ADC_CONTR = 0x80 + channel;	//ADC on + channel
	ADCCFG = RES_FMT + ADC_SPEED;
	PADC = 1;		//ADC 中断优先级 高
	
	TR0 = 0;	//停止计数
	AUXR |= 0x80;		//1T mode
	TH0 = (65536L - (MAIN_Fosc + SAMPLE_RATE/2) / SAMPLE_RATE) / 256;
	TL0 = (65536L - (MAIN_Fosc + SAMPLE_RATE/2) / SAMPLE_RATE) % 256;

	ET0 = 1;	//允许中断
	PT0 = 1;	//高优先级中断
	TMOD = (TMOD & ~0x03) | 0;	//工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
//	TMOD |=  0x04;	//对外计数或分频
//	INT_CLKO |=  0x01;	//输出时钟
//	TR0 = 1;	//开始运行

	EA  = 1;							//总中断打开 

}

  //==============================================================================================================
 //	*******************		   					main()							*********************************										  
 //===============================================================================================================

 void main()
 {
	u8	i;

	EAXSFR();
	EXT_CRYSTAL();		//XOSCCR = 0xc0;                              //启动外部晶振
	while (!(XOSCCR & 1));                      //等待时钟稳定
	CLKDIV = 0x00;                               //时钟不分频
	MainFosc_XTAL();	//CKSEL = 0x01;                               //选择外部晶振
	
	Init();
	Initialize_OLED();	//initialize OLED

	for(i=0; i<21; i++)	FFT_tmp[0][i] = i + '0';	FFT_tmp[0][i] = 0;
	printf_ASCII_text(0,0,&FFT_tmp[0][0]);
	
	ADC_Count = 0;
	EADC = 1;		//允许ADC中断
	TR0  = 1;		//开启定时器, 其中断触发ADC转换


	while(1)
	{
		if(B_ADC_OK)	//采样完成	21.2ms刷新一次  @22.1184MHZ
		{
		//	P41 = 1;
			FftExe(adc_sample, FFT_Real, FFT_Image);		//用int运算 8.5ms @22.1184MHZ
		//	P41 = 0;

			B_ADC_OK = 0;
			TR0  = 1;		//开启定时器, 其中断触发ADC转换

			
			P41 = 1;
			for(i=0; i<LENGTH/2; i++)
			{
			//	FFT_A[i] = sqrt((long)FFT_Real[i]*(long)FFT_Real[i] + (long)FFT_Image[i]*(long)FFT_Image[i]);   //计算模值	5.4ms @22.1184MHZ
				int	j,k;
				j = FFT_Real[i]/2;  k= FFT_Image[i]/2;
				FFT_A[i] = sqrt(j*j+k*k);	//平均2.5ms
			}
			P41 = 0;

		//	P41 = 1;
			Show_OLED();		//7.3ms @22.1184MHZ
		//	P41 = 0;
		}
	}
 }
 
 