本帖最后由 正点原子 于 2020-10-24 15:19 编辑

203429z6c3os33t8albi33.png (66.36 KB)

2019-7-28 15:14 上传

第五十一章 基于FFT IP核的音频频谱仪实验

FFT的英文全称是Fast Fourier Transformation, 即快速傅里叶变换, 它是根据离散傅里叶变换(DFT) 的奇、 偶、 虚、 实等特性, 在离散傅里叶变换的基础上改进得到的。 FFT主要用于频谱分析,可以将时域信号转化为频域信号, 在滤波、 图象处理和数据压缩等领域具有

普遍应用。 本章我们将使用Quartus II软件自带的FFT IP核来分析音频信号的频谱, 作为一个简单的例程, 向大家介绍Altera FFT IP核的使用方法。

本章包括以下几个部分:

51.1 FFT IP 核简介

51.2 实验任务

51.3 硬件设计

51.4 程序设计

51.5 下载验证

51.1 FFT IP核简介

首先, 我们简单介绍下FFT: FFT即快速傅里叶变换, 是1965年由J.W.库利和T.W.图基提出的。 采用这种算法能使计算机计算离散傅里叶变换(DFT) 所需要的乘法次数大为减少, 被变换的抽样点数N越多, FFT算法计算量的节省就越显著。FFT可以将一个时域信号变换到频域。 因为有些信号在时域上是很难看出什么特征的, 但是如果变换到频域之后, 就很容易看出特征了, 这就是很多信号分析采用FFT变换的原因。 另外, FFT可以将一个信号的频谱提取出来, 这在频谱分析方面也是经常用的。 简而言之, FFT就是将一个信号从时域变换到频域方便我们分析处理。 在实际应用中, 一般的处理过程是先对一个信号在时域进行采集, 比如我们通过ADC, 按照一定大小采样频率F去采集信号, 采集N个点, 那么通过对这N个点进行FFT运算, 就可以得到这个信号的频谱特性。这里还涉及到一个采样定理的概念: 在进行模拟/数字信号的转换过程中, 当采样频率F大于信号中最高频率fmax的2倍时(F>2*fmax), 采样之后的数字信号完整地保留了原始信号中的信息, 采样定理又称奈奎斯特定理。 举个简单的例子: 比如我们正常人发声, 频率范围一般在8KHz以内, 那么我们要通过采样之后的数据来恢复声音, 我们的采样频率必须为8KHz的2倍

以上, 也就是必须大于16KHz才行。模拟信号经过ADC采样之后, 就变成了数字信号, 采样得到的数字信号, 就可以做FFT变换了。 N个采样点数据, 在经过FFT之后, 就可以得到N个点的FFT结果。 为了方便进行FFT运算,通常N取2的整数次方。假设采样频率为F, 对一个信号采样, 采样点数为N, 那么FFT之后结果就是一个N点的复数,每一个点就对应着一个频率点(以基波频率为单位递增), 这个点的模值(sqrt(实部2+虚部2))就是该频点频率值下的幅度特性。具体跟原始信号的幅度有什么关系呢? 假设原始信号的峰值为A, 那么FFT的结果的每个点(除了第一个点直流分量之外) 的模值就是A的N/2倍, 而第一个点就是直流分量, 它的模值就是直流分量的N倍。这里还有个基波频率, 也叫频率分辨率的概念, 就是如果我们按照F的采样频率去采集一个信号, 一共采集N个点, 那么基波频率(频率分辨率) 就是fk=F/N。 这样, 第n个点对应信号

频率为: F*(n-1)/N; 其中n≥1, 当n=1时为直流分量。 关于FFT我们就介绍到这。 如果我们要自己实现FFT算法, 对于不懂数字信号处理的朋友来说, 是比较难的。 不过, Quartus II提供的IP核里面就有FFT IP核可以给我们使用, 因此我们只需要知道如何使用这个IP核, 就可以迅

速的完成FFT计算, 而不需要自己学习数字信号处理, 去编写代码了, 大大方便了我们的开发。

51.2 实验任务

本节实验任务是先将电脑或手机的音乐通过开拓者开发板上的WM8978器件输给FPGA, 然后使用Altera FFT IP核分析WM8978输出的音频信号的频谱, 并将采样点的幅度特性显示到4.3寸RGB TFT-LCD上。

51.3 硬件设计

音频WM8978接口部分的硬件设计与“音频环回实验” 完全相同, 请参考“音频环回实验”中的硬件设计部分。 RGB TFT-LCD接口部分的硬件设计请参考“RGB TFT-LCD彩条显示实验” 中的硬件设计部分。由于WM8978接口和RGB TFT-LCD引脚数目较多且在前面相应的章节中已经给出它们的管脚

列表, 这里不再列出管脚分配。

51.4 程序设计

图 51.4.1是根据本章实验任务画出的系统框图。 首先, WM8978模块通过控制接口配置WM8978相关的寄存器。 WM8978在接收电脑传来的音频数据后, 将一路音频数据送给喇叭播放,将另一路经ADC采集过的数据送给WM8978模块。WM8978模块紧接着将音频数据送给FFT模块做频

谱分析, 得到频谱幅度数据。 LCD模块则负责读取频谱幅度数据, 并在RGB TFT-LCD上显示频谱。

image846.png (51.97 KB)

2019-7-28 15:13 上传

图 51.4.1 IP核之FFT实验系统框图

程序中各模块端口及信号连接如图 51.4.2所示:

image847.png (77.04 KB)

2019-7-28 15:13 上传

图 51.4.2 模块连接图

FPGA顶层(FFT_audio_lcd) 例化了以下四个模块: pll时钟模块(pll) 、 wm8978模块(wm8978_ctrl) 、 FFT模块(FFT_top) 、 LCD模块(LCD_top) 。pll时钟模块(pll) : 本实验中WM8978模块所需要的时钟为12MHz, FFT模块的驱动时钟为50MHz, 另外LCD模块需要50Mhz的时钟来处理、 缓存FFT模块输出的数据, 并在10MHz的驱动时钟下驱动RGB TFT-LCD显示。 因此需要一个PLL模块用于产生系统各个模块所需的时钟频率。wm8978模块(wm8978_ctrl) : WM8978控制模块主要完成WM8978的配置和WM8978接收的录音音频数据的接收处理, 以及FPGA发送的音频数据的发送处理。 该模块和“音频环回实验” 章节中用到的wm8978_ctrl模块为同一个模块, 本实验对该模块有少许更改, 我们会在后面进行讲解, 有关该模块的详细介绍请大家参考“音频环回实验” 章节。FFT模块(FFT_top) : FFT模块将wm8978模块传输过来的音频信号进行缓存, 然后将其送给FFT IP核进行频谱分析。 接着计算FFT IP核输出复数的平方根, 即频谱的幅度值, 然后将其输出给LCD模块显示。LCD模块(LCD_top) : LCD模块取FFT模块传输过来的一帧数据的一半(也就是64个数据)

进行缓存, 并驱动RGB TFT-LCD液晶屏显示频谱。

顶层模块的代码如下:

1 module FFT_audio_lcd(

2 input sys_clk,

3 input rst_n,

4 5

// WM8978接口

6 output aud_mclk,

7 input aud_bclk,

8 input aud_lrc,

9 input aud_adcdat,

10 output aud_dacdat,

11 output aud_scl,

12 inout aud_sda,

13

14 //LCD接口

15 output lcd_hs,

16 output lcd_vs,

17 output lcd_de,

18 output [15:0] lcd_rgb,

19 output lcd_bl,

20 output lcd_rst,

21 output lcd_pclk

22 );

23

24 //wire define

25 wire clk50M;

26 wire clk10M;

27

28 wire audio_valid;

29 wire [15:0] audio_data;

30

31 wire fft_sop;

32 wire fft_eop;

33 wire fft_valid;

34 wire [15:0] fft_data;

35

36 //*****************************************************

37 //** main code

38 //*****************************************************

39

40 //锁相环模块

41 pll pll_inst (

42 .inclk0 (sys_clk),

43

44 .c0 (aud_mclk),

45 .c1 (clk50M),

46 .c2 (clk10M)

47 );

48

49 //例化WM8978控制模块

50 wm8978_ctrl u_wm8978_ctrl(

51 .clk (clk50M),

52 .rst_n (rst_n),

53

54 .aud_bclk (aud_bclk), // WM8978位时钟

55 .aud_lrc (aud_lrc), // 对齐信号

56 .aud_adcdat (aud_adcdat), // 音频输入

57 .aud_dacdat (aud_dacdat), // 音频输出

58

59 .aud_scl (aud_scl), // WM8978的SCL信号

60 .aud_sda (aud_sda), // WM8978的SDA信号

61

62 .dac_data (audio_data), // 输出的音频数据

63 .adc_data (audio_data), // 输入的音频数据

64 .rx_done (audio_valid), // 一次接收完成

65 .tx_done () // 一次发送完成

66 );

67

68 //对输入的音频数据进行傅里叶变换

69 FFT_top FFT_u(

70 .clk_50m (clk50M),

71 .rst_n (rst_n),

72

73 .audio_clk (aud_bclk),

74 .audio_data (audio_data),

75 .audio_valid (audio_valid),

76

77 .data_modulus (fft_data),

78 .data_sop (fft_sop),

79 .data_eop (fft_eop),

80 .data_valid (fft_valid)

81 );

82

83 //RGB_LCD 显示模块

84 LCD_top LCD_u(

85 .clk50M (clk50M),

86 .clk10M (clk10M),

87 .rst_n (rst_n),

88

89 .lcd_hs (lcd_hs),

90 .lcd_vs (lcd_vs),

91 .lcd_de (lcd_de),

92 .lcd_rgb (lcd_rgb),

93 .lcd_bl (lcd_bl),

94 .lcd_rst (lcd_rst),

95 .lcd_pclk (lcd_pclk),

96

97 .fft_data (fft_data),

98 .fft_sop (fft_sop),

99 .fft_eop (fft_eop),

100 .fft_valid (fft_valid)

101 );

102

103 endmodule

顶层模块主要完成了对各个子模块的例化、 接收外部传输给FPGA的数据、 以及输出数据给外设。WM8978模块里例化了三个子模块: 音频接收模块(audio_receive) 、 音频发送模块(audio_send) 、 WM8978配置模块(wm8978_config) 。WM8978模块(wm8978_ctrl)只是在“音频环回实验”章节中的WM8978控制模块(wm8978_ctrl模块) 的基础上做了两处修改, 下面我们会说明作出修改的地方, 以及这么修改的原因。wm8978_ctrl模块的详细介绍, 还请查看“音频环回实验” 章节里相应的部分。

第一个要修改的地方是wm8978_ctrl模块内部一个常量的定义, 代码如下所示:

1 module wm8978_ctrl(

2 //system clock

3 input clk , // 时钟信号

4 input rst_n , // 复位信号

5 6

//wm8978 interface

7 //audio interface(master mode)

8 input aud_bclk , // WM8978位时钟

9 input aud_lrc , // 对齐信号

10 input aud_adcdat , // 音频输入

11 output aud_dacdat , // 音频输出

12 //control interface

13 output aud_scl , // WM8978的SCL信号

14 inout aud_sda , // WM8978的SDA信号

15

16 //user interface

17 input [31:0] dac_data , // 输出的音频数据

18 output [31:0] adc_data , // 录音的数据

19 output rx_done , // 一次采集完成

20 output tx_done // 一次发送完成

21 );

22

23 //parameter define

24 parameter WL = 6'd16; // word length音频字长定义

25

26 //*****************************************************

27 //** main code

28 //*****************************************************

……省略部分代码……

67 endmodule

我们在代码的第24行对音频字长做了修改, 将常量WL的值由32改为了16。 这是因为如果这里依然保持字长为32位, 那么后面FFT模块占用的资源将会比较大, 所以这里将字长修改为16位。第二个要修改地方的是音频接收模块(audio_receive) 内部, lrc_edge信号的定义。 代

码如下所示:

1 module audio_receive #(parameter WL = 6'd32) ( // WL(word length音频字长定义)

2 //system clock 50MHz

3 input rst_n , // 复位信号

4 5

//wm8978 interface

6 input aud_bclk , // WM8978位时钟

7 input aud_lrc , // 对齐信号

8 input aud_adcdat, // 音频输入

9 1

0 //user interface

11 output reg rx_done , // FPGA接收数据完成

12 output reg [31:0] adc_data // FPGA接收的数据

13 );

14

15 //reg define

16 reg aud_lrc_d0; // aud_lrc延迟一个时钟周期

17 reg [ 5:0] rx_cnt; // 发送数据计数

18 reg [31:0] adc_data_t; // 预输出的音频数据的暂存值

19

20 //wire define

21 wire lrc_edge ; // 边沿信号

22

23 //*****************************************************

24 //** main code

25 //*****************************************************

26

27 //assign lrc_edge = aud_lrc ^ aud_lrc_d0; // LRC信号的边沿检测

28 assign lrc_edge = aud_lrc & (~aud_lrc_d0); // LRC信号的边沿检测

29

……省略部分代码……

73 endmodule

第27行的代码是修改前lrc_edge信号的定义, 第28行代码则是修改后的信号定义, 这里将原来信号的双边沿检测, 修改为上升沿检测。 LRC信号的下降/上升沿将用于采集左/右两个通道的音频数据; 修改成上升沿检测之后, 程序只采集单个通道(右通道) 的音频。 这么做的原

因是如果我们同时采集两个通道的音频数据, 那么在通道切换的时候, 会给信号的频谱带来一个高频的噪声。接下来我们介绍FFT模块(FFT_top) 的相关内容。 FFT模块(FFT_top) 内部例化了4个模块: 音频数据缓存模块(audio_in_fifo) 、 FFT控制模块(fft_ctrl) 、 FFT IP核(FFT) 、数据取模模块(data_modulus) , 模块结构如下所示:

image848.png (556.09 KB)

2019-7-28 15:13 上传

图 51.4.3 FFT模块内部结构图

由图可知音频数据进入FFT模块(FFT_top) 后, 先经音频数据缓存模块(audio_in_fifo)缓存数据, 然后再将数据送给FFT IP核。 音频数据经FFT IP核处理后, 输出形式为复数的数据。紧接着复数数据经过数据取模模块(data_modulus)处理后得到复数的模值, 最后将模值从FFT

模块(FFT_top) 输出出去。音频数据缓存模块(audio_in_fifo) : 音频数据缓存模块是一个fifo, 这里它的深度设置为64, 宽度为16bit。 它负责缓存WM8978模块传输过来的音频数据。 另外, 当FFT控制模块(fft_ctrl) 输出的读请求信号拉高时, 音频数据缓存模块会将缓存的数据输出给FFT IP核做频谱分析。FFT控制模块(fft_ctrl) : FFT控制模块依据FFT IP核的数据输入时序原理, 产生数据传输的控制信号, 来驱动FFT IP核不断地进行FFT分析。FFT IP核(FFT) : 这里直接例化Quartus II软件提供的FFT IP核, 我们只需按照IP核的数据传输时序, 将音频数据送给FFT IP核, 它会自动输出经过FFT分析后的复数数据。数据取模模块(data_modulus) : 数据取模模块负责计算FFT IP核输出的复数的模值, 也就是这个频率点的幅度模值。FFT模块(FFT_top) 的代码如下所示, 它完成了各个子模块的例化以及信号交互:

1 module FFT_top(

2 input clk_50m,

3 input rst_n,

4 5

input audio_clk,

6 input audio_valid,

7 input [15:0] audio_data,

8 9

output data_sop,

10 output data_eop,

11 output data_valid,

12 output [15:0] data_modulus

13 );

14

15 //wire define

16 wire [15:0] audio_data_w;

17 wire fifo_rdreq;

18 wire fifo_rd_empty;

19

20 wire fft_rst_n;

21 wire fft_ready;

22 wire fft_sop;

23 wire fft_eop;

24 wire fft_valid;

25

26 wire source_sop;

27 wire source_eop;

28 wire source_valid;

29 wire [15:0] source_real;

30 wire [15:0] source_imag;

31

32 //*****************************************************

33 //** main code

34 //*****************************************************

35

36 //例化fifo, 缓存wm8978输出的音频数据

37 audio_in_fifo fifo_inst(

38 .aclr (~rst_n),

39

40 .wrclk (audio_clk),

41 .wrreq (audio_valid),

42 .data (audio_data),

43 .wrfull (),

44

45 .rdclk (clk_50m),

46 .rdreq (fifo_rdreq),

47 .q (audio_data_w),

48 .rdempty (fifo_rd_empty)

49 );

50

51 //FFT控制模块, 控制FFT的输入端口

52 fft_ctrl u_fft_ctrl(

53 .clk_50m (clk_50m),

54 .rst_n (rst_n),

55

56 .fifo_rd_empty (fifo_rd_empty),

57 .fifo_rdreq (fifo_rdreq),

58

59 .fft_ready (fft_ready),

60 .fft_rst_n (fft_rst_n),

61 .fft_valid (fft_valid),

62 .fft_sop (fft_sop),

63 .fft_eop (fft_eop)

64 );

65

66 //例化 FFT IP核

67 FFT FFT_u(

68 .clk (clk_50m),

69 .reset_n (fft_rst_n),

70

71 .sink_ready (fft_ready), //FFT准备好信号, 此信号为高表示可输入变换数据

72 .sink_real (audio_data_w), //实部

73 .sink_imag (16'd0), //虚部

74 .sink_sop (fft_sop), //输入数据起始信号, 与第一个数据对齐

75 .sink_eop (fft_eop), //输入数据结束信号, 与最后一个数据对齐

76 .sink_valid (fft_valid), //输入数据有效信号, 在输入数据期间要保持高电平有效

77 .inverse (1'b0), //高电平为FFT反变换

78 .sink_error (1'b0), //输入错误信号, 置0即可

79

80 .source_ready (1'b1), //后端模块准备好信号, 置1即可

81 .source_real (source_real), //实部 有符号数

82 .source_imag (source_imag), //虚部 有符号数

83 .source_sop (source_sop), //起始信号

84 .source_eop (source_eop), //终止信号

85 .source_valid (source_valid), //输出有效信号, FFT变换完成后, 此信号置高开始输出数据

86 .source_exp (), //数据的缩放因子 有符号数

87 .source_error () //输出错误信号,若输入的数据格式有误, 则不进行FFT变

88 ); //换, 并给出错误值

89

90 //对FFT输出的实部和虚部进行取模运算

91 data_modulus u_sqrt_top(

92 .clk_50m (clk_50m),

93 .rst_n (rst_n),

94

95 .source_real (source_real),

96 .source_imag (source_imag),

97 .source_sop (source_sop),

98 .source_eop (source_eop),

99 .source_valid (source_valid),

100

101 .data_modulus (data_modulus),

102 .data_sop (data_sop),

103 .data_eop (data_eop),

104 .data_valid (data_valid)

105 );

106

107 endmodule

我们在代码的52行至64行例化了FFT控制模块(fft_ctrl) , 它负责产生驱动FFT IP核输入端口的控制信号, 代码如下所示:

1 module fft_ctrl(

2 input clk_50m,

3 input rst_n,

4 5

input fifo_rd_empty,

6 output fifo_rdreq,

7 8

input fft_ready,

9 output reg fft_rst_n,

10 output reg fft_valid,

11 output fft_sop,

12 output fft_eop

13 );

14

15 //reg define

16 reg state;

17 reg [4:0] delay_cnt;

18 reg [9:0] fft_cnt;

19 reg rd_en;

20

21 //*****************************************************

22 //** main code

23 //*****************************************************

24

25 assign fifo_rdreq = rd_en && (~fifo_rd_empty); //fifo读请求信号

26 assign fft_sop = (fft_cnt==10'd1) ? fft_valid : 1'b0; //生成sop信号

27 assign fft_eop = (fft_cnt==10'd128) ? fft_valid : 1'b0; //生成eop信号

28

29 //产生驱动FFT ip核的控制信号

30 always @ (posedge clk_50m or negedge rst_n) begin

31 if(!rst_n) begin

32 state <= 1'b0;

33 rd_en <= 1'b0;

34 fft_valid <= 1'b0;

35 fft_rst_n <= 1'b0;

36 fft_cnt <= 10'd0;

37 delay_cnt <= 5'd0;

38 end

39 else begin

40 case(state)

41 1'b0: begin

42 fft_valid <= 1'b0;

43 fft_cnt <= 10'd0;

44

45 if(delay_cnt < 5'd31) begin //延时32个时钟周期, 用于FFT复位

46 delay_cnt <= delay_cnt + 1'b1;

47 fft_rst_n <= 1'b0;

48 end

49 else begin

50 delay_cnt <= delay_cnt;

51 fft_rst_n <= 1'b1;

52 end

53

54 if((delay_cnt==5'd31)&&(fft_ready))

55 state <= 1'b1;

56 else

57 state <= 1'b0;

58 end

59 1'b1: begin

60 if(!fifo_rd_empty)

61 rd_en <= 1'b1;

62 else

63 rd_en <= 1'b0;

64

65 if(fifo_rdreq) begin

66 fft_valid <= 1'b1;

67 if(fft_cnt < 10'd128)

68 fft_cnt <= fft_cnt + 1'b1;

69 else

70 fft_cnt <= 10'd1;

71 end

72 else begin

73 fft_valid <= 1'b0;

74 fft_cnt <= fft_cnt;

75 end

76 end

77 default: state <= 1'b0;

78 endcase

79 end

80 end

81

82 endmodule

我们接下来会在介绍FFT IP核的数据输入时序的同时, 对代码进行讲解, 如图 51.4.4所示为IP核的数据输入时序。 在让FFT IP核工作之前, 需要先让IP核复位一段时间, 这里让IP核复位了32个时钟周期, 对应于代码的45行至52行。 在复位操作完成后, 需要先等FFT IP核拉

高sink_ready信号(表示IP核可以接受数据了) , 才能进行下一步操作, 这步操作对应于代码的54行至58行。sink_ready信号拉高后在给FFT IP核送数据的时候, 需要同时拉高sink_valid信号。 大家可以看到图 51.4.4中, 在发送第一个数据的时候, sink_sop信号(startofpacket, 数据包的

开始信号) 需要拉高一个时钟周期。 相应的, 在发送最后一个数据的时候, 需要拉高fft_eop信号(endofpacket, 数据包的结束信号) 一个时钟周期(图中未展示) 。 如代码的60行至63行所示, 在FFT IP核拉高sink_ready信号后, 先判断音频数据缓存模块(audio_in_fifo) 内

是否有数据, 若模块内无数据则保持等待。 当模块内有数据的时候, 会拉高rd_en信号。 此时大家可以看到代码第25行, 由于fifo不为空且rd_en信号拉高了, fifo_rdreq信号(fifo的读使能信号) 也会跟着一起拉高。 代码65行至75行, fifo_rdreq信号拉高后, fft_cnt计数器开

始计数, 计数满128的时候(发送了128个数据) , 一帧数据(一次数据传输的总量) 传输完毕,接着判断sink_valid信号是否为高电平, 这样周而复始下去。 需要注意的是, 在代码的第26行和第27行, 我们依据fft_cnt计数器的值产生了fft_sop和fft_eop信号。

image849.png (29.8 KB)

2019-7-28 15:13 上传

图 51.4.4 streaming数据流输入时序

在讲解完FFT控制模块(fft_ctrl) 后, 接下来说明一下怎么配置FFT IP核。 我们先打开MegaWizard Plug-In Manager界面(详细步骤可参考“IP核之RAM实验” 章节的程序设计部分),在搜索框中输入FFT, 界面中便会显示我们需要的FFT IP核, 如下图所示:

image850.png (45 KB)

2019-7-28 15:13 上传

图 51.4.5 FFT IP核配置界面

此时, 我们需要点击界面中的FFT v13.1来选中这个IP核, 然后选择IP核的生成路径。 我们一般习惯将IP核放置在工程文件夹的par\ipcore下。 然后点击Next, 出现以下界面:

image851.png (25.58 KB)

2019-7-28 15:13 上传

图 51.4.6 FFT IP核主界面

然后点击parameterize(参数化) , 进入参数配置界面, 如下所示:

image852.png (35.22 KB)

2019-7-28 15:13 上传

图 51.4.7 FFT IP核Parameterize配置界面

实际上, 我们只需要配置这个界面下的Parameters窗口里的参数就可以了。 我们开发板上使用的是Cyclone IV系列的FPGA芯片, 所以不用修改Target Device Family中的选项。 由于我们这次实验的采样点数是128个, 所以, 这里Transform Length设置为128。 传输给FFT IP核的

音频数据位宽为16bit,所以这里Data Input Precision(输入数据位宽)设置为16bits。TwiddleWidth是旋转因子的数据位宽,只要比输入数据的位宽低就可以了,这里将其设置成8bits。DataOutput Precision(输出数据位宽) 选项这里无法修改。

接下来我们看一下Architecture界面下的选项, Architecture界面如下所示:

image853.png (40.22 KB)

2019-7-28 15:13 上传

图 51.4.8 Architecture配置界面

在I/O Data Flow配置界面下有4个选项, 分别为: 流模式(Streaming)、 缓存突发模式(Buffered Burst)、 可变流模式(Variable Streaming)、 突发模式(Burst)。 流模式(Streaming)运算速度大于缓存突发模式(Buffered Burst), 突发模式(Buffered Burst) 运算速度大于突

发模式(Burst), 且占用资源也依次减少。 Variable Streaming模式可用于在线改变TransformLength的大小。速度和流模式差不多,资源占用更多。 这里我们使用默认的流模式(Streaming)。然后, 我们看一下Implementation Options界面, 如图 51.4.9所示。

image854.png (47.13 KB)

2019-7-28 15:13 上传

图 51.4.9 Implementation Options界面

我们依然保留默认设置就好了, 图中的配置说明FFT使用了4个乘法器以及2个加法器、 以及DSP块和逻辑单元。 这里点击Finish回到FFT IP核主界面, 如图 51.4.6所示。 接着点击第2个选项Set Up Simulation选项, 出现以下界面:

image855.png (176.36 KB)

2019-7-28 15:13 上传

图 51.4.10 Set Up Simulation界面

如果需要仿真FFT IP核, 则需要勾选Generate Simulation Model选项。 勾选这个选项后,就会依据选择的language来生成仿真IP核所需的一系列文件。接下来点击OK,回到如图 51.4.6所示的主界面。最后点击Generate选项, 就会生成配置好的FFT IP核, 若在生成的过程长时间卡顿在下图

所示的界面 , 这个时候只需点击Cancel按钮, 回到主界面再次点击Generate选项就好了, 若还是不行, 请多重复几次, 这是正常的情况。

image856.png (39.71 KB)

2019-7-28 15:13 上传

最后出现下图所示的界面就表示IP核生成成功, 点击Next就完成IP核的创建了。

image857.png (43.4 KB)

2019-7-28 15:13 上传

图 51.4.11 FFT IP核生成完成界面

我们在FFT模块(FFT_top) 代码的97行至110行例化了data_modulus模块, data_modulus

模块的代码如下所示:

1 module data_modulus(

2 input clk_50m,

3 input rst_n,

4 5

input [15:0] source_real,

6 input [15:0] source_imag,

7 input source_sop,

8 input source_eop,

9 input source_valid,

10

11 output [15:0] data_modulus,

12 output reg data_sop,

13 output reg data_eop,

14 output reg data_valid

15 );

16

17 //reg define

18 reg [31:0] source_data;

19 reg [15:0] data_real;

20 reg [15:0] data_imag;

21 reg data_sop1;

22 reg data_sop2;

23 reg data_eop1;

24 reg data_eop2;

25 reg data_valid1;

26 reg data_valid2;

27

28 //*****************************************************

29 //** main code

30 //*****************************************************

31

32 //取实部和虚部的平方和

33 always @ (posedge clk_50m or negedge rst_n) begin

34 if(!rst_n) begin

35 source_data <= 32'd0;

36 data_real <= 16'd0;

37 data_imag <= 16'd0;

38 end

39 else begin

40 if(source_real[15]==1'b0) //由补码计算原码

41 data_real <= source_real;

42 else

43 data_real <= ~source_real + 1'b1;

44

45 if(source_imag[15]==1'b0) //由补码计算原码

46 data_imag <= source_imag;

47 else

48 data_imag <= ~source_imag + 1'b1;

49 //计算原码平方和

50 source_data <= (data_real*data_real) + (data_imag*data_imag);

51 end

52 end

53

54 //例化sqrt模块, 开根号运算

55 sqrt sqrt_inst (

56 .clk (clk_50m),

57 .radical (source_data),

58

59 .q (data_modulus),

60 .remainder ()

61 );

62

63 //数据取模运算共花费了三个时钟周期, 此处延时三个时钟周期

64 always @ (posedge clk_50m or negedge rst_n) begin

65 if(!rst_n) begin

66 data_sop <= 1'b0;

67 data_sop1 <= 1'b0;

68 data_sop2 <= 1'b0;

69 data_eop <= 1'b0;

70 data_eop1 <= 1'b0;

71 data_eop2 <= 1'b0;

72 data_valid <= 1'b0;

73 data_valid1 <= 1'b0;

74 data_valid2 <= 1'b0;

75 end

76 else begin

77 data_valid1 <= source_valid;

78 data_valid2 <= data_valid1;

79 data_valid <= data_valid2;

80 data_sop1 <= source_sop;

81 data_sop2 <= data_sop1;

82 data_sop <= data_sop2;

83 data_eop1 <= source_eop;

84 data_eop2 <= data_eop1;

85 data_eop <= data_eop2;

86 end

87 end

88

89 endmodule

我们在代码的40行至48行将FFT IP核输出的复数的实部与虚部进行了处理, 求得了它们的原码, 并在第50行计算了原码的平方和。 在代码的55行至61行, 例化了sqrt IP核(求平方根),我们将前面计算得到的平方和输送给sqrt IP核, 进行平方根运算, 得到的结果将在后面用于

在LCD上显示频谱。我们接下来了解一下sqrt IP核的配置, 方法和前面配置FFT IP核一样。 先在MegaWizardPlug-In Manager界面搜索框内输入sqrt, 出现下图所示的三个IP核选项:

image858.png (47.23 KB)

2019-7-28 15:13 上传

图 51.4.12 sqrt IP核选择界面

我们这里使用的是选项中的第三个IP核(ALTSQRT IP核) , 然后进入IP核的配置界面, 配置后的界面如下图所示:

image859.png (44.32 KB)

2019-7-28 15:13 上传

图 51.4.13 ALTSQRT IP核配置界面

在代码77行至85行, 为了将sqrt IP核输出的数据与source_valid、 source_sop、source_eop信号对齐, 对这三个信号进行了打拍处理。我们在顶层例化了LCD模块(LCD_top) , 其内部结构如下所示:

image860.png (41.61 KB)

2019-7-28 15:23 上传

图 51.4.14 LCD模块的内部结构图

如图所示LCD模块(LCD_top) 内部例化了3个模块: fifo控制模块(fifo_ctrl) 、 fifo缓存模块(FFT_LCD_FIFO) 、 LCD显示模块(lcd_rgb_top) 。 FFT模块(FFT_top) 传输过来的幅度数据经过fifo控制模块(fifo_ctrl) 处理, 送到fifo缓存模块(FFT_LCD_FIFO) 进行缓

存, 然后送给LCD用于频谱显示。fifo控制模块(fifo_ctrl) : fifo控制模块负责fifo缓存模块(FFT_LCD_FIFO) 的读写控制。 由于经过FFT得到的频谱是对称的, 所以只需要显示频谱的一半即可, 因此这里缓存的一帧数据的长度为64, 也就是采样长度128的一半。 此外, 由于LCD读取数据的速度较慢, 为了防止fifo缓存模块(FFT_LCD_FIFO) 写满, 这里对fifo缓存模块(FFT_LCD_FIFO) 的写数据使能做了一些处理。 此外, 当LCD显示模块(lcd_rgb_top) 请求数据的时候, fifo控制模块(fifo_ctrl) 负责拉高fifo缓存模块(FFT_LCD_FIFO) 的读数据使能。

fifo缓存模块(FFT_LCD_FIFO) : fifo缓存模块负责缓存频谱幅度数据, 当读数据使能拉高的时候, 输出数据给LCD显示模块(lcd_rgb_top) 。LCD显示模块(lcd_rgb_top) : LCD显示模块负责依据读取到的幅度数据, 在RGB TFT-LCD上显示频谱。

LCD模块(LCD_top) 的代码如下所示:

1 module LCD_top(

2 input clk50M,

3 input clk10M,

4 input rst_n,

5 6

output lcd_hs,

7 output lcd_vs,

8 output lcd_de,

9 output [15:0] lcd_rgb,

10 output lcd_bl,

11 output lcd_rst,

12 output lcd_pclk,

13

14 input [15:0] fft_data,

15 input fft_sop,

16 input fft_eop,

17 input fft_valid

18 );

19

20 //wire define

21 wire [6:0] line_cnt;

22 wire [15:0] line_length;

23 wire data_req;

24 wire wr_over;

25

26 wire fifo_wr_req;

27 wire fifo_rd_req;

28 wire [15:0] fifo_wr_data;

29 wire fifo_empty;

30

31 //*****************************************************

32 //** main code

33 //*****************************************************

34

35 //fifo读写控制模块

36 fifo_ctrl u_fifo_ctrl(

37 .clk_50m (clk50M),

38 .lcd_clk (clk10M),

39 .rst_n (rst_n),

40

41 .fft_data (fft_data),

42 .fft_sop (fft_sop),

43 .fft_eop (fft_eop),

44 .fft_valid (fft_valid),

45

46 .data_req (data_req),

47 .wr_over (wr_over),

48 .rd_cnt (line_cnt), //频谱的序号

49

50 .fifo_wr_data (fifo_wr_data),

51 .fifo_wr_req (fifo_wr_req),

52 .fifo_rd_req (fifo_rd_req)

53 );

54

55 //例化fifo

56 FFT_LCD_FIFO FFT_LCD_FIFO_inst (

57 .aclr (~rst_n),

58 //写端口

59 .wrclk (clk50M),

60 .wrreq (fifo_wr_req),

61 .data (fifo_wr_data),

62 //读端口

63 .rdclk (clk10M),

64 .rdreq (fifo_rd_req),

65 .q (line_length), //频谱的幅度

66

67 .rdempty (fifo_empty)

68 );

69

70 //LCD驱动显示模块

71 lcd_rgb_top u_lcd_rgb_top(

72 .lcd_clk (clk10M),

73 .sys_rst_n (rst_n &(~fifo_empty)),

74

75 .lcd_hs (lcd_hs),

76 .lcd_vs (lcd_vs),

77 .lcd_de (lcd_de),

78 .lcd_rgb (lcd_rgb),

79 .lcd_bl (lcd_bl),

80 .lcd_rst (lcd_rst),

81 .lcd_pclk (lcd_pclk),

82

83 .line_cnt (line_cnt), //频谱的序号(0~63)

84 .line_length (line_length[15:3]),//频谱的幅度, 缩小8倍以适应屏幕尺寸

85 .data_req (data_req), //请求频谱数据输入

86 .wr_over (wr_over) //” 一条频谱绘制完成” 标志信号

87 );

88

89 endmodule

LCD模块(LCD_top) 完成了三个子模块的例化。 不过需要注意的是, 在代码的第84行, 为了在播放音乐的时候能够看到合适的频谱, 我们对频谱的幅度进行了缩放处理。接下来, 我们看一下fifo控制模块(fifo_ctrl) , fifo控制模块的代码如下所示:

1 module fifo_ctrl(

2 input clk_50m,

3 input lcd_clk,

4 input rst_n,

5 6

input [15:0] fft_data,

7 input fft_sop,

8 input fft_eop,

9 input fft_valid,

10

11 input data_req, //外部数据请求信号

12 input wr_over,

13 output reg [6:0] rd_cnt,

14

15 output [15:0] fifo_wr_data,

16 output fifo_wr_req,

17 output reg fifo_rd_req

18 );

19

20 //parameter define

21 parameter Transform_Length = 128;

22

23 //reg define

24 reg [1:0] wr_state;

25 reg [1:0] rd_state;

26 reg [6:0] wr_cnt;

27 reg wr_en;

28 reg fft_valid_r;

29 reg [15:0] fft_data_r;

30

31 //*****************************************************

32 //** main code

33 //*****************************************************

34

35 //产生fifo写请求信号

36 assign fifo_wr_req = fft_valid_r && wr_en;

37 assign fifo_wr_data = fft_data_r;

38

39 //将数据与有效信号延时一个时钟周期

40 always @ (posedge clk_50m or negedge rst_n) begin

41 if(!rst_n) begin

42 fft_data_r <= 16'd0;

43 fft_valid_r <= 1'b0;

44 end

45 else begin

46 fft_data_r <= fft_data;

47 fft_valid_r <= fft_valid;

48 end

49 end

50

51 //控制FIFO写端口, 每次向FIFO中写入前半帧(64个) 数据

52 always @ (posedge clk_50m or negedge rst_n) begin

53 if(!rst_n) begin

54 wr_state <= 2'd0;

55 wr_en <= 1'b0;

56 wr_cnt <= 7'd0;

57 end

58 else begin

59 case(wr_state)

60 2'd0: begin //等待一帧数据的开始信号

61 if(fft_sop) begin

62 wr_state <= 2'd1;

63 wr_en <= 1'b1;

64 end

65 else begin //进入写数据过程, 拉高写使能wr_en

66 wr_state <= 2'd0;

67 wr_en <= 1'b0;

68 end

69 end

70 2'd1: begin

71 if(fifo_wr_req) //对写入FIFO中的数据计数

72 wr_cnt <= wr_cnt + 1'b1;

73 else

74 wr_cnt <= wr_cnt;

75 //由于FFT得到的数据具有对称性, 因此只取一帧数据的一半

76 if(wr_cnt < Transform_Length/2 - 1'b1) begin

77 wr_en <= 1'b1;

78 wr_state <= 2'd1;

79 end

80 else begin

81 wr_en <= 1'b0;

82 wr_state <= 2'd2;

83 end

84 end

85 2'd2: begin //当FIFO中的数据被读出一半的时候, 进入下一帧数据写过程

86 if((rd_cnt == Transform_Length/4)&& wr_over) begin

87 wr_cnt <= 7'd0;

88 wr_state <= 2'd0;

89 end

90 else

91 wr_state <= 2'd2;

92 end

93 default:

94 wr_state <= 2'd0;

95 endcase

96 end

97 end

98

99 //控制FIFO读端口, 每次输出一个数据用于绘制频谱

100 always @ (posedge lcd_clk or negedge rst_n) begin

101 if(!rst_n) begin

102 rd_state <= 2'd0;

103 rd_cnt <= 7'd0;

104 fifo_rd_req <= 1'b0;

105 end

106 else begin

107 case(rd_state)

108 2'd0: begin //外部请求频谱数据时, 拉高读FIFO请求信号

109 if(data_req) begin

110 fifo_rd_req <= 1'b1;

111 rd_state <= 2'd1;

112 end

113 else begin

114 fifo_rd_req <= 1'b0;

115 rd_state <= 2'd0;

116 end

117 end

118 2'd1: begin //读FIFO请求仅拉高一个时钟周期

119 fifo_rd_req <= 1'b0;

120 rd_state <= 2'd2;

121 end

122 2'd2: begin //等待输出的频谱数据绘制结束

123 if(wr_over) begin

124 rd_state <= 2'd0;

125 if( rd_cnt== Transform_Length/2 -1 )

126 rd_cnt <= 7'd0;

127 else

128 rd_cnt <= rd_cnt + 1'b1;

129 end

130 else

131 rd_state <= 2'd2;

132 end

133 default:

134 rd_state <= 2'd0;

135 endcase

136 end

137 end

138

139 endmodule

在代码的59行至95行所描述的状态机如图 51.4.15所示 , 在state的值为0的时候,fft_sop信号(数据包开始信号) 一拉高, 就进入state值为1的状态。 此时当wr_req信号为高电平的时候(见代码36行, 此时fft_valid_r信号也为高电平, fft_valid_r为数据有效信号) ,

开始往fifo里写数据, 同时让wr_cnt计数器累加计数。 当wr_cnt计数器的值等于63的时候, 也就是fifo里写入了64个数据的时候, 进入下一状态。 在这个状态里保持等待, 直到LCD显示模块(lcd_rgb_top) 读了32个数据的时候回到state的值等于0的状态, 这样一直循环下去。

QQ截图20190528165007.png (37.77 KB)

2019-7-28 15:13 上传

图 51.4.15 往fifo内写数据状态机示意图

107行至135行代码所示的状态机如图 51.4.16所示:

QQ截图20190528165038.png (38.8 KB)

2019-7-28 15:13 上传

图 51.4.16 从fifo读数据状态机示意图

在state的值为0的时候, draw_able信号(LCD显示模块请求数据信号) 一拉高, 就进入state值为1的状态。 此时读取fifo里的一个数据, 并拉低rd_en信号, 然后进入下一状态。 当wr_over信号为高电平的时候(LCD显示模块显示了一条频谱) , 让rd_cnt计数器自加1(rd_cnt

计数器的值等于63的时候清零, 对应于代码的126行) , 并回到state的值为0的状态。我们在LCD模块(LCD_top) 中例化了fifo缓存模块(FFT_LCD_FIFO) , 它起到了缓存数据的作用, 我们在前面已经也对该模块的作用进行了详细的描述, 这里就不再赘述了。 接下来,

我们来了解一下LCD显示模块(lcd_rgb_top) 。我们在LCD显示模块中例化了LCD显示模块(lcd_rgb_top) , 它在内部还例化了两个模块:lcd驱动模块(lcd_driver模块) 以及lcd显示模块(lcd_display模块) 。 LCD显示模块

(lcd_rgb_top) 的内部结构图如下所示:

image863.png (85.95 KB)

2019-7-28 15:13 上传

图 51.4.17 LCD显示模块内部结构图

lcd驱动模块(lcd_driver模块) : 在像素时钟的驱动下输出数据使能信号用于数据同步,同时还需要输出像素点的纵横坐标, 供LCD显示模块(lcd_display) 调用, 以绘制图案。 有关LCD驱动模块的详细介绍请大家参考“RGB TFT-LCD彩条显示实验” 章节。

接下来我们了解一下lcd显示模块(lcd_display模块) , 它的代码如下所示:

1 module lcd_display(

2 input lcd_clk, //lcd驱动时钟

3 input sys_rst_n, //复位信号

4 5

input [10:0] pixel_xpos, //像素点横坐标

6 input [10:0] pixel_ypos, //像素点纵坐标

7 8

input [6:0] line_cnt, //频点

9 input [15:0] line_length, //频谱数据

10 output data_req, //请求频谱数据

11 output wr_over, //绘制频谱完成

12 output [15:0] lcd_data //LCD像素点数据

13 );

14

15 //parameter define

16 parameter H_LCD_DISP = 11'd480; //LCD分辨率——行

17 localparam BLACK = 16'b00000_000000_00000; //RGB565 黑色

18 localparam WHITE = 16'b11111_111111_11111; //RGB565 白色

19

20 //*****************************************************

21 //** main code

22 //*****************************************************

23

24 //请求像素数据信号(这里加8是为了图像居中显示)

25 assign data_req = ((pixel_ypos == line_cnt * 4'd4 + 4'd8 - 4'd1)

26 && (pixel_xpos == H_LCD_DISP - 1)) ? 1'b1 : 1'b0;

27

28 //在要显示图像的列, 显示line_length长度的白色条纹

29 assign lcd_data = ((pixel_ypos == line_cnt * 4'd4 + 4'd8)

30 && (pixel_xpos <= line_length)) ? WHITE : BLACK;

31

32 //wr_over标志着一个频点上的频谱绘制完成,该信号会触发line_cnt加1

33 assign wr_over = ((pixel_ypos == line_cnt * 4'd4 + 4'd8)

34 && (pixel_xpos == H_LCD_DISP - 1)) ? 1'b1 : 1'b0;

35

36 endmodule

正点原子4.3寸RGB TFT-LCD屏幕的分辨率是480*272的, LCD的扫描原理是扫描完一行接着扫描下一行的。 而LCD的数据来源是fifo, 无法保存已经读过的数据。 那么为了能在LCD上显示频谱(在屏幕上显示64个像素条) , 我们将272行像素点64等分, 也就是每4行显示一个频率点

的幅度图像, 这样272行像素还余下16行像素不显示图像。 为了让频谱能够居中显示, 我们从第8行开始显示第一个频率点的幅度图像, 幅度图像(像素条) 的长度由该频率点的幅值(从fifo中读出) 决定。如代码29行所示, 我们在第8行开始显示第一个频率点的幅度图像, 当列像素点的值小于处理后的频谱幅值时(line_length) , 显示白色像素点, 其他像素点不显示。 然后以4行为间隔显示其他频率点的幅度图像。 但在显示图像之前, 需要先获取幅值。 所以在代码第25行, 我们在显示频谱条纹的前一行的最后一列发出读请求信号, 从fifo中获得幅值用于绘制频谱。 此

外, 如代码的第32行所示, 每当一条频谱绘制完成后, 将绘制完成的标志信号wr_over拉高,通知fifo_ctrl模块当前频谱绘制完成。 然后随着line_cnt计数器从0累加到63, 再回到0这样循环的变化, 我们就能在LCD上观察到不断变化的频谱。

到此, 程序设计部分就结束了。

51.5 下载验证

首先我们打开IP核之FFT实验工程, 在工程所在的路径下打开FFT_audio_lcd/par文件夹,在里面找到“FFT_audio_lcd.qpf” 并双击打开。 注意工程所在的路径名只能由字母、 数字以及下划线组成, 不能出现中文、 空格以及特殊字符等。 工程打开后如图 51.5.1示:

image864.png (110.01 KB)

2019-7-28 15:13 上传

图 51.5.1 IP核之FFT实验工程

将下载器一端连接电脑, 另一端与开发板上对应端口连接, 然后用音频线连接电脑和开发板, 最后连接电源线并打开电源开关。需要注意的是, 使用FFT IP核需要LICENSE! 如果我们的LICENSE文件不包含该IP核的使用许可, 那么工程编译结束之后, 将会生成一个带“_time_limited” 后缀的sof文件。 该sof文件只能运行一个小时, 然后自动停止运行, 不过这并不影响我们本次实验的下载验证。点击工具栏中的“Programmer” 图标打开下载界面, 通过点击“Add File” 按钮选择FFT_audio_lcd/par/output_files 目 录 下 的 “FFT_audio_lcd_time_limited.sof” 或 者“FFT_audio_lcd.sof” 文件。开发板电源打开后, 在程序下载界面点击“Hardware Setup” , 在弹出的对话框中选择当前的硬件连接为“USB-Blaster[USB-0]” 。 然后点击“Start” 将工程编译完成后得到的sof文件下载到开发板中, 如下图所示:

image865.png (50.35 KB)

2019-7-28 15:13 上传

图 51.5.2 下载界面

下载完成后,打开工程目录下的“音频文件”文件夹,里面有个名为“SHT_noise_96k.wav”的音频文件。 该音频是掺杂了噪声的一小段“上海滩” 音乐, 噪声频率为9.6KHz。 在电脑上使用播放器播放这段音频, 我们可以听到开发板背面的喇叭在播放上海滩的音乐, 音乐中混杂了一个尖锐的类似蜂鸣器的声音, 同时我们可以在LCD上看到如图 51.5.3所示的音频频谱图。

QQ截图20190528165134.png (14.39 KB)

2019-7-28 15:13 上传

图 51.5.3 频谱图

由本章简介部分的内容可知, 频谱第n个点对应信号频率为: F*(n-1)/N。 我们的采样频率F是WM8978内部ADC的采样频率, 即48KHz; N是FFT IP核的一次频谱分析长度, 即128; n是频谱中白色条纹的序号。上图中, 幅度最高的频谱的序号为27(从左往右数第27个白色条纹最高) , 经过计算得出该频谱对应的频率为48*(27-1)/128=9.75KHz, 它就是我们在音乐播放过程中所听到的9.6KHz的高频噪声。 从频谱中计算出来的频率值误差为0.15KHz, 在频率精度(48/128=0.375KHz) 范围内, 说明我们本次实验在开拓者FPGA开发板上下载验证成功。

频谱仪的更改ip_【正点原子FPGA连载】第五十一章 基于FFT IP核的音频频谱仪-摘自【正点原子】开拓者 FPGA 开发指南 (amobbs.com 阿莫电子论坛)...相关推荐

  1. linux 正点原子ov5640_【正点原子FPGA连载】第四十七章 基于OV5640的以太网传输视-摘自【正点原子】开拓者 FPGA 开发指南 (amobbs.com 阿莫电子论坛)...

    本帖最后由 正点原子 于 2020-10-23 17:12 编辑 203429z6c3os33t8albi33.png (66.36 KB) 2019-7-26 22:21 上传 第四十七章 基于OV ...

  2. linux 正点原子ov5640_【正点原子FPGA连载】第二十六章基于OV5640的二值化实验-摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南 (amobbs.com 阿莫电子论坛)...

    本帖最后由 正点原子 于 2020-10-26 16:21 编辑 QQ群头像.png (1.78 KB) 2020-10-24 10:50 上传5)关注正点原子公众号,获取最新资料 100846rel ...

  3. zynqsd的读写数据_【正点原子FPGA连载】 第十二章SD卡读写TXT文本实验-摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南 (amobbs.com 阿莫电子论坛)...

    本帖最后由 正点原子 于 2020-10-24 10:25 编辑 QQ群头像.png (1.78 KB) 2020-10-24 10:25 上传5)关注正点原子公众号,获取最新资料 100846rel ...

  4. linux 正点原子ov5640_【正点原子FPGA连载】第二十四章OV5640摄像头HDMI显示-摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南 (amobbs.com 阿莫电子论坛)...

    [code]#----------------------摄像头接口的时钟--------------------------- #72M create_clock -period 13.888 -n ...

  5. linux 正点原子ov5640_【正点原子FPGA连载】第二十三章OV5640摄像头LCD显示-摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南 (amobbs.com 阿莫电子论坛)...

    #----------------------摄像头接口的时钟--------------------------- #72M create_clock -period 13.888 -name ca ...

  6. 正点原子linux串口驱动下载,【正点原子Linux连载】第二十一章UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南 (amobbs.com 阿莫电子论坛)...

    示例代码21.3.1 bsp_uart.h文件代码 1  #ifndef _BSP_UART_H 2  #define _BSP_UART_H 3  #include "imx6ul.h&q ...

  7. input自适应_【正点原子FPGA连载】第十一章基于OV5640的自适应二值化实验-领航者ZYNQ之HLS 开发指南...

    1)摘自[正点原子]领航者ZYNQ之HLS 开发指南 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手 ...

  8. FPGA数字信号处理(九)Vivado FFT IP核实现

    该篇是FPGA数字信号处理的第9篇,选题为DSP系统中极其常用的FFT运算.上篇介绍了Quartus环境下FFT IP核的使用"FPGA数字信号处理(八)Quartus FFT IP核实现h ...

  9. 韦东山 IMX6ULL和正点原子_「正点原子FPGA连载」第十一章RGB LCD彩条显示(一)

    1)实验平台:正点原子达芬奇FPGA开发板 2) 摘自[正点原子]达芬奇之Microblaze 开发指南 3)购买链接:https://detail.tmall.com/item.htm?id=624 ...

最新文章

  1. java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderL
  2. 百度 UNIT 技术负责人揭秘:如何让你的对话系统更智能
  3. 从员工出走仅剩 5 人,到一支打胜仗的铁军
  4. 多进程Socket_Server
  5. 外星人图像和外星人太空船_卫星图像:来自太空的见解
  6. WIFI 网络操作--------------------笔记
  7. centOS 安装及部署 SVN
  8. 开课吧:Java开发学习路线-基础知识
  9. ntp server 配置参数_NTP时间服务器的配置
  10. informix 如何下载
  11. 红帽子linux系统下载服务器OS:Windows、Linux与Unix三分天下
  12. 综述---图像处理中的注意力机制
  13. 科学网—世界上最神奇的30个经典定律——读书笔记 - 贾琳的博文 http://blog.sciencenet.cn/blog-455749-859616.html
  14. antdesign 柱状图_你绝对想不到柱形图背后有这么多故事
  15. 计算机无法自动排列,为什么我的电脑不能自动排列图标
  16. UITableView上下滚动卡顿(获取网络数据,下载图片之后)
  17. 使用javascript实现植物大战僵尸部分功能
  18. Java判断日期格式是否正确
  19. 自己动手写数据库系统:容灾恢复原理和容灾恢复日志的设计
  20. 常用图标素材分享网站

热门文章

  1. 基于阿里平头哥的单片机软、硬件i2C驱动oled
  2. 韵达快递单号可以批量查询吗
  3. win10定时关机c语言,win10定时关机怎么设置,win10设置定时关机设置方法-电脑怎么定时开关机...
  4. 锂离子电池热失控预警资料整理(三)
  5. Uncaught SyntaxError: Unexpected identifier问题解决
  6. 计算机视觉-图像处理基础
  7. Android 下拉选择框自定义view
  8. Linux 启动时间优化实战,2.41 秒启动应用!
  9. 浅谈单片机、ARM和DSP的异同
  10. Linux基础认知——Linux系统文件、目录结构认知