LPC54102FFT(快速傅里叶变化) 应用 双音频软解码DTMF(Double Tone Multi Frequency,双音多频)
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]
- void getMax1(float32_t *_buffer,uint32_t _bufferLength,uint32_t *_maxIndexArray,uint32_t *_maxValueArray,uint32_t maxIndexCount)
- {
- float32_t maxValue;
- uint32_t maxIndex;
- uint32_t i;
- for(i=0;i<maxIndexCount;i++)
- {
- //获得最大值索引
- getMax(_buffer,_bufferLength,&maxIndex,&maxValue);
- //将最大值索引存放到数组
- _maxIndexArray[i] = maxIndex;
- _maxValueArray[i] = maxValue;
- //将最大值置零,否则下一次最大值还是当前值
- _buffer[maxIndex] = 0;
-
- }
- }
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]
- typedef struct
- {
- uint32_t x;
- uint32_t y;
- char c;
- }DTMF_t;
- /*
- 1209 :50
- 1336 :55
- 1477 :61
- 1633 :68
-
- 697 : 29
- 770 : 32
- 852 : 35
- 941 : 39
- */
- DTMF_t dtmf[] =
- {
- {50,29,'1'},
- {50,32,'4'},
- {50,35,'7'},
- {50,39,'*'},
- {55,29,'2'},
- {55,32,'5'},
- {55,35,'8'},
- {55,39,'0'},
- {61,29,'3'},
- {61,32,'6'},
- {61,35,'9'},
- {61,39,'#'},
- {68,29,'A'},
- {68,32,'B'},
- {68,35,'C'},
- {68,39,'D'}
- };
- //DTMF间隔
- uint8_t dtmfSpace = 0;
- //DTMF字符
- 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我就先认为它是好用滴。
运行代码测试视频:
这是最终的主文件代码
- /*
- * @brief ADC polling example using the ROM API
- *
- * @note
- * Copyright(C) NXP Semiconductors, 2014
- * All rights reserved.
- *
- * @par
- * Software that is described herein is for illustrative purposes only
- * which provides customers with programming information regarding the
- * LPC products. This software is supplied "AS IS" without any warranties of
- * any kind, and NXP Semiconductors and its licensor disclaim any and
- * all warranties, express or implied, including all implied warranties of
- * merchantability, fitness for a particular purpose and non-infringement of
- * intellectual property rights. NXP Semiconductors assumes no responsibility
- * or liability for the use of the software, conveys no license or rights under any
- * patent, copyright, mask work right, or any other intellectual property rights in
- * or to any products. NXP Semiconductors reserves the right to make changes
- * in the software without notification. NXP Semiconductors also makes no
- * representation or warranty that such application will be suitable for the
- * specified use without further testing or modification.
- *
- * @par
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, under NXP Semiconductors' and its
- * licensor's relevant copyrights in the software, without fee, provided that it
- * is used in conjunction with NXP Semiconductors microcontrollers. This
- * copyright, permission, and disclaimer notice must appear in all copies of
- * this code.
- */
-
- #include "board.h"
- #include "romapi_adc.h"
- #include "adc.h"
- #include "pwm.h"
- #include "arm_math.h"
- #include "main.h"
- #define MAX_COUNT 5
-
- typedef struct
- {
- uint32_t x;
- uint32_t y;
- char c;
- }DTMF_t;
- /*
- FS = 5K
- 1209 :50
- 1336 :55
- 1477 :61
- 1633 :68
-
- 697 : 29
- 770 : 32
- 852 : 35
- 941 : 39
- */
- /*
- FS = 50K
- 1209 :50
- 1336 :55
- 1477 :61
- 1633 :68
-
- 697 : 29
- 770 : 32
- 852 : 35
- 941 : 39
- */
- DTMF_t dtmf[] =
- {
- {50,29,'1'},
- {50,32,'4'},
- {50,35,'7'},
- {50,39,'*'},
- {55,29,'2'},
- {55,32,'5'},
- {55,35,'8'},
- {55,39,'0'},
- {61,29,'3'},
- {61,32,'6'},
- {61,35,'9'},
- {61,39,'#'},
- {68,29,'A'},
- {68,32,'B'},
- {68,35,'C'},
- {68,39,'D'}
- };
-
- //#define X1 14
- //#define X2 16
- //#define X3 17
- //#define X4 19
- //#define Y1 8
- //#define Y2 9
- //#define Y3 10
- //#define Y4 11
- //50K
- //DTMF_t dtmf[] =
- //{
- // {X1,Y1,'1'},
- // {X1,Y2,'4'},
- // {X1,Y3,'7'},
- // {X1,Y4,'*'},
- // {X2,Y1,'2'},
- // {X2,Y2,'5'},
- // {X2,Y3,'8'},
- // {X2,Y4,'0'},
- // {X3,Y1,'3'},
- // {X3,Y2,'6'},
- // {X3,Y3,'9'},
- // {X3,Y4,'#'},
- // {X4,Y1,'A'},
- // {X4,Y2,'B'},
- // {X4,Y3,'C'},
- // {X4,Y4,'D'}
- //};
- //DTMF间隔
- uint8_t dtmfSpace = 0;
- //DTMF字符
- char dtmfChar = 0;
- //FFT复数数据输入输出,输入数据填充奇数,偶数为0,输出数据奇数=实部,偶数=虚部
- float32_t fftInput[FFT_INPUT_BUFFER_LENGTH];
- //FFT取模数据输出
- static float32_t fftOutput[FFT_INPUT_BUFFER_LENGTH/2];
- //FFT长度
- uint32_t fftSize = FFT_INPUT_BUFFER_LENGTH/2;
- //FFT一半长度,用于取输出的对称数据的前半部分
- uint32_t halfFftSize = FFT_INPUT_BUFFER_LENGTH/4;
-
- uint32_t ifftFlag = 0;
- uint32_t doBitReverse = 1;
- //输出数据的最大索引
- uint32_t maxIndex;
- //最大值索引数组
- uint32_t maxIndexArray[MAX_COUNT];
- //最大值数组
- uint32_t maxValueArray[MAX_COUNT];
- //频率数组
- float32_t fnArray[MAX_COUNT];
- //幅度数组
- float32_t rangeArray[MAX_COUNT];
- //输出数据的最大值
- float32_t maxValue;
- //指定索引下的频率
- float32_t fn ;
- //对应频率的幅度
- float range;
-
- float fftResolution;
- float maxFftFs;
-
-
- //系统时钟
- extern uint32_t SystemCoreClock;
- //LED状态
- bool ledState = 0;
-
- //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;
- }
- //取最大索引和最大值
- void getMax(float32_t *_buffer,uint32_t length,uint32_t * _maxIndex,float32_t *_maxValue)
- {
- /* Calculates maxValue and returns corresponding BIN value */
- arm_max_f32(_buffer, length, _maxValue, _maxIndex);
- }
- void getMax1(float32_t *_buffer,uint32_t _bufferLength,uint32_t *_maxIndexArray,uint32_t *_maxValueArray,uint32_t maxIndexCount)
- {
- float32_t maxValue;
- uint32_t maxIndex;
- uint32_t i;
- for(i=0;i<maxIndexCount;i++)
- {
- //获得最大值索引
- getMax(_buffer,_bufferLength,&maxIndex,&maxValue);
- //将最大值索引存放到数组
- _maxIndexArray[i] = maxIndex;
- _maxValueArray[i] = maxValue;
- //将最大值置零,否则下一次最大值还是当前值
- _buffer[maxIndex] = 0;
-
- }
- }
- char getDTMF(uint32_t *_maxIndexArray,uint8_t len)
- {
- uint32_t i,j,k;
- for(i=0;i<16;i++)
- {
- if((dtmf[i].x == _maxIndexArray[1] && dtmf[i].y == _maxIndexArray[2]) || (dtmf[i].x == _maxIndexArray[2] && dtmf[i].y == _maxIndexArray[1]))
- {
- return dtmf[i].c;
- }
- for(j=0;j<len;j++)
- {
- if(dtmf[i].x == _maxIndexArray[j])
- {
- for(k=0;k<len;k++)
- {
- if(dtmf[i].y == _maxIndexArray[k])
- {
- return dtmf[i].c;
- }
- }
- }
- }
- }
- return 0;
- }
- int main(void)
- {
- int i;
-
- fftResolution = 1*FS/(float32_t)fftSize;
- maxFftFs = halfFftSize*FS/(float32_t)fftSize;
- /* Generic Initialization */
- SystemCoreClockUpdate();
- Board_Init();
- //初始化ADC
- adc_init();
-
- pwm_init();
- // for(i=0;i<fftSize;i+=100)
- // {
- // set_pwm(i,fftSize,4096);
- // }
- // while(1)
- // {
- // adc_read(buffer,fftSize);
- // }
- // set_pwm();
- while (1) {
-
- // memset(fftInput,0,sizeof(fftInput
- adc_red_buff(fftInput,fftSize);
- //对上一次数据做FFT
- fft_test(fftInput,fftOutput,fftSize);
- getMax1(fftOutput,halfFftSize,maxIndexArray,maxValueArray,MAX_COUNT);
-
- for(i=0;i<MAX_COUNT;i++)
- {
- fnArray[i] = maxIndexArray[i] * FS / (float32_t)fftSize;
- rangeArray[i] = maxValueArray[i] / (halfFftSize);
- }
-
- dtmfChar = getDTMF(maxIndexArray,4);
- if(dtmfChar == 0)
- {
- dtmfSpace = 1;
- }
- else
- {
- //输出过间隔
- if(dtmfSpace)
- {
- dtmfSpace = 0;
- Board_UARTPutChar(dtmfChar);
- DEBUGSTR("\r\n");
- }
- }
-
-
- __NOP();
- }
- }
本帖最后由 littleshrimp 于 2015-4-14 12:16 编辑