找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3297|回复: 2
收起左侧

使用0.96OLED与STC单片机的音乐频谱程序

[复制链接]
ID:570019 发表于 2019-6-23 13:42 | 显示全部楼层 |阅读模式
自己写的一个项目,基于FFT算法实现的音乐频谱,利用OLED实现显示,需要在32M晶振运行

单片机源程序如下:
  1. #include"OLED.H"

  2. sbit sb0=P3^6;//定义按钮位置
  3. sbit sb1=P3^5;

  4. idata uchar fft_shuzu[2][32];
  5. uint s_time;

  6. struct compx                                                                           //定义复数结构体
  7. {
  8.         float real;
  9.         float imag;
  10. };
  11. xdata struct compx s[ 64 ];                                                 //FFT数据缓存放在XDATA空间
  12. struct compx EE(struct compx,struct compx);                 //复数乘法函数的声明
  13. void FFT(struct compx xin[],int N);                                  //FFT函数的声明
  14. struct compx EE(struct compx a1,struct compx b2)         //复数乘法
  15. {
  16.         struct compx b3;
  17.         b3.real=a1.real*b2.real-a1.imag*b2.imag;
  18.         b3.imag=a1.real*b2.imag+a1.imag*b2.real;
  19.         return(b3);
  20. }
  21. /*FFT函数*/
  22. void FFT(struct compx xin[],int N)                                  
  23. {
  24.         int f,m,nv2,nm1,i,k,j=1,l;
  25.         struct compx v,w,t;
  26.         nv2=N/2;
  27.         f=N;
  28.         for(m=1;(f=f/2)!=1;m++){;}
  29.         nm1=N-1;
  30.         for(i=0;i<nm1;i++)                                           //倒序操作
  31.         {
  32.                 if(i<j)
  33.                 {
  34.                         t=xin[j];
  35.                         xin[j]=xin[i];
  36.                         xin[i]=t;
  37.                 }
  38.                 k=nv2;                                                       //k为倒序中相应位置的权值
  39.                 while(k<j)
  40.                 {
  41.                         j=j-k;
  42.                         k=k/2;
  43.                 }
  44.                 j=j+k;
  45.         }
  46.         {
  47.                 int le,lei,ip;
  48.                   float pi;
  49.                   for(l=1;l<=m;l++)
  50.                    {
  51.                 le=pow(2,l);                                                 //乘方
  52.                     lei=le/2;
  53.                     pi=3.14159265;
  54.                     v.real=1.0;
  55.                 v.imag=0.0;  
  56.                    w.real=cos(pi/lei);                                           //旋转因子
  57.                     w.imag=-sin(pi/lei);
  58.                    
  59.                 for(j=1;j<=lei;j++)                                           //控制蝶形运算的级数
  60.                      {
  61.                         for(i=j-1;i<N;i=i+le)                                         //控制每级蝶形运算的次数
  62.                               {
  63.                                     ip=i+lei;
  64.                                        t=EE(xin[ ip ],v);
  65.                                        xin[ ip ].real=xin[ i ].real-t.real;   //蝶形计算
  66.                                        xin[ ip ].imag=xin[ i ].imag-t.imag;
  67.                                        xin[ i ].real=xin[ i ].real+t.real;
  68.                                        xin[ i ].imag=xin[ i ].imag+t.imag;
  69.                               }
  70.                               v=EE(v,w);   
  71.                       }     
  72.                    }
  73.           }
  74. }
  75. void showbar0()//延时跳帽函数
  76. {                               
  77.     unsigned char i,j,x,p,high,tigh,temp,itemp;
  78.         xdata unsigned char dis_rdata[16];
  79.         for(i=0;i<16;i++)          //读取FFT转换数据
  80.         {
  81.                 float t0=0;
  82.                 t0=sqrt(pow((s[i  ].real+s[i+1].real),2)+pow((s[i  ].imag+s[i+1].imag),2))/2;
  83.                 dis_rdata[i]=(unsigned char)t0;
  84.         }
  85. //        OLED_Display_On();
  86.         for(i=0;i<16;i++)//16个条形柱控制
  87.         {
  88.                 itemp=high=0;
  89.                 itemp=high=dis_rdata[i];//对处理变量赋值
  90. //                high=high/1;
  91.                 for(x=0;x<8;x++)//清空当前条形柱空间
  92.                 {
  93.                         OLED_Set_Pos(i*8,7-x);
  94.                         temp=0x00;
  95.                         for(j=0;j<8;j++)
  96.                         {
  97.                                 OLED_WR_Byte(temp,1);
  98.                         }
  99.                 }
  100.                
  101.                 if(high>64)high=64;//溢出值限制
  102.                 if(fft_shuzu[0][i]<=high)//检测当前大小
  103.                 {
  104.                         fft_shuzu[0][i]=high;//大于赋值当前频谱帽为此值
  105.                         fft_shuzu[1][i]=10;//重新设置当前延时
  106.                 }
  107.                 fft_shuzu[1][i]--;//当前延时自减
  108.                 if(fft_shuzu[1][i]==0)//如果当前延时为0
  109.                 {
  110.                         fft_shuzu[0][i]=high;//强度等于当前大小
  111.                 }
  112.                
  113.                 p=high/8;                //扫描图形生成
  114.                 tigh=high%8;
  115.                 for(x=0;x<p;x++)//填满单元区域
  116.                 {
  117.                         OLED_Set_Pos(i*8,7-x);
  118.                         temp=0xff;
  119.                         for(j=0;j<7;j++)
  120.                         {
  121.                                 OLED_WR_Byte(temp,1);
  122.                         }
  123.                 }
  124.                
  125.                 p=fft_shuzu[0][i]/8;//生成延时帽所在位置函数
  126.                 tigh=fft_shuzu[0][i]%8;
  127.                 if((itemp/8)==p)//如果与所在位置重合
  128.                 {
  129.                         OLED_Set_Pos(i*8,7-p);//到达所在位置
  130.                         temp=0x80>>tigh;//生成延时帽
  131.                         temp=temp|~0xff>>(high%8);//合并当前位置
  132.                         for(j=0;j<7;j++)//发送屏幕
  133.                         {
  134.                                 OLED_WR_Byte(temp,1);
  135.                         }
  136.                 }
  137.                 else//如果不重合
  138.                 {
  139.                         OLED_Set_Pos(i*8,7-p);//到达生成位置
  140.                         temp=0x80>>tigh;//生成延时帽
  141.                         for(j=0;j<7;j++)//发送屏幕
  142.                         {
  143.                                 OLED_WR_Byte(temp,1);
  144.                         }
  145.                        
  146.                         OLED_Set_Pos(i*8,7-(high/8));//到达非满数单元
  147.                         temp=~0xff>>(itemp%8);//生成条形柱
  148.                         for(j=0;j<7;j++)//发送
  149.                         {
  150.                                 OLED_WR_Byte(temp,1);
  151.                         }
  152.                 }
  153.         }
  154. }

  155. /*----以下生成函数原理差不多----*/

  156. void showbar1()//条形柱函数
  157. {                               
  158.     unsigned char i,j,x,p,high,tigh,temp;
  159.         xdata unsigned char dis_rdata[16];
  160.         for(i=0;i<16;i++)          //读取FFT转换数据
  161.         {
  162.                 float t0=0;
  163.                 t0=sqrt(pow((s[i  ].real+s[i+1].real),2)+pow((s[i  ].imag+s[i+1].imag),2))/2;
  164.                 dis_rdata[i]=(unsigned char)t0;
  165.         }
  166. //        OLED_Display_On();
  167.         for(i=0;i<16;i++)
  168.         {
  169.                 high=0;
  170.                 high=dis_rdata[i];
  171. //                high=high/1;
  172.                 for(x=0;x<8;x++)
  173.                 {
  174.                         OLED_Set_Pos(i*8,7-x);
  175.                         temp=0x00;
  176.                         for(j=0;j<8;j++)
  177.                         {
  178.                                 OLED_WR_Byte(temp,1);
  179.                         }
  180.                 }
  181.                 if(high>64)high=64;
  182.                 if(high==0)high=1;
  183.                 p=high/8;
  184.                 tigh=high%8;
  185.                 for(x=0;x<p;x++)
  186.                 {
  187.                         OLED_Set_Pos(i*8,7-x);
  188.                         temp=0xff;
  189.                         for(j=0;j<7;j++)
  190.                         {
  191.                                 OLED_WR_Byte(temp,1);
  192.                         }
  193.                 }
  194.                 OLED_Set_Pos(i*8,7-(high/8));
  195.                 temp=~0xff>>(high%8);
  196.                 for(j=0;j<7;j++)
  197.                 {
  198.                         OLED_WR_Byte(temp,1);
  199.                 }
  200.         }
  201. }

  202. void showbar2()//等号指示函数
  203. {                               
  204.     unsigned char i,j,x,p,high,tigh,temp,itemp;
  205.         xdata unsigned char dis_rdata[16];
  206.         for(i=0;i<16;i++)          //读取FFT转换数据
  207.         {
  208.                 float t0=0;
  209.                 t0=sqrt(pow((s[i  ].real+s[i+1].real),2)+pow((s[i  ].imag+s[i+1].imag),2))/2;
  210.                 dis_rdata[i]=(unsigned char)t0;
  211.         }
  212. //        OLED_Display_On();
  213.         for(i=0;i<16;i++)
  214.         {
  215.                 itemp=high=0;
  216.                 itemp=high=dis_rdata[i];
  217. //                high=high/1;
  218.                 for(x=0;x<8;x++)
  219.                 {
  220.                         OLED_Set_Pos(i*8,7-x);
  221.                         temp=0x00;
  222.                         for(j=0;j<8;j++)
  223.                         {
  224.                                 OLED_WR_Byte(temp,1);
  225.                         }
  226.                 }
  227.                 if(high>64)high=64;
  228.                 p=high/8;
  229.                 tigh=high%8;
  230.                 if(p==0)
  231.                 {
  232.                         p=1;
  233.                         temp=0x80;
  234.                 }
  235.                 else temp=0x88;
  236.                 for(x=0;x<p;x++)
  237.                 {
  238.                         OLED_Set_Pos(i*8,7-x);
  239.                         for(j=0;j<7;j++)
  240.                         {
  241.                                 OLED_WR_Byte(temp,1);
  242.                         }
  243.                 }
  244.         }
  245. }

  246. void showbar3()//延时跳帽函数取反
  247. {                               
  248.     unsigned char i,j,x,p,high,tigh,temp,itemp;
  249.         xdata unsigned char dis_rdata[16];
  250.         for(i=0;i<16;i++)          //读取FFT转换数据
  251.         {
  252.                 float t0=0;
  253.                 t0=sqrt(pow((s[i  ].real+s[i+1].real),2)+pow((s[i  ].imag+s[i+1].imag),2))/2;
  254.                 dis_rdata[i]=(unsigned char)t0;
  255.         }
  256. //        OLED_Display_On();
  257.         for(i=0;i<16;i++)
  258.         {
  259.                 itemp=high=0;
  260.                 itemp=high=dis_rdata[i];
  261. //                high=high/1;
  262.                 for(x=0;x<8;x++)
  263.                 {
  264.                         OLED_Set_Pos(i*8,7-x);
  265.                         temp=0xFF;
  266.                         for(j=0;j<8;j++)
  267.                         {
  268.                                 OLED_WR_Byte(temp,1);
  269.                         }
  270.                 }
  271.                
  272.                 if(high>64)high=64;
  273.                 if(fft_shuzu[0][i]<=high)
  274.                 {
  275.                         fft_shuzu[0][i]=high;
  276.                         fft_shuzu[1][i]=10;
  277.                 }
  278.                 fft_shuzu[1][i]--;
  279.                 if(fft_shuzu[1][i]==0)
  280.                 {
  281.                         fft_shuzu[0][i]=high;
  282.                 }
  283.                
  284.                 p=high/8;
  285.                 tigh=high%8;
  286.                 for(x=0;x<p;x++)
  287.                 {
  288.                         OLED_Set_Pos(i*8,7-x);
  289.                         temp=0x00;
  290.                         for(j=0;j<7;j++)
  291.                         {
  292.                                 OLED_WR_Byte(temp,1);
  293.                         }
  294.                 }
  295.                
  296.                 p=fft_shuzu[0][i]/8;
  297.                 tigh=fft_shuzu[0][i]%8;
  298.                 if((itemp/8)==p)
  299.                 {
  300.                         OLED_Set_Pos(i*8,7-p);
  301.                         temp=~0x80>>tigh;
  302.                         temp=temp|0xff>>(high%8);
  303.                         for(j=0;j<7;j++)
  304.                         {
  305.                                 OLED_WR_Byte(temp,1);
  306.                         }
  307.                 }
  308.                 else
  309.                 {
  310.                         OLED_Set_Pos(i*8,7-p);
  311.                         temp=~0x80>>tigh;
  312.                         for(j=0;j<7;j++)
  313.                         {
  314.                                 OLED_WR_Byte(temp,1);
  315.                         }
  316.                        
  317.                         OLED_Set_Pos(i*8,7-(high/8));
  318.                         temp=0xff>>(itemp%8);
  319.                         for(j=0;j<7;j++)
  320.                         {
  321.                                 OLED_WR_Byte(temp,1);
  322.                         }
  323.                 }
  324.         }
  325. }

  326. void showbar4()//条形柱取反函数
  327. {                               
  328.     unsigned char i,j,x,p,high,tigh,temp;
  329.         xdata unsigned char dis_rdata[16];
  330.         for(i=0;i<16;i++)          //读取FFT转换数据
  331.         {
  332.                 float t0=0;
  333.                 t0=sqrt(pow((s[i  ].real+s[i+1].real),2)+pow((s[i  ].imag+s[i+1].imag),2))/2;
  334.                 dis_rdata[i]=(unsigned char)t0;
  335.         }
  336. //        OLED_Display_On();
  337.         for(i=0;i<16;i++)
  338.         {
  339.                 high=0;
  340.                 high=dis_rdata[i];
  341. //                high=high/1;
  342.                 for(x=0;x<8;x++)
  343.                 {
  344.                         OLED_Set_Pos(i*8,7-x);
  345.                         temp=0xff;
  346.                         for(j=0;j<8;j++)
  347.                         {
  348.                                 OLED_WR_Byte(temp,1);
  349.                         }
  350.                 }
  351.                 if(high>64)high=64;
  352.                 if(high==0)high=1;
  353.                 p=high/8;
  354.                 tigh=high%8;
  355.                 for(x=0;x<p;x++)
  356.                 {
  357.                         OLED_Set_Pos(i*8,7-x);
  358.                         temp=0x00;
  359.                         for(j=0;j<7;j++)
  360.                         {
  361.                                 OLED_WR_Byte(temp,1);
  362.                         }
  363.                 }
  364.                 OLED_Set_Pos(i*8,7-(high/8));
  365.                 temp=0xff>>(high%8);
  366.                 for(j=0;j<7;j++)
  367.                 {
  368.                         OLED_WR_Byte(temp,1);
  369.                 }
  370.         }
  371. }

  372. void showbar5()//等号指示函数
  373. {                               
  374.   unsigned char i,j,x,p,high,tigh,temp,itemp;
  375.         xdata unsigned char dis_rdata[16];
  376.         for(i=0;i<16;i++)          //读取FFT转换数据
  377.         {
  378.                 float t0=0;
  379.                 t0=sqrt(pow((s[i  ].real+s[i+1].real),2)+pow((s[i  ].imag+s[i+1].imag),2))/2;
  380.                 dis_rdata[i]=(unsigned char)t0;
  381.         }
  382. //        OLED_Display_On();
  383.         for(i=0;i<16;i++)
  384.         {
  385.                 itemp=high=0;
  386.                 itemp=high=dis_rdata[i];
  387. //                high=high/1;
  388.                 for(x=0;x<8;x++)
  389.                 {
  390.                         OLED_Set_Pos(i*8,7-x);
  391.                         temp=0xff;
  392.                         for(j=0;j<8;j++)
  393.                         {
  394.                                 OLED_WR_Byte(temp,1);
  395.                         }
  396.                 }
  397.                 if(high>64)high=64;
  398.                 p=high/8;
  399.                 tigh=high%8;
  400.                 if(p==0)
  401.                 {
  402.                         p=1;
  403.                         temp=~0x80;
  404.                 }
  405.                 else temp=~0x88;
  406.                 for(x=0;x<p;x++)
  407.                 {
  408.                         OLED_Set_Pos(i*8,7-x);
  409.                         for(j=0;j<7;j++)
  410.                         {
  411.                                 OLED_WR_Byte(temp,1);
  412.                         }
  413.                 }
  414.         }
  415. }

  416. /*主函数*/
  417. void main()                                                                    
  418. {
  419.         int N=64,i,k;                                                             //变量初始化,64点FFT运算
  420.         float offset;
  421.         s_time=0;
  422.        
  423.         TMOD=0X01;                                                //定时器开到模式1
  424.         TH0=(65535-10000)/256;                        //设置延时时间
  425.         TL0=(65535-10000)%256;
  426.         TR0=1;                                                        //启动定时器0
  427.         ET0=1;                                                        //开启定时器0中断
  428.         EA=1;                                                        //开启中断
  429.        
  430.         oled_init();  //OLED
  431.         OLED_Clear();
  432.        
  433.         P1ASF=0x01;                                                                      //P10口做AD 使用
  434.         P1M0 = 0x01;                                                //0000,0001用于A/D转换的P1.x口,先设为开漏
  435.         P1M1 = 0x01;                                                //0000,0001 P1.0先设为开漏。断开内部上拉电阻
  436.         ADC_CONTR=0xC8;                                                                 //40.96K采样率
  437.         while(!(ADC_CONTR&0x10));
  438.         offset=((float)ADC_RES*4+(float)(ADC_RESL%0x04)); //AD结果高8位左移2位,低2位不变,然后相加
  439.         while(1)
  440.         {                if(P3==(P3&0xFE))IAP_CONTR=0x60;               
  441.                         for(i=0;i<N;i++)                                      //采集音频信号
  442.                           {
  443.                                                 ADC_CONTR=0xC8;                                         //40.96K采样率
  444.                                                 while(!(ADC_CONTR&0x10));
  445.                                                         s[i].real=((float)ADC_RES*4+(float)(ADC_RESL%0x04)-offset);//((((int)ADC_DATA-128)/2))*4;
  446.                                                         s[i].imag=0;
  447.                           }
  448.                           FFT(s,N);                        //调用FFT函数进行变换
  449.                         EA=0;
  450.                         if(sb0==0)                        //模式调节按钮控制
  451.                         {
  452.                                 delay(100);
  453.                                 if(sb0==0)
  454.                                 {
  455.                                         while(!sb0);
  456.                                         delay(100);
  457.                                         k+=1;
  458.                                         if(k==6)k=0;
  459.                                 }
  460.                         }
  461.                         if(sb1==0)                        //延时调节按钮控制
  462.                         {
  463.                                 delay(100);
  464.                                 if(sb1==0)
  465.                                 {
  466.                                         while(!sb1);
  467.                                         delay(100);
  468.                                         s_time+=100;
  469.                                         if(s_time==1100)s_time=0;
  470.                                 }
  471.                         }
  472.                         switch(k)                        //显示频谱
  473.                         {
  474.                                 case 0:showbar0();break;                        //延时跳帽函数
  475.                                 case 1:showbar1();break;                        //条形柱函数
  476.                                 case 2:showbar2();break;                        //等号条函数
  477.                                 case 3:showbar3();break;                        //延时跳帽函数取反
  478.                                 case 4:showbar4();break;                        //条形柱函数取反
  479.                                 case 5:showbar5();break;                        //等号条函数取反
  480.                         }
  481.                         EA=1;
  482.                         delay(s_time);//显示延时函数
  483.         }
  484. }

  485. void yp_l()interrupt 1
  486. {
  487.        
  488.         TH0=(65535-10000)/256;                //设置延时时间
  489.         TL0=(65535-10000)%256;
  490. }
复制代码

所有程序51hei提供下载:
OLED FFT.rar (72.2 KB, 下载次数: 146)

评分

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

查看全部评分

回复

使用道具 举报

ID:546014 发表于 2019-10-30 22:14 | 显示全部楼层
有原理图吗
回复

使用道具 举报

ID:48994 发表于 2021-9-20 14:31 | 显示全部楼层
感谢分享,正在学习这方面的知识!
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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