用8051做的16x16音乐频谱显示,内有问题请指教TvT

2019-07-18 10:27发布

原理图如下:
无标题.png

代码分为两部分,一个C51一个汇编的~



  1. #include "REG51.H"
  2. #include "intrins.h"

  3. //--------------------常量定义-------------------------

  4. //ORDER是FFT所需要进行的阶段数目
  5. //N16 = 2^ORDER,这两个量是相互对应的
  6. #define ORDER 4
  7. #define N16 16  

  8. //为了以整型变量实现小数计算,将小数乘上(2^SCALE),算好后再除回去
  9. #define SCALE 4

  10. //输入衰减
  11. #define IN_SCALE_DOWN 2

  12. //峰值点(帽子)停留的时间
  13. #define HAT_STAY_tiME 4

  14. //计时器T1的初值,以控制采样率
  15. #define SAMP_TIMER 0xfE//FE
  16. //0xCE   50us, i.e. sample rate = 20000 HZ
  17. //0x9C  100us, i.e. sample rate = 10000 HZ
  18. //0x38  200us, i.e. sample rate =  5000 HZ
  19. //就算时钟设得很短,采样率也只能达到 6000Hz. one 750Hz sound wave can be put in 8 sample points. 750x8=6000

  20. //AD模块需要的控制端定义
  21. sbit AD_OUT = P1^5;
  22. sbit AD_CLK = P1^7;
  23. sbit AD_CS  = P1^6;
  24. //切换示波器与频谱显示的开关, 接在8位逻辑电平输出中的其中一个
  25. sbit SWITCH1= P3^3;

  26. //声明复数结构类型
  27. typedef struct
  28. {
  29.         char real;
  30.         char imag;
  31. }complex_t;

  32. //声明双字节的复数结构类型,用于中间运算过程
  33. typedef struct
  34. {
  35.         int real;
  36.         int imag;
  37. }complex_long_t;


  38. //--------------------查表-------------------------

  39. //FFT需要将数据下标按位倒序重排,此表列出了旧下标与新下标的对应关系
  40. char code reverse_table_16[16]={0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};

  41. //复旋转因子表,依赖于SCALE==4,并且由下面两个式子给出
  42. //twiddle_table[i].real=(char)(  cos(2*PI*i/N16)  * (1<<SCALE)  );
  43. //twiddle_table[i].imag=(char)(  sin(2*PI*i/N16)  * (1<<SCALE)  );
  44. complex_t code twiddle_table[N16/2]={
  45.         16,0, 14,6, 11,11, 6,14, 0,16, -6,14, -11,11, -14,6,
  46. };

  47. //FFT向DCT转化所需要的系数,16个计算结果分别乘上这些系数。它们由下面式子给出
  48. //ww = exp(-1i*(0:n-1) * pi/(2*n))  /  sqrt(2*n)   , n=16
  49. //ww(0) = ww(0) / sqrt(2);
  50. complex_t code dct_coefficient[N16]={
  51.         4,0, 6,-1, 6,-1, 5,-2, 5,-2, 5,-3, 5,-3, 4,-4, 4,-4, 4,-4, 3,-5, 3,-5, 2,-5, 2,-5, 1,-6, 1,-6       
  52. };

  53. //--------------------全局变量-------------------------
  54. //峰值点所处位置
  55. unsigned char xdata hatpos [N16];
  56. //峰值点停留时间(倒计时)
  57. unsigned char xdata hattime[N16] _at_ 0x0040;
  58. //峰值点的值
  59. unsigned char xdata hat    [N16] _at_ 0x0030;

  60. //采样得到的原始数据
  61. char xdata src[N16] _at_ 0x0020;

  62.                                 //计算得到的频谱数据
  63.                                 char xdata am0[N16] _at_ 0x0010;

  64. //显示模块的数据暂存
  65. char data        am [N16] _at_ 0x5F;

  66.                                 //显示模块的“行状态”标记
  67.                                 char data lstate;

  68. //零频补偿,将输入信号用软件的方法调整到平均值为零,因为硬件电路调整偏置到0以后,会有一定的漂移
  69. unsigned char data in_offset=0x07A;


  70. //--------------------函数-------------------------

  71. //汇编函数:显示中断服务子程序。由计时器T0控制,每次计满则调用一次
  72. extern void display_int(void);

  73. //实现两个复数的乘法。输入为单字节复数,输出为双字节复数
  74. complex_long_t fft_mult(complex_t a,complex_t b){
  75.         complex_long_t c;
  76.         c.real=(int)a.real * b.real - (int)a.imag * b.imag;
  77.         c.imag=(int)a.real * b.imag + (int)a.imag * b.real;
  78.         return c;
  79. }
  80. //实现两个复数的加法。并且假定b已经乘上了(2^SCALE),而a没有乘
  81. complex_t fft_add(complex_t a,complex_long_t b){
  82.         complex_t c;
  83.         c.real  = (char)((((int)a.real << SCALE) + b.real) >> SCALE);
  84.         c.imag  = (char)((((int)a.imag << SCALE) + b.imag) >> SCALE);
  85.         return c;
  86. }
  87. //原理同“复数的加法”
  88. complex_t fft_sub(complex_t a,complex_long_t b){
  89.         complex_t c;
  90.         c.real = (char)((((int)a.real << SCALE) - b.real) >> SCALE);
  91.         c.imag = (char)((((int)a.imag << SCALE) - b.imag) >> SCALE);
  92.         return c;
  93. }

  94. //中断初始化
  95. void ini_interrupt(){
  96.         lstate=0x80; //显示屏“行状态”的初始化  
  97.         EA=1;
  98.         IP=0;//
  99.         TMOD=0;
  100.         TCON=0;
  101.        
  102.         //INT0        Power off
  103.         EX0=1;
  104.         IT0=0;////////////////////低电平触发
  105.        
  106.         //Timer0        Display
  107.         ET0=1;
  108.         TMOD |= 0x01;    //M1 M0 = 0 1
  109.         TH0=0x00;
  110.         TL0=0x00;
  111.        
  112.         //Timer1       
  113.         ET1=0; //查寻法
  114.         TMOD |= 0x20;        //M1 M0 = 1 0
  115.         TH1=SAMP_TIMER;   
  116.         TL1=SAMP_TIMER;

  117.         TCON=0x050;        //TR0 TR1
  118. }



  119. //单个数据点的采集/////////////////////采到的是单数据点8位数据
  120. char AD_convert(void)
  121. {
  122.         char i= 0;
  123.         char temp=0;
  124.         for(i=0;i<8;i++)
  125.         {
  126.                 AD_CLK=1;        //AD_CLK=1;       
  127.                 AD_CLK=0;        //AD_CLK=0;
  128.                 temp<<=1;
  129.                 temp|=AD_OUT;
  130.         }
  131.        
  132.         AD_CS=1;        //AD_CS=1;
  133.         return temp;
  134. }




  135. //16个数据点的采集/////////////////////////////////////////////VOID函数,数据放进SRC
  136. void sample16()
  137. {
  138.         char n,k;
  139.         char sum=0
  140.        
  141.         TF1=0;       
  142.         lstate=0x080;//if runs to here, means there's a new set of am0[] to be display.
  143.         TR0=0
  144.         for (n=-1;n<N16;n++
  145.         {
  146.                 while (TF1==0);
  147.                 TR1=0;
  148.                 TF1=0;
  149.                 k=(  AD_convert()-in_offset  );
  150.                 TR1=1;
  151.                 k>>=IN_SCALE_DOWN;  
  152.                 sum+=k;
  153.                 if (k>7)
  154.                         k=7;
  155.                 else if(k<-7)
  156.                         k=-7;
  157.        
  158.                 if(n>=0)
  159.                 {
  160.                         src[n]=k;
  161.                 }
  162.         }
  163.         if (sum>5)
  164.         {
  165.                 in_offset++;
  166.         }
  167.         else if (sum<-5)
  168.         {
  169.                 in_offset--;
  170.         }
  171.         P0=~in_offset;
  172.         TR0=1;
  173. }


  174. //主程序
  175. void main(void)
  176. {
  177.         //局部变量的含义将在调用时说明
  178.         char n, k, m;
  179.         char k2;
  180.         char twiddle;
  181.         char twiddle_jump;
  182.         char twiddle_max;
  183.         char n_jump;
  184.         char n_start;
  185.         complex_long_t tmp_product;
  186.         complex_t twiddle_factor;
  187.         complex_t dest[N16];
  188.         char finaltmp;
  189.        
  190.         ini_interrupt();





  191. //主循环
  192.         while(1)
  193.         {

  194.                         sample16();

  195.                         for(n=0; n < N16; n++)
  196.                         {
  197.                                
  198.                                 //同时进行两项“重新排序”,
  199.                                 //src方括号内的式子实现奇偶分离,
  200.                                 //dest方括号内的式子实现FFT的下标按位倒序
  201.                                 if (n<(N16/2))
  202.                                 {
  203.                                         dest[reverse_table_16[n]].real = src[n*2];
  204.                                 }
  205.                                 else
  206.                                 {
  207.                                         dest[reverse_table_16[n]].real = src[(2*N16-1)-2*n];
  208.                                 }
  209.                                 dest[reverse_table_16[n]].imag = 0;
  210.                         }

  211.                         for(k=0; k < ORDER; k++)
  212.                         {
  213.                                 n = 0;
  214.                                 k2 = (1<<k);

  215.                                 //计算旋转因子的单次旋转量,和最大旋转量
  216.                                 twiddle_max = (1<<( ORDER - 1));
  217.                                 twiddle_jump = (1<<( ORDER - k - 1)); //equals 2^(kmax - k)

  218.                                 //蝴蝶翅膀的翼展
  219.                                 n_jump = k2<<1;
  220.                                 n_start = 0;

  221.                                 for(twiddle = 0; twiddle < twiddle_max; twiddle += twiddle_jump)
  222.                                 {

  223.                                         //旋转因子,由对称性,只取 < N/2
  224.                                         twiddle_factor.real = twiddle_table[twiddle].real;
  225.                                         twiddle_factor.imag = -twiddle_table[twiddle].imag;

  226.                                         for(n=n_start; n < N16; n+= n_jump)
  227.                                         {
  228.                                                 m = n+k2;
  229.                                                 //单次蝶形运算
  230.                                                 tmp_product = fft_mult(dest[m], twiddle_factor);  

  231.                                                 dest[m] = fft_sub(dest[n], tmp_product);
  232.                                                 dest[n] = fft_add(dest[n], tmp_product);
  233.                                         }
  234.                                         n_start++;
  235.                                 }
  236.                         }
  237.                        
  238. ////////////////////////////////////////////进行计算结果的处理//////////////////////////////////////////////////////////////////////
  239.                         n=0;
  240.                         for (k=0;k<N16;k++)
  241.                         {
  242.                                
  243.                                 //调整系数
  244.                                 finaltmp = ( dest[k].real * dct_coefficient[k].real  -  dest[k].imag * dct_coefficient[k].imag ) >> SCALE;

  245.                                 //取绝对值
  246.                                 if (finaltmp<0)
  247.                                 {
  248.                                         finaltmp = -finaltmp;
  249.                                 }
  250.                                 if (finaltmp>8)
  251.                                 {
  252.                                         finaltmp=8;
  253.                                 }
  254.                                 if(finaltmp>0)
  255.                                 {
  256.                                         am0[k]=finaltmp;
  257.                                 else
  258.                                 {
  259.                                         am0[k]=0;
  260.                                 }
  261.                           }
  262.        
  263.         }
  264.        
  265. return;

  266. }
复制代码
  1. ;计时器0初值
  2. TH_INI EQU 0xFC;f4
  3. TL_INI EQU 0x06;06

  4. ;连线: P1.0、P1.1、P1.2分别与LED双 {MOD}点阵显示模块的SCLK 、RCLK、DIN相连
  5. LED_CLK                BIT        P1.3;
  6. LED_CLK2        BIT        P1.4
  7. LED_RCLK        BIT        P1.2;
  8. LED_DIN                BIT        P1.0;
  9. LED_DIN2        BIT        P1.1

  10. EXTRN        DATA (lstate)
  11. EXTRN        DATA (am)
  12. EXTRN        XDATA (am0)
  13. ;EXTRN        XDATA (hatpos)


  14. ;显示中断服务子程序
  15. CSEG        AT        0BH                                                ;连接器必须把函数入口放在0BH处
  16.         LJMP        _display_int

  17. ?PR?_display_int?MAIN SEGMENT CODE         ;在程序存储区中定义段

  18. PUBLIC _display_int                                        ;声明全局函数

  19. RSEG  ?PR?_display_int?MAIN                        ;函数可被连接器放在任何地方。
  20.        
  21. USING        2












  22. _display_int:
  23.         PUSH         ACC
  24.         PUSH         DPH
  25.         PUSH         DPL
  26.         PUSH         PSW
  27.         USING        2
  28.         MOV          PSW,#10H

  29.         CLR        P2.0
  30.         SETB        P2.0

  31.         MOV          TH0,#TH_INI
  32.         MOV          TL0,#TL_INI
  33.         ;CLR                TF0

  34.         ;刷新显示;;;;;;;;;;;;;;;;;;;;;;;;;;;;;左移一位,向上选通一行;;;;;;;;;;;;;;;;循环的
  35.         MOV                A,lstate
  36.         RL                A
  37.         MOV                lstate,A
  38.         CJNE                A,#1,skip_copy;;;;;;;;;不相等则跳转
  39.     ;复制数据//////////////////////////////////////////////////////////////////////////am0复制到am中
  40.         MOV                DPTR,#am0
  41.         MOV                R1,#((am)+15)
  42.         MOV                R7,#16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;R7=16
  43.         copy_loop:
  44.                 MOVX        A,@DPTR;
  45.                 MOV        @R1,A;
  46.                 INC        DPTR
  47.                 DEC        R1
  48.                 DJNZ        R7,copy_loop;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;R7用于计数



  49.         ;选中需要输出的行
  50.         MOV                A,lstate
  51.         MOV                R7,#8    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;R7=8

  52.         line_out1:

  53.                 CLR                LED_CLK       
  54.                 RRC                A
  55.                 MOV                LED_DIN,C
  56.                 CLR                C
  57.                 NOP
  58.                 NOP;;;                       
  59.                 SETB                LED_CLK
  60.                 NOP;;;
  61.                 NOP
  62.                 DJNZ                R7,line_out1;;;;;;;;;;;;;;;;;;;;;;;;R7计数
  63.        
  64. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;行信息录入完毕;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  65.         MOV                R0,#LOW (am)
  66.         mov                r1,#low(am)
  67.         MOV                R7,#2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;R7=2
  68.         disp_loop7:       
  69.                
  70.                 ;绿 {MOD},用于显示频谱的瞬时值
  71.                 MOV        R5,#8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;R5=8
  72.                 disp_loop5:
  73.                                 CJNE        @R0,#0,a_not_0
  74.                                 SETB        LED_DIN2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LED_DIN2
  75.                                 NOP
  76.                                 NOP
  77.                                 CLR        LED_CLK2
  78.                                 NOP
  79.                                 NOP
  80.                                 SETB        LED_CLK2
  81.                         SJMP        end_a_not_0
  82.                         a_not_0:
  83.                                 CLR                LED_DIN2
  84.                                 NOP
  85.                                 NOP
  86.                                 CLR                LED_CLK2
  87.                                 NOP
  88.                                 NOP
  89.                                 SETB                LED_CLK2
  90.                                 NOP
  91.                                 DEC                @R0
  92.                         end_a_not_0:
  93.                         INC         R0
  94.                 DJNZ        R5,disp_loop5;;;;;;;;;;;;;;;;;;;;;;;;;R5计数
  95.                
  96.         DJNZ        R7,disp_loop7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;R7计数
  97.         SETB        LED_RCLK      
  98.         NOP
  99.         CLR        LED_RCLK
  100.        
  101.            POP          PSW
  102.         POP          DPL
  103.         POP          DPH
  104.         POP          ACC
  105. RETI


  106. END
复制代码




因为基于别人的程序修改的,我感觉都看明白了。但是实际仿真时发现,输入一个正弦波,16x16LED屏依旧在跳动,而不是某一频率上强度以稳定高度显示。
我想,采集16个数据进行FFT,在16个频段上显示这件事靠谱嘛?鉴于感觉很多例子都是这么做出来的,我想一定是程序里有问题,但是到底是哪里有问题呢TwT


请教各位大大了!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
2条回答
武力戡乱
1楼-- · 2019-07-18 15:54
我新手,但也是单片机爱好者。上来,in_offset  变量的作用就不理解,以至于不能理解 k=(  AD_convert()-in_offset  );这个是干嘛的,尤其in_offset  变量时unsigned char,AD_convert返回变量时char,能说说嘛,我也想学fft了!
xyzzy
2楼-- · 2019-07-18 20:26
武力戡乱 发表于 2014-6-19 09:55
我新手,但也是单片机爱好者。上来,in_offset  变量的作用就不理解,以至于不能理解 k=(  AD_convert()-in ...

这个是原本的程序里就是这么设计的
in_offset是零频补偿,将输入信号用软件的方法调整到平均值为零,因为硬件电路调整偏置到0以后,会有一定的漂移


我就理解为一种大小的调节啦,毕竟16x16显示的范围有限。

一周热门 更多>