[原创] LPC54102 FFT(快速傅里叶变化) 应用 双音频软解码DTMF(Double Tone Multi Freque...

littleshrimp   2015-4-14 12:16 楼主
LPC54102FFT(快速傅里叶变化) 应用 双音频软解码DTMF(Double Tone Multi Frequency,双音多频) 33.LPC54102 FFT DTMF.docx.png
LPC54102 FFT(快速傅里叶变化) 应用 双音频软解码DTMF(Double Tone Multi Frequency,双音多频)
上次发了一个LPC54102测试FFT的贴子
https://bbs.eeworld.com.cn/thread-459019-1-1.html
因为是在网上下的通用的C代码freebsder说这样比较浪费M4F的资源,因为没有使用M4F专有的指令集。
之前调试过CMSIS的DSP库,不知道为什么总报错,为了不影响效率,又做了尝试,这回终于成功。
在CMSIS-SP-00300-r3p1-00rel0\CMSIS\Lib\ARM目录下有N多个math库,针对不同的处理器,比如M0,M3,M4,M4F 后边的小l和小b表示大小端, 我也是听说的。
在项目里引入对应的lib文件,再引用arm_math.h头文件,还需要配置宏定义MDK在项目配置里的C/C++选项卡下把,ARM_MATH_CM4, __FPU_PRESENT = 1填入Define内,就可以正常使用DSP库了。
参考官方例程CMSIS-SP-00300-r3p1-00rel0\CMSIS\DSP_Lib\Examples\arm_fft_bin_example\ARM我写了一个FFT测试程序
_inputBuffer是要输入输出的复数数组,要可读可写,_outputBuffer是用来输出将数复数求模后的数据
[url=]cpp[/url] ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 //FFT arm_status fft_test(float32_t *_inputBuffer, float32_t * _outputBuffer,uint32_t _fftSize) { arm_status status; arm_cfft_radix4_instance_f32 S; status = ARM_MATH_SUCCESS; /* Initialize the CFFT/CIFFT module */ status = arm_cfft_radix4_init_f32(&S, _fftSize, ifftFlag, doBitReverse); /* Process the data through the CFFT/CIFFT module */ arm_cfft_radix4_f32(&S, _inputBuffer); /* Process the data through the Complex Magnitude Module for calculating the magnitude at each bin */ arm_cmplx_mag_f32(_inputBuffer, _outputBuffer, fftSize); return status; }
接下来是求最大值函数,下边函数传输求模后的数据,函数会将maxIndexCount个最大值索引和最大值存入_maxIndexArray和_maxValueArray数组
[url=]cpp[/url]
  1. void getMax1(float32_t *_buffer,uint32_t _bufferLength,uint32_t *_maxIndexArray,uint32_t *_maxValueArray,uint32_t maxIndexCount)
  2. {
  3. float32_t maxValue;
  4. uint32_t maxIndex;
  5. uint32_t i;
  6. for(i=0;i<maxIndexCount;i++)
  7. {
  8. //获得最大值索引
  9. getMax(_buffer,_bufferLength,&maxIndex,&maxValue);
  10. //将最大值索引存放到数组
  11. _maxIndexArray[i] = maxIndex;
  12. _maxValueArray[i] = maxValue;
  13. //将最大值置零,否则下一次最大值还是当前值
  14. _buffer[maxIndex] = 0;
  15. }
  16. }
FFT功能完成后开始测试DTMF检测
关于DTMF在ADOBE Audition的“生成”菜单下有一个“生成脉冲信号”的功能,打开后显示下图界面
键盘矩阵对应的频率分别为横轴:1209,1336,1477,1633纵轴为:697,770,852,941
因为LPC54102的ADC采样率设置没完全弄透,暂时先不计算准确频率,而是用Audition生成以上频率,看看在某个采样率下各频率对应的数据是多少。
在Autidion生成菜单中还有一个“生成音调”的选项,打下显示下边界面:
在基准频率下输入第一个数据1209点一下试听按钮此时音箱发出声音
连接音频输出到LPC54102的ADC0,运行代码:
此时maxIndexArra[0]表示直流分量对应的幅度为4226,maxIndexArray[1]为最大值即1209Hz对应的索引幅度为2586,接下来依次测量各频率对应的索引,得到如下结果:
1209 :50
1336 :55
1477 :61
1633 :68
697 : 29
770 : 32
852 : 35
941 : 39
添加下边代码
[url=]cpp[/url]
  1. typedef struct
  2. {
  3. uint32_t x;
  4. uint32_t y;
  5. char c;
  6. }DTMF_t;
  7. /*
  8. 1209 :50
  9. 1336 :55
  10. 1477 :61
  11. 1633 :68
  12. 697 : 29
  13. 770 : 32
  14. 852 : 35
  15. 941 : 39
  16. */
  17. DTMF_t dtmf[] =
  18. {
  19. {50,29,'1'},
  20. {50,32,'4'},
  21. {50,35,'7'},
  22. {50,39,'*'},
  23. {55,29,'2'},
  24. {55,32,'5'},
  25. {55,35,'8'},
  26. {55,39,'0'},
  27. {61,29,'3'},
  28. {61,32,'6'},
  29. {61,35,'9'},
  30. {61,39,'#'},
  31. {68,29,'A'},
  32. {68,32,'B'},
  33. {68,35,'C'},
  34. {68,39,'D'}
  35. };
  36. //DTMF间隔
  37. uint8_t dtmfSpace = 0;
  38. //DTMF字符
  39. char dtmfChar = 0;
编写DTMF识别函数
[url=]cpp[/url] ?
1 2 3 4 5 6 7 8 9 10 11 12 13 char getDTMF(uint32_t fnX,uint32_t fnY) { uint32_t i; for(i=0;i<16;i++) { If((dtmf.x == fnX && dtmf.y == fnY) || (dtmf.x == fnY && dtmf.y == fnX)) { return dtmf.c; } } return 0; }
主程序下代码如下:
[url=]cpp[/url] ?
1 2 3 4 5 fft_test(fftInput,fftOutput,fftSize); getMax1(fftOutput,halfFftSize,maxIndexArray,maxValueArray,MAX_COUNT); dtmfChar = getDTMF(maxIndexArray[1],maxIndexArray[2]);
电路部分,因为调试时要使用电脑,音频也是由电脑产生,电脑和单片机之间是共地的,音频又是+-(这种应该用什么术语?双电源?)信号,单片机承受不了。
翻箱倒柜找到一个220V转3V的变压器,我把音频输出接到变压器3V处,220V作为输出,输出一端连接到VCC/2虚地上,一端直接引入LPC54102的A0,电脑的音量不能太大,不然电压过高数据采集就不正常了,因为示波器一直在实时测量ADC输入信号,有次我不小心把电量调的太高,在示波器上看到的波形已经接近方波了,不知道这是不是就是传说的斩波,估计是单片机内部有保护电路,超过ADC的允许的电压应该是被保护电路吸收掉了,这样才没有把ADC烧坏,至于性能上有没有影响,凭我的水平还看不出来,测量GND时约为0测量VDD约为4095我就先认为它是好用滴。
运行代码测试视频:
这是最终的主文件代码
  1. /*
  2. * @brief ADC polling example using the ROM API
  3. *
  4. * @note
  5. * Copyright(C) NXP Semiconductors, 2014
  6. * All rights reserved.
  7. *
  8. * @par
  9. * Software that is described herein is for illustrative purposes only
  10. * which provides customers with programming information regarding the
  11. * LPC products. This software is supplied "AS IS" without any warranties of
  12. * any kind, and NXP Semiconductors and its licensor disclaim any and
  13. * all warranties, express or implied, including all implied warranties of
  14. * merchantability, fitness for a particular purpose and non-infringement of
  15. * intellectual property rights. NXP Semiconductors assumes no responsibility
  16. * or liability for the use of the software, conveys no license or rights under any
  17. * patent, copyright, mask work right, or any other intellectual property rights in
  18. * or to any products. NXP Semiconductors reserves the right to make changes
  19. * in the software without notification. NXP Semiconductors also makes no
  20. * representation or warranty that such application will be suitable for the
  21. * specified use without further testing or modification.
  22. *
  23. * @par
  24. * Permission to use, copy, modify, and distribute this software and its
  25. * documentation is hereby granted, under NXP Semiconductors' and its
  26. * licensor's relevant copyrights in the software, without fee, provided that it
  27. * is used in conjunction with NXP Semiconductors microcontrollers. This
  28. * copyright, permission, and disclaimer notice must appear in all copies of
  29. * this code.
  30. */
  31. #include "board.h"
  32. #include "romapi_adc.h"
  33. #include "adc.h"
  34. #include "pwm.h"
  35. #include "arm_math.h"
  36. #include "main.h"
  37. #define MAX_COUNT 5
  38. typedef struct
  39. {
  40. uint32_t x;
  41. uint32_t y;
  42. char c;
  43. }DTMF_t;
  44. /*
  45. FS = 5K
  46. 1209 :50
  47. 1336 :55
  48. 1477 :61
  49. 1633 :68
  50. 697 : 29
  51. 770 : 32
  52. 852 : 35
  53. 941 : 39
  54. */
  55. /*
  56. FS = 50K
  57. 1209 :50
  58. 1336 :55
  59. 1477 :61
  60. 1633 :68
  61. 697 : 29
  62. 770 : 32
  63. 852 : 35
  64. 941 : 39
  65. */
  66. DTMF_t dtmf[] =
  67. {
  68. {50,29,'1'},
  69. {50,32,'4'},
  70. {50,35,'7'},
  71. {50,39,'*'},
  72. {55,29,'2'},
  73. {55,32,'5'},
  74. {55,35,'8'},
  75. {55,39,'0'},
  76. {61,29,'3'},
  77. {61,32,'6'},
  78. {61,35,'9'},
  79. {61,39,'#'},
  80. {68,29,'A'},
  81. {68,32,'B'},
  82. {68,35,'C'},
  83. {68,39,'D'}
  84. };
  85. //#define X1 14
  86. //#define X2 16
  87. //#define X3 17
  88. //#define X4 19
  89. //#define Y1 8
  90. //#define Y2 9
  91. //#define Y3 10
  92. //#define Y4 11
  93. //50K
  94. //DTMF_t dtmf[] =
  95. //{
  96. // {X1,Y1,'1'},
  97. // {X1,Y2,'4'},
  98. // {X1,Y3,'7'},
  99. // {X1,Y4,'*'},
  100. // {X2,Y1,'2'},
  101. // {X2,Y2,'5'},
  102. // {X2,Y3,'8'},
  103. // {X2,Y4,'0'},
  104. // {X3,Y1,'3'},
  105. // {X3,Y2,'6'},
  106. // {X3,Y3,'9'},
  107. // {X3,Y4,'#'},
  108. // {X4,Y1,'A'},
  109. // {X4,Y2,'B'},
  110. // {X4,Y3,'C'},
  111. // {X4,Y4,'D'}
  112. //};
  113. //DTMF间隔
  114. uint8_t dtmfSpace = 0;
  115. //DTMF字符
  116. char dtmfChar = 0;
  117. //FFT复数数据输入输出,输入数据填充奇数,偶数为0,输出数据奇数=实部,偶数=虚部
  118. float32_t fftInput[FFT_INPUT_BUFFER_LENGTH];
  119. //FFT取模数据输出
  120. static float32_t fftOutput[FFT_INPUT_BUFFER_LENGTH/2];
  121. //FFT长度
  122. uint32_t fftSize = FFT_INPUT_BUFFER_LENGTH/2;
  123. //FFT一半长度,用于取输出的对称数据的前半部分
  124. uint32_t halfFftSize = FFT_INPUT_BUFFER_LENGTH/4;
  125. uint32_t ifftFlag = 0;
  126. uint32_t doBitReverse = 1;
  127. //输出数据的最大索引
  128. uint32_t maxIndex;
  129. //最大值索引数组
  130. uint32_t maxIndexArray[MAX_COUNT];
  131. //最大值数组
  132. uint32_t maxValueArray[MAX_COUNT];
  133. //频率数组
  134. float32_t fnArray[MAX_COUNT];
  135. //幅度数组
  136. float32_t rangeArray[MAX_COUNT];
  137. //输出数据的最大值
  138. float32_t maxValue;
  139. //指定索引下的频率
  140. float32_t fn ;
  141. //对应频率的幅度
  142. float range;
  143. float fftResolution;
  144. float maxFftFs;
  145. //系统时钟
  146. extern uint32_t SystemCoreClock;
  147. //LED状态
  148. bool ledState = 0;
  149. //FFT
  150. arm_status fft_test(float32_t *_inputBuffer, float32_t * _outputBuffer,uint32_t _fftSize)
  151. {
  152. arm_status status;
  153. arm_cfft_radix4_instance_f32 S;
  154. status = ARM_MATH_SUCCESS;
  155. /* Initialize the CFFT/CIFFT module */
  156. status = arm_cfft_radix4_init_f32(&S, _fftSize,
  157. ifftFlag, doBitReverse);
  158. /* Process the data through the CFFT/CIFFT module */
  159. arm_cfft_radix4_f32(&S, _inputBuffer);
  160. /* Process the data through the Complex Magnitude Module for
  161. calculating the magnitude at each bin */
  162. arm_cmplx_mag_f32(_inputBuffer, _outputBuffer,
  163. fftSize);
  164. return status;
  165. }
  166. //取最大索引和最大值
  167. void getMax(float32_t *_buffer,uint32_t length,uint32_t * _maxIndex,float32_t *_maxValue)
  168. {
  169. /* Calculates maxValue and returns corresponding BIN value */
  170. arm_max_f32(_buffer, length, _maxValue, _maxIndex);
  171. }
  172. void getMax1(float32_t *_buffer,uint32_t _bufferLength,uint32_t *_maxIndexArray,uint32_t *_maxValueArray,uint32_t maxIndexCount)
  173. {
  174. float32_t maxValue;
  175. uint32_t maxIndex;
  176. uint32_t i;
  177. for(i=0;i<maxIndexCount;i++)
  178. {
  179. //获得最大值索引
  180. getMax(_buffer,_bufferLength,&maxIndex,&maxValue);
  181. //将最大值索引存放到数组
  182. _maxIndexArray[i] = maxIndex;
  183. _maxValueArray[i] = maxValue;
  184. //将最大值置零,否则下一次最大值还是当前值
  185. _buffer[maxIndex] = 0;
  186. }
  187. }
  188. char getDTMF(uint32_t *_maxIndexArray,uint8_t len)
  189. {
  190. uint32_t i,j,k;
  191. for(i=0;i<16;i++)
  192. {
  193. if((dtmf[i].x == _maxIndexArray[1] && dtmf[i].y == _maxIndexArray[2]) || (dtmf[i].x == _maxIndexArray[2] && dtmf[i].y == _maxIndexArray[1]))
  194. {
  195. return dtmf[i].c;
  196. }
  197. for(j=0;j<len;j++)
  198. {
  199. if(dtmf[i].x == _maxIndexArray[j])
  200. {
  201. for(k=0;k<len;k++)
  202. {
  203. if(dtmf[i].y == _maxIndexArray[k])
  204. {
  205. return dtmf[i].c;
  206. }
  207. }
  208. }
  209. }
  210. }
  211. return 0;
  212. }
  213. int main(void)
  214. {
  215. int i;
  216. fftResolution = 1*FS/(float32_t)fftSize;
  217. maxFftFs = halfFftSize*FS/(float32_t)fftSize;
  218. /* Generic Initialization */
  219. SystemCoreClockUpdate();
  220. Board_Init();
  221. //初始化ADC
  222. adc_init();
  223. pwm_init();
  224. // for(i=0;i<fftSize;i+=100)
  225. // {
  226. // set_pwm(i,fftSize,4096);
  227. // }
  228. // while(1)
  229. // {
  230. // adc_read(buffer,fftSize);
  231. // }
  232. // set_pwm();
  233. while (1) {
  234. // memset(fftInput,0,sizeof(fftInput
  235. adc_red_buff(fftInput,fftSize);
  236. //对上一次数据做FFT
  237. fft_test(fftInput,fftOutput,fftSize);
  238. getMax1(fftOutput,halfFftSize,maxIndexArray,maxValueArray,MAX_COUNT);
  239. for(i=0;i<MAX_COUNT;i++)
  240. {
  241. fnArray[i] = maxIndexArray[i] * FS / (float32_t)fftSize;
  242. rangeArray[i] = maxValueArray[i] / (halfFftSize);
  243. }
  244. dtmfChar = getDTMF(maxIndexArray,4);
  245. if(dtmfChar == 0)
  246. {
  247. dtmfSpace = 1;
  248. }
  249. else
  250. {
  251. //输出过间隔
  252. if(dtmfSpace)
  253. {
  254. dtmfSpace = 0;
  255. Board_UARTPutChar(dtmfChar);
  256. DEBUGSTR("\r\n");
  257. }
  258. }
  259. __NOP();
  260. }
  261. }
本帖最后由 littleshrimp 于 2015-4-14 12:16 编辑
虾扯蛋,蛋扯虾,虾扯蛋扯虾

回复评论 (6)

好厉害,果断学习下,回去也研究试试
点赞  2015-4-14 13:55
音频不懂。关于cortex的dsp extension的话,各家编译器有自己的“惯用写法”,idiom,不知道怎么翻译。keil的c/c++手册里有详细描述。
默认摸鱼,再摸鱼。2022、9、28
点赞  2015-4-14 13:59
高大上,dsp都用上了,回头好好研究一下。。
HELLO_WATER
点赞  2015-4-14 20:36
新手,请问还能看到更多源代码吗?谢谢~
点赞  2017-1-17 19:27
对帖子内容很感兴趣,楼主可以多发点源码吗?
点赞  2017-1-17 19:29
这个支持一下,也想测试做一下
点赞  2017-8-9 12:09
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复