[技术问答] 8位单片机实现FFT

[复制链接]
 楼主| jonas222 发表于 2023-7-28 20:00 | 显示全部楼层 |阅读模式
  1. #include <math.h>
  2. #include "OLED.h"
  3. #define FFT_N 64                                                   //定义福利叶变换的点数
  4. #define PI 3.1415926535897932384626433832795028841971               //定义圆周率值
  5. #define Process_sensitivity        0                                                                                //修改Process_sensitivity的数值个调整频谱变化幅度,不建议超过4
  6. unsigned char tmrcnt=0;
  7. float ADC_OutBuf[FFT_N/4];
  8. struct compx ADC_InBuf[FFT_N];                                      //FFT输入和输出:从S[0]开始存放,根据大小自己定义
  9. float SIN_TAB[FFT_N/4+1];                                           //定义正弦表的存放空间
  10. struct compx {float real,imag;};

  11. struct compx EE(struct compx a,struct compx b)      
  12. {
  13. struct compx c;
  14. c.real=a.real*b.real-a.imag*b.imag;
  15. c.imag=a.real*b.imag+a.imag*b.real;
  16. return(c);
  17. }

  18. void create_sin_tab(float *sin_t)                     
  19. {
  20.   int i;
  21.   for(i=0;i<=FFT_N/4;i++)
  22.   sin_t[i]=sin(2*PI*i/FFT_N);
  23. }

  24. float sin_tab(float pi)
  25. {
  26.   int n;
  27.   float a=0;
  28.    n=(int)(pi*FFT_N/2/PI);
  29.    
  30.   if(n>=0&&n<=FFT_N/4)
  31.     a=SIN_TAB[n];
  32.   else if(n>FFT_N/4&&n<FFT_N/2)
  33.     {
  34.      n-=FFT_N/4;
  35.      a=SIN_TAB[FFT_N/4-n];
  36.     }
  37.   else if(n>=FFT_N/2&&n<3*FFT_N/4)
  38.     {
  39.      n-=FFT_N/2;
  40.      a=-SIN_TAB[n];
  41.    }
  42.   else if(n>=3*FFT_N/4&&n<3*FFT_N)
  43.     {
  44.      n=FFT_N-n;
  45.      a=-SIN_TAB[n];
  46.    }
  47.   
  48.   return a;
  49. }

  50. float cos_tab(float pi)
  51. {
  52.    float a,pi2;
  53.    pi2=pi+PI/2;
  54.    if(pi2>2*PI)
  55.      pi2-=2*PI;
  56.    a=sin_tab(pi2);
  57.    return a;
  58. }

  59. void FFT(struct compx *xin)
  60. {
  61.   register int f,m,nv2,nm1,i,k,l,j=0;
  62.   struct compx u,w,t;
  63.    
  64.    nv2=FFT_N/2;                  //变址运算,即把自然顺序变成倒位序,采用雷德算法
  65.    nm1=FFT_N-1;  
  66.    for(i=0;i<nm1;i++)        
  67.    {
  68.     if(i<j)                    //如果i<j,即进行变址
  69.      {
  70.       t=xin[j];           
  71.       xin[j]=xin[i];
  72.       xin[i]=t;
  73.      }
  74.     k=nv2;                    //求j的下一个倒位序
  75.     while(k<=j)               //如果k<=j,表示j的最高位为1   
  76.      {           
  77.       j=j-k;                 //把最高位变成0
  78.       k=k/2;                 //k/2,比较次高位,依次类推,逐个比较,直到某个位为0
  79.      }
  80.    j=j+k;                   //把0改为1
  81.   }
  82.                         
  83.   {
  84.    int le,lei,ip;                            //FFT运算核,使用蝶形运算完成FFT运算
  85.     f=FFT_N;
  86.    for(l=1;(f=f/2)!=1;l++)                  //计算l的值,即计算蝶形级数
  87.            ;
  88.   for(m=1;m<=l;m++)                         // 控制蝶形结级数
  89.    {                                        //m表示第m级蝶形,l为蝶形级总数l=log(2)N
  90.     le=2<<(m-1);                            //le蝶形结距离,即第m级蝶形的蝶形结相距le点
  91.     lei=le/2;                               //同一蝶形结中参加运算的两点的距离
  92.     u.real=1.0;                             //u为蝶形结运算系数,初始值为1
  93.     u.imag=0.0;
  94.     w.real=cos_tab(PI/lei);                //w为系数商,即当前系数与前一个系数的商
  95.     w.imag=-sin_tab(PI/lei);
  96.     for(j=0;j<=lei-1;j++)                  //控制计算不同种蝶形结,即计算系数不同的蝶形结
  97.      {
  98.       for(i=j;i<=FFT_N-1;i=i+le)           //控制同一蝶形结运算,即计算系数相同蝶形结
  99.        {
  100.         ip=i+lei;                          //i,ip分别表示参加蝶形运算的两个节点
  101.         t=EE(xin[ip],u);                   //蝶形运算,详见公式
  102.         xin[ip].real=xin[i].real-t.real;
  103.         xin[ip].imag=xin[i].imag-t.imag;
  104.         xin[i].real=xin[i].real+t.real;
  105.         xin[i].imag=xin[i].imag+t.imag;
  106.        }
  107.       u=EE(u,w);                          //改变系数,进行下一个蝶形运算
  108.      }
  109.    }
  110.   }
  111.   
  112. }
  113. void Timer0Init(void)                //1毫秒@24.000MHz
  114. {
  115.         AUXR |= 0x80;                //定时器时钟1T模式
  116.         TMOD &= 0xF0;                //设置定时器模式
  117.         TL0 = 0x40;                //设置定时初值
  118.         TH0 = 0xA2;                //设置定时初值
  119.         TF0 = 0;                //清除TF0标志
  120.         TR0 = 1;                //定时器0开始计时
  121.         ET0 = 1;
  122. }
  123. void Delay(unsigned short n)
  124. {
  125.     unsigned short x;

  126.     while (n--)
  127.     {
  128.         x = 5000;
  129.         while (x--);
  130.     }
  131. }
  132. void InitADC()
  133. {
  134.     P1ASF = 0xff;                   //设置P1口为AD口
  135.     ADC_CONTR =0x80|0x60;
  136.     Delay(2);                       //ADC上电并延时
  137. }
  138. unsigned short GetADCResult(unsigned char ch)//ch为输入端口
  139. {
  140.         ADC_RES = 0;
  141.         ADC_RESL = 0;
  142.     ADC_CONTR =0x80|0x60|ch|0x08;
  143.     _nop_();                        
  144.     _nop_();
  145.     _nop_();
  146.     _nop_();
  147.     while (!(ADC_CONTR & 0x10));
  148.     ADC_CONTR &= ~0x10;         
  149.     return ((unsigned short)(ADC_RES<<2)+ADC_RESL);
  150. }
  151. void processfft(void)
  152. {
  153.         unsigned char data pt=0,i,high,x,temp,p,j;
  154.         for(pt=0;pt<(FFT_N);pt++)
  155.         {
  156.                 ADC_InBuf[pt].imag=0;
  157.         }

  158.         FFT(ADC_InBuf);

  159.         for(pt=2,i=0;pt<(FFT_N/2+1);pt+=2)
  160.         {
  161.                 ADC_OutBuf[i++] = sqrt(ADC_InBuf[pt].real*ADC_InBuf[pt].real+ADC_InBuf[pt].imag*ADC_InBuf[pt].imag);
  162.         }

  163.         for(i=0;i<(FFT_N/4);i++)
  164.         {
  165.                 high=(((unsigned char)ADC_OutBuf[i])>>Process_sensitivity);//修改Process_sensitivity的数值个调整频谱变化幅度,不建议超过4
  166.                 for(x=0;x<8;x++)
  167.                 {
  168.                         OLED_SetPos(i*8,7-x);
  169.                         temp=0x00;
  170.                         for(j=0;j<8;j++)
  171.                         {
  172.                                 OLED_WriteData(temp);
  173.                         }
  174.                 }
  175.                 if(high>64)high=64;
  176.                 if(high==0)high=1;
  177.                 p=high/8;
  178.                 for(x=0;x<p;x++)
  179.                 {
  180.                         OLED_SetPos(i*8,7-x);
  181.                         temp=0xff;
  182.                         for(j=0;j<7;j++)
  183.                         {
  184.                                 OLED_WriteData(temp);
  185.                         }
  186.                 }
  187.                 OLED_SetPos(i*8,7-(high/8));
  188.                 temp=~0xff>>(high%8);
  189.                 for(j=0;j<7;j++)
  190.                 {
  191.                         OLED_WriteData(temp);
  192.                 }
  193.         }
  194. }
  195. void main()
  196. {
  197.         unsigned char i;
  198.         P0M1=0x00;
  199.         P0M0=0x00;
  200.         P1M1=0xc0; //设定AD输入为P16/P17,实际只用了P16
  201.         P1M0=0x00;
  202.         EA=1;
  203.         Oled_Init();
  204.         Timer0Init();
  205.         InitADC();
  206.         create_sin_tab(SIN_TAB);
  207.         while(1)
  208.         {
  209.                 if(tmrcnt>=70)
  210.                 {
  211.                         P55=1;
  212.                         for(i=0;i<(FFT_N);i++)
  213.                         {  
  214.                                 ADC_InBuf[i].real=((GetADCResult(6))<<Process_sensitivity);//修改Process_sensitivity的数值个调整频谱变化幅度,不建议超过4
  215.                         }
  216.                         
  217.                         processfft();
  218.                         P55=0;
  219.                         tmrcnt=0;
  220.                 }
  221.         }
  222. }
  223. void Time0() interrupt 1
  224. {
  225.         tmrcnt++;
  226. }


tpgf 发表于 2023-8-10 16:28 | 显示全部楼层
8位单片机实现fft是不是需要对算法进行精简啊
八层楼 发表于 2023-8-10 17:19 | 显示全部楼层
如果想要实现fft的话 对单片机的最低要求是什么
观海 发表于 2023-8-10 17:38 | 显示全部楼层
FFT是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的.
guanjiaer 发表于 2023-8-10 17:58 | 显示全部楼层
点数越多,运算量的节约就越大,这就是FFT的优越性
heimaojingzhang 发表于 2023-8-10 18:18 | 显示全部楼层
fft计算的深度有上限吗 大概是多少啊
keaibukelian 发表于 2023-8-11 08:59 | 显示全部楼层
感觉fft的算法真是挺复杂的 根本看不懂啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

44

主题

1654

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

44

主题

1654

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部