vivado+zedboard之音频分析仪_HW

环境:win7 64   vivado 2014.1
开发板:zedboard version d    xc7z020clg484-1
串口软件:SecureCRT
目标:对一段随机的音频信号进行实时频谱分析。从pc获取音频信号,经由PL的fft IP处理后送入OLED,进行音频频谱的实时显示。通过本实例学习vivado+zedboard软硬件设计的方法,学习控制zedboard外设的方法。本文在商品博客的基础上,把fft函数改为fft ip,实现相同的功能,对部分函数进行优化。

说明:本文的fft算法部分采用硬件实现的,使用的是vivado自带的ip,参考ug871。

注意:本文中所有的源码、工程文件在“我的资源”中可以找到,如果没有请联系作者本人。转载请注明出处。

正文:

本文将分为以下步骤:

1. 使用Vivado 建一个工程,添加oled、audio_ctrl等IP,完成硬件设计。generate,最后导入到SDK中

2. 在SDK中新建工程,添加文件,编译。下载到ZedBoard上进行调试

3. 程序分析

4. 总结

1. 使用Vivado 建一个工程,添加oled、audio_ctrl等IP,完成硬件设计。generate,最后导入到SDK中

1)新建目录audio_fre_hw,在其下新建vivado工程。选择zedboard开发板。

2)将ip_repo拷贝到audio_fre_hw/下。打开IP catalog目录,选择IP setting,添加/audio_fre_hw/ip_repo.

3)将system.tcl拷贝到audio_fre_hw/下。

4)打开vivado的TCL console,

   输入:cd (dir)/project/audio_fre_sw。

   输入:source system.tcl

   此时可以看到自动创建了一个system.bd。


5)generate output product

6) create HDL wrapper

7) 添加约束文件。oled.xdc和zed_audio_constraints.xdc

8)generate bitstream

9)open implementation design & block design

10) export for SDK

2. 在SDK中新建工程,添加文件,编译。下载到ZedBoard上进行调试

11) 在SDK中新建工程,audio_fre_hw,选择空模板

12)添加源文件,见/src/sdk

13) 检查自动编译的结果是否正确。如果报错,提示缺少sin、cos等,在project->property->build setting->linker library中添加参数m,以使用数学函数库

   添加完毕后,SDK会自动编译程序。出现“'Finished building: test_audio.elf.size'”说明成功。

14)使用音频线(音箱和电脑连接的那个线)连接pc的音频口和zedboard的LINE IN接口,将耳机的音频插头接入LINE OUT接口。使用miniUSB连接J14和J17接口用于串口通信和烧写程序。

15)打开SecureCRT并连接。选择Xilinx tools->Program FPGA,烧写程序,蓝灯DONE亮说明成功。

16)打开PC的音频播放器,随便播放一段音频。run->run as,选择GDB。

17)可通过开关选择可以进行控制。


SW0:0--播放原始音频;1--进行音频实时fft分析;

3. 程序分析

18) 关于fft

  fft ip是参考ug871进行实现的,核心是vivado自带的fft,再加上通过HLS生成的前处理和后处理的IP核,共同构成了RealFFT核(本来想单独给它生成IP的,一时未验证成功)。博主本人之前尝试着把上篇软件的fft经HLS转为IP,虽然转成功了,但是vivado综合不了,猜想是数据类型和数据量的原因以至于无法传输大批量数据。本文默认的FFT_SIZE是1024,数据量更为庞大,那么是如何处理的呢?通过使用streaming方式使FFT IP直接与cpu通信,即采用DMA IP连接RealFFT IP和CPU,从而CPU只需要对DMA进行操作即可。因此数据的输入输出只需要和DMA打交道就可以了。这里贴上代码和个人注释,以供参考。这里面使用的simpleTransfer函数,只能处理长度为32位的数据,因此本文的输入输出数据均为short类型,更为复杂的类型还需后续研究。

void RealFFT_DMA(XAxiDma *InstancePtr,short *dataIn,compx * dataOut)
{
	int i=0;
	int status;

	// *IMPORTANT* - flush contents of 'realdata' from data cache to memory
	// before DMA. Otherwise DMA is likely to get stale or uninitialized data
	Xil_DCacheFlushRange((unsigned)dataIn, 4 * FFT_SIZE * sizeof(short));		//refresh cache

	// DMA enough data to push out first result data set completely	??why 4?why must be?why not 5,6,..?
	//	   Request DMA transfer from PS to PL. Enough data to fill the front-end block and the FFT processing
	//	   pipelines must be sent in order for spectral data to be ready when the PL to PS transfer is requested.
	//	   Therefore, four data sets are sent before the first output set is requested:
	status = XAxiDma_SimpleTransfer(InstancePtr, (u32)dataIn,			//send enough data to DMA
				4 * FFT_SIZE * sizeof(short), XAXIDMA_DMA_TO_DEVICE);

	// Do multiple DMA xfers from the RealFFT core's output stream and
	// display data for bins with significant energy. After the first frame,
	// there should only be energy in bins around the frequencies specified
	// in the generate_waveform() function - currently bins 191~193 only
	for (i = 0; i < 2; i++)			//8?2*4?	2 is ok  initial value 8 is assuring the fft out is ok
	{
		// Setup DMA from PL to PS memory using AXI DMA's 'simple' transfer mode
		status = XAxiDma_SimpleTransfer(InstancePtr, (u32)dataOut,
				FFT_SIZE / 2 * sizeof(compx), XAXIDMA_DEVICE_TO_DMA);		//send result to PS
		// Poll the AXI DMA core
		do
		{
		 status = XAxiDma_Busy(InstancePtr, XAXIDMA_DEVICE_TO_DMA);		//wait until transfer done
		} while(status);

		// Data cache must be invalidated for 'realspectrum' buffer after DMA
		Xil_DCacheInvalidateRange((unsigned)dataOut,						//invalidated cache
				FFT_SIZE / 2 * sizeof(compx));

		// DMA another frame of data to PL
		//Push another set of stimulus data to the PL in order to start the accelerator processing the next frame:
		if (!XAxiDma_Busy(InstancePtr, XAXIDMA_DMA_TO_DEVICE))			//continue sending data
		 status = XAxiDma_SimpleTransfer(InstancePtr, (u32)dataIn,
				 FFT_SIZE * sizeof(short), XAXIDMA_DMA_TO_DEVICE);

		printf("\n\rFrame #%d received:\n\r", i);
		printf("End of frame.\n\r");
	}

	printf("fft done.\n\r");
	fflush(stdout);
	printf("fft done 1.\n\r");
}
在使用时还必须注意dataIn和dataOut的类型,本文为:

	short dataIn[FFT_SIZE*4];
	compx out[FFT_SIZE/2];
	float mag[FFT_SIZE/2];
数组长度必须使用上述所示,因为设计函数时没有考虑通用性,只考虑功能实现了。(新手不容易啊~~)

为什么要乘4呢?博主认为乘其他的也可以,但是没有去验证了,它只是为了多传点数据以供处理,包括for循环也是如此,目的都是为了最终的处理数据准确,不会因为时间问题处理错了或者没来得及传出来等。对于本例的音频数据,本文处理如下:

		//====== audio value =============
		get_audioData(dataIn);
		for (i = 1; i < 4; i++)
			for(j=0;j<FFT_SIZE;j++)
				dataIn[j + i * FFT_SIZE]=dataIn[j];

19) OLED显示

本来在之前的例子中是么有这个问题的,但是由于本文采用了1024点,那么显示的时候就出问题了,因为OLED只能显示128的长度,因此必须重新写它的显示函数,而且对于音频数据,想要其显示的好看,还必须设置显示高度。经多次尝试,本文成功实现在OLED的中央显示最大幅值,两侧均匀分布。

void oled_show(float *mag)
{
	printf("oled_show begin \r\n");
	int outLED[oled_L];
	int i,maxi;
	float tmp;

	u8 oled_equalizer_buf[oled_L];
	for (i = 0; i < oled_L; i++) outLED[i] = 0;

	for(i=1; i<FFT_SIZE/2; i++)			//get the  max mag & its fre
	{
		if(tmp<mag[i])
		{
			tmp=mag[i];
			maxi=i;
		}
			//xil_printf("Cur Mag= %x MHz\r\n",mag[i]);
	}

	if(FFT_SIZE/2>=oled_L)				//the max fre will be shown in the middle
	{
		if(maxi-64<0)
		{
			for(i=0;i<maxi+64;i++)
				outLED[i+64-maxi]+=mag[i];
			for(i=0;i<64-maxi;i++)
				outLED[i]+=0;
		}
		else if(maxi+64>FFT_SIZE/2)
		{
			for(i=0;i<FFT_SIZE/2+64-maxi;i++)
				outLED[i]+=mag[i+maxi-64];
			for(i=FFT_SIZE/2;i<maxi+64;i++)
				outLED[i+64-maxi]+=0;
		}
		else
		{
			for(i=0;i<oled_L;i++)
				outLED[i]+=mag[i+maxi-64];
		}
	}
	else
	{
		for(i=0;i<FFT_SIZE/2;i++)
			outLED[i+64-maxi]+=mag[i];
		for(i=0;i<i+64-maxi;i++)
			outLED[i]+=0;
		for(i=0;i<oled_L;i++)
			outLED[i+64-maxi+FFT_SIZE/2]+=0;
	}

	oled_equalizer_buf[0]=mag[0]/2;
	for (i=1; i<oled_L; i++)
	{
		oled_equalizer_buf[i]=outLED[i]*AMP;			//control the height
//		if(i%10==0)
//			printf("oled_equalizer_buf[%d]=%d\r\n",i,oled_equalizer_buf[i]);
	}
	OLED_Clear();
	OLED_Equalizer_128(oled_equalizer_buf);
	OLED_Refresh_Gram();
	//OLED_ShowString(0,0,"hello world!");
}
20) AUDIO数据获取

在"vivado+zedboard之audio驱动"一文中已经做了分析。此处就不详述了。
4. 总结

本文学习和使用了FFT、DMA等ip核,在使用audio、oled、fft前后处理等自定义ip的基础上,构建了音频分析仪的硬件部分,并在SDK中编写了控制程序,在zedboard中成功验证。可以实时显示音频频谱。关于例子本身还有诸多要优化的地方,参见上篇博文。




评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值