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

2019-04-15 17:41发布

原文地址:FFT算法在单片机中的使用&&LCD12864驱动作者:元大帝  好久没更新博客了,觉得对不起自己建立博客的初衷。我这个人太懒了,又没有坚持下去的决心,唉~     言归正传,本次创新基金我是要做一个简易的频谱仪,核心就是要进行一个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 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>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算法,我进行了些改进,原则就是尽量地少使用变量,一个变量可以重复的使用是最理想的了,大家可以在程序中看出。个人意见这是能节省变量最少的了,如果有好的方法,希望可以告诉我下,我的邮箱是albertvictordu@139.com,谢谢!

   下面是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 #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;

void initw();                                  //初始化旋转因子 void bitReverse();                             //比特反转 void FFT();
complex x[N]; uchar vis[N];

void delayms(uint ms) {     uint i,j;  for(i=0;i
//写数据 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< 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>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驱动