找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7943|回复: 14
打印 上一主题 下一主题
收起左侧

C51单片机FFT OLED显示程序

  [复制链接]
跳转到指定楼层
楼主
ID:460753 发表于 2020-4-8 11:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
经过10几天的努力,参考http://www.51hei.com/bbs/dpj-122556-1.html贴子,终于完成OLED C51频谱显示,实际测试频谱范围约500Hz-20KHz,64个点约150ms。
制作出来的实物图如下:



单片机源程序如下:
  1. /*
  2. 0.芯片为STC15W4K58S4
  3. 1.使用的为IIC OLED,端口为OLED_SDA=P0^1;OLED_SCL=P0^0;
  4. 2.AD输入为P1^6;
  5. 3.晶振选择为内置,24M;
  6. */

  7. #include <math.h>
  8. #include "OLED.h"
  9. #define FFT_N 64                                                   //定义福利叶变换的点数
  10. #define PI 3.1415926535897932384626433832795028841971               //定义圆周率值
  11. #define Process_sensitivity        0                                                                                //修改Process_sensitivity的数值个调整频谱变化幅度,不建议超过4
  12. unsigned char tmrcnt=0;
  13. float ADC_OutBuf[FFT_N/4];
  14. struct compx ADC_InBuf[FFT_N];                                      //FFT输入和输出:从S[0]开始存放,根据大小自己定义
  15. float SIN_TAB[FFT_N/4+1];                                           //定义正弦表的存放空间
  16. struct compx {float real,imag;};

  17. struct compx EE(struct compx a,struct compx b)      
  18. {
  19. struct compx c;
  20. c.real=a.real*b.real-a.imag*b.imag;
  21. c.imag=a.real*b.imag+a.imag*b.real;
  22. return(c);
  23. }

  24. void create_sin_tab(float *sin_t)                     
  25. {
  26.   int i;
  27.   for(i=0;i<=FFT_N/4;i++)
  28.   sin_t[i]=sin(2*PI*i/FFT_N);
  29. }

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

  56. float cos_tab(float pi)
  57. {
  58.    float a,pi2;
  59.    pi2=pi+PI/2;
  60.    if(pi2>2*PI)
  61.      pi2-=2*PI;
  62.    a=sin_tab(pi2);
  63.    return a;
  64. }

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

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

  164.         FFT(ADC_InBuf);

  165.         for(pt=2,i=0;pt<(FFT_N/2+1);pt+=2)
  166.         {
  167.                 ADC_OutBuf[i++] = sqrt(ADC_InBuf[pt].real*ADC_InBuf[pt].real+ADC_InBuf[pt].imag*ADC_InBuf[pt].imag);
  168.         }

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

fft5.png (1.11 MB, 下载次数: 119)

fft5.png

fft4.png (1.39 MB, 下载次数: 112)

fft4.png

fft3.png (999.53 KB, 下载次数: 113)

fft3.png

code.rar

65.44 KB, 下载次数: 213, 下载积分: 黑币 -5

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏20 分享淘帖 顶4 踩
回复

使用道具 举报

沙发
ID:582255 发表于 2020-4-17 22:23 | 只看该作者
楼主有仿真不
回复

使用道具 举报

板凳
ID:169891 发表于 2021-7-27 09:40 | 只看该作者
正需要用到FFT,谢谢楼主
回复

使用道具 举报

地板
ID:206695 发表于 2021-8-1 14:50 | 只看该作者
楼主能说说怎么用吗
回复

使用道具 举报

5#
ID:422023 发表于 2021-9-23 21:59 | 只看该作者
可否注释下processfft()里的每是怎么来的。得出的结果又是什么?
回复

使用道具 举报

6#
ID:156220 发表于 2022-7-2 09:32 | 只看该作者
参考参考,谢谢楼主的分享
回复

使用道具 举报

7#
ID:962396 发表于 2022-7-2 11:29 | 只看该作者

学习,谢谢楼主无私的分享
回复

使用道具 举报

8#
ID:164385 发表于 2022-7-3 12:05 | 只看该作者
亲,半年报把那两个工具也共享一下?谢谢!
回复

使用道具 举报

9#
ID:1063066 发表于 2023-2-14 22:37 | 只看该作者
不错 可以参考一下
回复

使用道具 举报

10#
ID:1063621 发表于 2023-7-15 17:42 | 只看该作者
你好,这个代码怎样设置频率,我想把显示的频率做更低一些60Hz-20K
回复

使用道具 举报

11#
ID:285069 发表于 2023-7-16 09:38 | 只看该作者
谢谢楼主无私的分享
回复

使用道具 举报

12#
ID:460753 发表于 2023-7-18 08:03 | 只看该作者
7631001 发表于 2022-7-3 12:05
亲,半年报把那两个工具也共享一下?谢谢!

两个工具,一个是音频信号发生器,一个是逻辑分析仪。上传了信号发生器,逻辑分析议我用的是金沙滩的,百度一下即可。

WaveGen3.8.rar

109.63 KB, 下载次数: 8, 下载积分: 黑币 -5

回复

使用道具 举报

13#
ID:582109 发表于 2023-7-22 22:28 | 只看该作者
有时间研究一下
回复

使用道具 举报

14#
ID:1017706 发表于 2023-9-15 17:13 | 只看该作者
刚好可以来测试一下,谢谢分享
回复

使用道具 举报

15#
ID:1098277 发表于 2023-11-2 22:36 | 只看该作者
STC15W4K58S4 支持float 类型吗?
是否可以直接在不支持float上跑
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表