本次立异基金我是要做一个简易的频谱仪,中心便是要进行一个FFT运算。咱们知道,假如选用DSP芯片作用那是相当好的。但因为项目资金以及时刻不行等状况,我选用的是ATMEL公司的AVR单片机,这款单片机的FLASH存储和内存比51单片机尖锐得多。
因为选用的是12864液晶,也便是一个横128点竖64点的一个点阵,因而选用128点FFT运算已然够了,因为即便得到再多的数据也无法在液晶上可视化显现出来。本文是根据128点FFT运算。
程序如下:
#include
#include
#include
#define N 128
#define PI 3.141592653589
#define uchar unsigned char
#define uint unsigned int
typedef struct
{
int real;
int img;
}complex;
void initw(); //初始化旋转因子
void bitReverse(); //比特回转
void FFT();
complex x[N];
uchar vis[N];
void delayms(uint ms)
{
uint i,j;
for(i=0;i
for(j=0;j<3;j++);
}
}
void FFT()
{
int i,j,k,t,P,B,m;
complex up,down,product;
for (i=0;i<7;i++)
{
B=1<for (j=0;j{
t=1<<(6-i);
P=t*j;
for (k=j;k
complex product;
product.real=x[k+B].real*cos(2*PI*P/N)+x[k+B].img*sin(2*PI*P/N);
product.img=x[k+B].real*(-1)* sin(2*PI*P/N)+x[k+B].img*cos(2*PI*P/N);
x[k+B].real=x[k].real-product.real;
x[k+B].img=x[k].img-product.img;
x[k].real=x[k].real+product.real;
x[k].img=x[k].img+product.img;
}
}
}
}
void initw() //初始化旋转因子
{
int i;
for (i=0;i
}
void bitReverse() //比特回转
{
int i,j=0;
int k=0;
int q=0;
complex tmp3;
for (i=0;i
int tmp=i,tmp2=0,j;
for(j=0;j<7;j++)
tmp2+=((tmp>>j)&1)*(1<<(6-j));
if(vis[i]==0)
{
tmp3=x[i];
x[i]=x[tmp2];
x[tmp2]=tmp3;
vis[i]=1;
vis[tmp2]=1;
}
}
}
void main()
{
uchar ii,y;
float tmp;
for (ii=0;ii<20;ii++)
{
x[ii].real=3;
x[ii].img=0;
}
for (ii=20;ii<128;ii++)
{
x[ii].real=0;
x[ii].img=0;
}
initw();
bitReverse();
FFT();
while(1);
}
上图是8点FFT运算,依照上图的流程所示,FFT运算主要有两步,一步是比特回转,便是右边不是依照0、1、2、3……这样次序进行核算的,而左面是的,两头的联系便是进行一个比特回转。能够看到右边0对应二进制为000,左面对应二进制为000,右边1二进制001,左面4对应二进制100,顺次下去,能够清楚看到,关于8位FFT运算,对应二进制有三位,而左右两头的联系恰巧是依照中间位进行了个回转。
FFT运算第二步便是乘以旋转因子,留意的是这里是复数运算,虚部和实部都要参加运算。乘以旋转因子后对进行加减运算得到新的值,顺次下去得到终究解。
因为单片机内存的约束,因而关于传统的FFT算法,我进行了些改善,准则便是尽量地少运用变量,一个变量能够重复的运用是最理想的了,咱们能够在程序中看出。个人定见这是能节约变量最少的了,假如有好的办法,期望能够告诉我下,我的邮箱是albertvictordu@139.com,谢谢!
下面是12864液晶驱动程序的写法:
LCD12864液晶,即像素为128*64的显现液晶。它的每一行横向一共有128个可显现点,每一列纵向有64个,这些“点”其实也都是一个个发光二极管。它能够在一个16*16的点阵区域上显现一个中文,也能够在一个8*16的点阵区域显现一个非中文字符,一般称为半宽字体。即一个中文字所占显现面积是一个非中文字符的两倍。
关于驱动函数的书写,是液晶显现的根底,整个液晶驱动主要有四个函数组成:
1、写指令函数;
2、写数据函数;
3、读状况函数;
4、读数据函数;
这四个函数并不是有必要悉数写的,详细要看你完结的功用,假如仅仅单纯的显现汉字和字符,写指令、写数据、读状况这三个函数就够了,如过你还需求进行一些绘图的操作,那读数据函数也有必要书写。
别的关于读状况函数,其实也便是用于判忙操作,准则上每次对操控器进行读写操作之前,都有必要进行读写检测,因为单片机的操作速度慢于液晶操控器的反应速度,因而可不进行读写检测,或许只进行简略的延时即可。因而,读状况函数也能够不写,只用简略的延时函数替换即可。
单片机用于操控LCD的管脚主要为RS、RW和E管脚,别离的功用是RS为0时,对应单片机拜访的是指令寄存器,为1时对应数据寄存器;RW为1时,对应单片机操作为读操作,为0时对应单片机为写操作;E是使能信号。
读操作如下图所示
写操作如下图所示
在12864液晶中,开发商将一些根本指令现已写入到指令寄存器中,咱们调用该指令就能够完结相应的功用。
LCD初始化
初始化操作如下:
1. 芯片上电;
2. 延时40ms以上;
3. 复位操作:RST呈现一个上升沿(RST=1;RST=0;RST=1;);
4. 功用设定;
5. 延时100us以上;
6. 再次进行功用设定;
7. 延时37us;
8. 显现开关操控;
9. 延时100us以上;
10. 铲除显现;
11. 延时10ms以上;
12. 进入点设置;
13. 初始化完毕;
LCD液晶屏初始化进程如图所示为:
打点函数
打点函数是创立GUI的根底,打点函数的书写分为以下几个过程:
1. 进入扩展形式
2. 写入打点地址
3. 读取该地址的数据
4. 修正该地址的数据
5. 将修正后的数据输入LCD中
6. 进入一般形式
GDRAM地址散布状况,需求留意的是横纵坐标的开始地址都是0x80,还有上下半屏的横坐标是不相同的,下半屏的横坐标要加上0x08,而纵坐标跟对应的上半屏的纵坐标是相同的。GDRAM地址散布图,如图所示。
下面的函数是12864与FFT算法的一个结合,里边设置了一个门函数,12864上显现的成果则是一个sinc函数,证明成果是正确的。
#include
#include
#include
#define N 128
#define PI 3.141592653589
#define uchar unsigned char
#define uint unsigned int
#define RS (1<<4)
#define RW (1<<5)
#define EN (1<<6)
//
typedef struct
{
int real;
int img;
}complex;