FFT算法在单片机中的使用&&L…

原文地址:FFT算法在单片机中的使用&&LCD12864驱动 作者:元大帝
    好久没更新博客了,觉得对不起自己建立博客的初衷。我这个人太懒了,又没有坚持下去的决心,唉~
      言归正传,本次创新基金我是要做一个简易的频谱仪,核心就是要进行一个FFT运算。大家知道,如果采用DSP芯片效果那是相当好的。但由于项目资金以及时间不够等情况,我采用的是ATMEL公司的AVR单片机,这款单片机的FLASH存储和内存比51单片机犀利得多。
      由于采用的是12864液晶,也就是一个横128点竖64点的一个点阵,因而采用128点FFT运算已然够了,因为即使得到再多的数据也无法在液晶上可视化显示出来。本文是基于128点FFT运算。
      程序如下:
#include <iom16v.h>
#include <macros.h>
#include<math.h>
#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<ms;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<<i;
for (j=0;j<B;j++)
{
t=1<<(6-i);
P=t*j;
for (k=j;k<N;k=k+2*B)
{
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<N;i++)
vis[i]=0;
}

void bitReverse()   //比特反转
{
int i,j=0;
int k=0;
int q=0;
complex tmp3;
for (i=0;i<N;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);
}
[转载]FFT算法在单片机中的使用&&LCD12864驱动
  [转载]FFT算法在单片机中的使用&&LCD12864驱动   
      上图是8点FFT运算,按照上图的流程所示,FFT运算主要有两步,一步是比特反转,就是右边不是按照0、1、2、3……这样顺序进行计算的,而左边是的,两边的关系就是进行一个比特反转。可以看到右边0对应二进制为000,左边对应二进制为000,右边1二进制001,左边4对应二进制100,依次下去,可以清楚看到,对于8位FFT运算,对应二进制有三位,而左右两边的关系恰巧是按照中间位进行了个反转。
      FFT运算第二步就是乘以旋转因子,注意的是这里是复数运算,虚部和实部都要加入运算。乘以旋转因子后对进行加减运算得到新的值,依次下去得到最终解。
[转载]FFT算法在单片机中的使用&&LCD12864驱动

      由于单片机内存的限制,因而对于传统的FFT算法,我进行了些改进,原则就是尽量地少使用变量,一个变量可以重复的使用是最理想的了,大家可以在程序中看出。个人意见这是能节省变量最少的了,如果有好的方法,希望可以告诉我下,我的邮箱是[email protected],谢谢!


    下面是12864液晶驱动程序的写法:
      LCD12864液晶,即像素为128*64的显示液晶。它的每一行横向一共有128个可显示点,每一列纵向有64个,这些其实也都是一个个发光二极管。它可以在一个16*16的点阵区域上显示一个中文,也可以在一个8*16的点阵区域显示一个非中文字符,一般称为半宽字体。即一个中文字所占显示面积是一个非中文字符的两倍。

关于驱动函数的书写,是液晶显示的基础,整个液晶驱动主要有四个函数组成:

1、写命令函数;

2、写数据函数;

3、读状态函数;   

4、读数据函数;      

这四个函数并不是必须全部写的,具体要看你实现的功能,如果只是单纯的显示汉字和字符,写命令、写数据、读状态这三个函数就够了,如过你还需要进行一些绘图的操作,那读数据函数也必须书写。      

另外关于读状态函数,其实也就是用于判忙操作,原则上每次对控制器进行读写操作之前,都必须进行读写检测,由于单片机的操作速度慢于液晶控制器的反应速度,因此可不进行读写检测,或者只进行简短的延时即可。因此,读状态函数也可以不写,只用简短的延时函数替换即可。

单片机用于控制LCD的管脚主要为RSRWE管脚,分别的功能是RS0时,对应单片机访问的是命令寄存器,为1时对应数据寄存器;RW1时,对应单片机操作为读操作,为0时对应单片机为写操作;E是使能信号。

    读操作如下图所示

[转载]FFT算法在单片机中的使用&&LCD12864驱动
  写操作如下图所示

[转载]FFT算法在单片机中的使用&&LCD12864驱动
  
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液晶屏初始化过程如图所示为:

[转载]FFT算法在单片机中的使用&&LCD12864驱动

打点函数

打点函数是创建GUI的基础,打点函数的书写分为以下几个步骤:

1.        进入扩展模式

2.        写入打点地址

3.        读取该地址的数据

4.        修改该地址的数据

5.        将修改后的数据输入LCD

6.        进入普通模式

GDRAM地址分布情况,需要注意的是横纵坐标的起始地址都是0x80,还有上下半屏的横坐标是不一样的,下半屏的横坐标要加上0x08,而纵坐标跟对应的上半屏的纵坐标是一样的。GDRAM地址分布图,如图所示。

[转载]FFT算法在单片机中的使用&&LCD12864驱动

下面的函数是12864与FFT算法的一个结合,里面设置了一个门函数,12864上显示的结果则是一个sinc函数,证明结果是正确的。

#include <iom16v.h>

#include <macros.h>

#include<math.h>

#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;



void initw();                                  //初始化旋转因子

void bitReverse();                             //比特反转

void FFT();


complex x[N];

uchar vis[N];



void delayms(uint ms)

{

    uint i,j;

 for(i=0;i<ms;i++)

 {

    for(j=0;j<3;j++);

    }

}

//此处定义字符串



//写数据

void WriteDataLCM(unsigned char WDLCM)  //写数据函数

{

// ReadStatusLCM(); //检测忙

delayms(1);

PORTA|=RS;       //RS=1    

delayms(1);

PORTA&=~RW;      //RW=0

delayms(1);

PORTA|=EN;       //EN=1

delayms(1);

PORTB=WDLCM;     //输出数据

delayms(1);

PORTA&=~EN;      //EN=0

delayms(1);

}


//写指令

void WriteCommandLCM(unsigned char WCLCM) //写命令函数

{

// ReadStatusLCM(); //根据需要检测忙

delayms(1);

PORTA&=~RS;      //RS=0

delayms(1);

PORTA&=~RW;      //RW=0

delayms(1);

PORTA|=EN;       //EN=1

delayms(1);

PORTB=WCLCM;     //输出指令

delayms(1);

PORTA&=~EN;      //EN=0

delayms(1);

}


//读状态:检测忙

void ReadStatusLCM()  //读状态函数

{

uchar temp;

    uchar flag = 1;

    while(flag==1)

{

PORTB=0xff; 

delayms(1);

   DDRB=0x00;      //端口B改为输入   

delayms(1);

PORTA&=~RS;     //RS=0

delayms(1);

PORTA|=RW;      //RW=1

delayms(1);

PORTA|=EN;      //EN=1

delayms(10);

temp = PINB;    //读端口B

delayms(10);

DDRB=0xff;      //端口B改为

delayms(10);

PORTA&=~EN;     //EN=0

delayms(1);

if(temp>>7==0)

flag = 0;

}

}


uchar read_data() //读数据函数

{

  uchar lcd_data;

  PORTB=0xff;

  DDRB=0x00;

  PORTA|=RW;

  PORTA|=RS; 

  delayms(10);

  PORTA|=EN;

  delayms(10);

  lcd_data=PINB;

  delayms(10);

  PORTA&=~EN;

  DDRB=0xff;

  return(lcd_data) ;

void point(uchar x,uchar y) //打点函数,最重要的函数,GUI的基础

{

  uchar x_Dyte,x_byte;

  uchar y_Dyte,y_byte;

  uchar GDRAM_hbit,GDRAM_lbit;

  WriteCommandLCM(0x36);

  x_Dyte=x/16;

  x_byte=x&0x0f;

  y_Dyte=y/32;

  y_byte=y&0x1f;


  WriteCommandLCM(0x80+y_byte);

  WriteCommandLCM(0x80+x_Dyte+8*y_Dyte);

  read_data();

  GDRAM_hbit=read_data();

  GDRAM_lbit=read_data();

   delayms(10);

  WriteCommandLCM(0x80+y_byte);

  WriteCommandLCM(0x80+x_Dyte+8*y_Dyte);

   delayms(10);

  if(x_byte<8)

    {

 WriteDataLCM(GDRAM_hbit|(0x01<<(7-x_byte)));

 WriteDataLCM(GDRAM_lbit);

}

  else

    {

 WriteDataLCM(GDRAM_hbit);

 WriteDataLCM(GDRAM_lbit|(0x01<<(15-x_byte)));

}

 WriteCommandLCM(0x30);

}


//LCM初始化

void LCMInit(void) 

{

WriteCommandLCM(0x30);    //三次显示模式设置,不检测忙信号

delayms(10);

WriteCommandLCM(0x30);

delayms(10); 

WriteCommandLCM(0x30);

delayms(10); 

WriteCommandLCM(0x30);    //显示模式设置,开始要求每次检测忙信号

WriteCommandLCM(0x08);    //关闭显示

WriteCommandLCM(0x01);    //显示清屏

WriteCommandLCM(0x06);    //显示光标移动设置

WriteCommandLCM(0x0C);    //显示开及光标设置

}


void clear(uchar dat)  //清屏函数

{

  uchar i,j,k;

  uchar addr=0x80;

  for(i=0;i<2;i++)

    {

 for(j=0;j<32;j++)

   {

 for(k=0;k<8;k++)

   {

 WriteCommandLCM(0x36);

 WriteCommandLCM(0x80+j);

 WriteCommandLCM(addr+k);

 WriteDataLCM(dat);

 WriteDataLCM(dat);

}

 

}

  addr=0x88;

}

   WriteCommandLCM(0x36);

   WriteCommandLCM(0x30);

}


void heng(uchar a)

{

  uchar i;

  for(i=0;i<127;i++)

    point(i,a);

}

void su(uchar a)

{

  uchar i; 

  for(i=0;i<63;i++)

    point(a,i);

}


void FFT()

{

int i,j,k,t,P,B,m;

complex up,down,product;

for (i=0;i<7;i++)

{

B=1<<i;

for (j=0;j<B;j++)

{

t=1<<(6-i);

P=t*j;

for (k=j;k<N;k=k+2*B)

{

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<N;i++)

vis[i]=0;

}


void bitReverse()  //比特反转

{

int i,j=0;

int k=0;

int q=0;

complex tmp3;

for (i=0;i<N;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 xian(uchar x,uchar y)

{

  uchar i;

  for(i=63;i>=y;i--)

    point(x,i);

}

//主函数

void main(void)

{

uchar ii,y;

float tmp;

    //端口初始化

DDRA=0xff;

PORTA=0xff;

DDRB=0xff;

PORTB=0xff;

DDRD=0xff;

PORTD=0x00;

delayms(20); 

delayms(20);  

LCMInit(); //LCM初始化   //液晶初始化

delayms(100);

    clear(0x00);

heng(0);

heng(63);

su(0);

su(127);

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();

for(ii=64;ii<128;ii++)

 {

   tmp=sqrt((x[ii].real*x[ii].real)+(x[ii].img*x[ii].img));

   y= 63-(int)tmp;

point(ii-64,y);

xian(ii-64,y);

 }

for(ii=0;ii<64;ii++)

 {

   tmp=sqrt((x[ii].real*x[ii].real)+(x[ii].img*x[ii].img));

   y= 63-(int)tmp;

point(ii+64,y);

xian(ii+64,y);

 }

while(1);

}

得到的图片:

[转载]FFT算法在单片机中的使用&&LCD12864驱动

MATLAB仿真图形:
[转载]FFT算法在单片机中的使用&&LCD12864驱动

 

你可能感兴趣的:(FFT算法在单片机中的使用&&L…)