本帖最后由 Harvestlamb 于 2017-8-24 00:37 编辑
项目名称:基于FPGA的数字频率计
试用计划:基于fpga的数字频率计,能达到2015年全国大学生电子设计大赛频
率测量测技术指标(100M),并且达到精准测频,误差符合题目要求。
今天正式起笔,感谢朋友的支持让我得到了试用机会,其次感谢小梅哥能提供FPGA学习平台,首先进入硬件展示篇:正面
背面
附送模块
整体合影
硬件上的优点外围芯片相比较其他类型产品很用心,比如dac芯片,比我的锆石好很多。同时引脚分配图在背面也能加分。
至于缺点,就是layout没那么精致(因人而异,500元级别还可以)
教学上,我觉得实体教学最好,网络始终不能有那么好的效果,听说小梅哥最近开了实体课。但是现在网络课程中不得不说小梅哥的课程可以说通俗易懂,老少皆宜.
数字频率计的代码部分.首先我想使用小梅哥板子上的数码管,数码管等视频讲解在购买后都会有的(设计与验证vip版群里有,这个PDF十分适合入门),弄了一下午代码,觉得位数不够,最终索性就不用数码管了,使用了uart发送给STM32.
我想说说测频的方法,直接测频,周期测频,等精度测频。直接测频便是在自己定的1s闸门时间内,计数待测信号的上升沿个数。(这一测开发板上便是用这一方法。但是把 fre_in写入敏感事件列不是特别好。)
- module fre(reset,clk,fre_in,zhiout);
- input clk,reset;
- input fre_in;
- output [26:0]zhiout;
- reg [26:0]i,j,l;
-
- reg k;
- always @(posedge clk or negedge reset)
- if(!reset)
- begin
- i<=0;
- l<=0;
- k<=0;
- end
- else if(i==49999999)
- begin
- i<=0;
- l<=j;
- k<=1;
- end
- else
- begin
- i=i+1;
- k<=0;
- end
- always @(posedge fre_in or negedge reset or posedge k)
- if(!reset)
- j<=0;
- else if(k==1)
- j<=0;
- else
- j<=j+1;
- assign zhiout=l;
- endmodule
复制代码
周期测频是在一个被测信号的周期内测系统时钟clk的个数。(这里我把主频clk使用了pll 倍频到了500m,并且最后如果大家采用这种方法,自行处理数据读出的时间,否则数据通过uart发送时会比较乱。)
- module fre (
- input clk,
- input inpluse,
- output reg [26:0] cntout,
- output reg cntok,
- output [26:0] fre
- );
-
- reg [31:0] cnt;
- reg plusebuf;
-
- always @ (posedge clk)
- begin
- plusebuf <= inpluse;
- end
- always @ (posedge clk)
- begin
- if ((plusebuf == 1'b0)&&(inpluse == 1'b1))
- begin
-
- cntout <= cnt;
- cnt <= 32'd1;
- cntok <= 1'b1;
- end
- else
- begin
- cnt <= cnt + 32'd1;
- cntok <= 1'b0;
- end
- end
-
- assign fre = 500000000/cntout;
-
-
- endmodule
复制代码
等精度测频
这两张PPT便是等精度测频的核心精髓(此PPT源于武汉大学电赛培训)
至于uart发送部分的,先看看RTL视图
- module tx_module_start
- (
- // input port
- CLK_50M , RST_N , data_tx ,
- // output port
- tx_start_flag , data_tx_div
- );
- //--------------------------------------------------------------------
- // 外部端口定义
- //--------------------------------------------------------------------
- input CLK_50M ; // 时钟输入
- input RST_N; //复位的端口,低电平复位
- input [26:0] data_tx ;
- output reg tx_start_flag ;
- output reg [7:0 ] data_tx_div ;
- //--------------------------------------------------------------------
- // 内部端口定义
- //--------------------------------------------------------------------
- reg [20:0] time_seconds; //秒钟低位计数器
- reg [20:0] time_seconds_n; //time_seconds的下一个状态
- parameter SEC_TIME_1mS = 20'd50_000;
- reg [9:0] num_cnt ; //定义10位的加法计数器
- reg [9:0] num_cnt_n; //num_cnt的下一个状态
- parameter SET_NUM_1000 = 8'd10;
- //---------------------------------------------------------------------------
- //时序威廉希尔官方网站
,用来给time_seconds寄存器赋值
- always @ (posedge CLK_50M or negedge RST_N)
- begin
- if(!RST_N) //判断复位
- time_seconds <= 1'b0; //初始化time_seconds值
- else
- time_seconds <= time_seconds_n; //用来给time_seconds赋值
- end
- //组合威廉希尔官方网站
,实现1s的定时计数器
- always @ (*)
- begin
- if(time_seconds == SEC_TIME_1mS) //判断1s时间
- time_seconds_n = 1'b0; //如果到达1s,定时计数器将会被清零
- else
- time_seconds_n = time_seconds + 1'b1;
- end
- //---------------------------------------------------------------------------
- //时序威廉希尔官方网站
,用来给num_cnt寄存器赋值
- //时序威廉希尔官方网站
,用来给num_cnt寄存器赋值
- always @ (posedge CLK_50M or negedge RST_N)
- begin
- if(!RST_N) //判断复位
- num_cnt <= 1'b0; //初始化time_seconds值
- else
- num_cnt <= num_cnt_n; //用来给time_seconds赋值
- end
- //组合威廉希尔官方网站
- always @ (*)
- begin
- if(time_seconds == SEC_TIME_1mS)
- num_cnt_n = num_cnt + 1'b1;
- else if(num_cnt == SET_NUM_1000)
- num_cnt_n = 1'b0;
- else
- num_cnt_n = num_cnt; //否则,定时计数器将会保持不变
- end
- //---------------------------------------------------------------------------
- //组合威廉希尔官方网站
,用来判断当前数据
- always @ (*)
- begin
- if(num_cnt <= 4'd3)
- tx_start_flag = 1'b1 ;
- else
- tx_start_flag = 1'b0 ;
- end
- //---------------------------------------------------------------------------
- //设置定时器用来设定当前发送数据
- reg [2:0] count_send ;
- reg [2:0] count_send_n ;
- always @ (posedge CLK_50M or negedge RST_N)
- begin
- if(!RST_N) //判断复位
- count_send <= 1'b0;
- else
- count_send <= count_send_n;
- end
- //组合威廉希尔官方网站
- always @ (*)
- begin
- if(num_cnt == SET_NUM_1000)
- count_send_n = count_send + 1'b1;
- else if (count_send == 3'd7)
- count_send_n = 1'b0;
- else
- count_send_n = count_send; //否则,定时计数器将会保持不变
- end
- always @ (*)
- begin
- case (count_send)
- 3'd0 : data_tx_div = 8'b11001100;//0xcc(stm32校验码,自行设置)
- 3'd1 : data_tx_div = {1'b0,1'b0,1'b0,1'b0,1'b0,data_tx[26],data_tx[25],data_tx[24]};
- 3'd2 : data_tx_div = {data_tx[23:16]};
- 3'd3 : data_tx_div = {data_tx[15:8]};
- 3'd4 : data_tx_div = {data_tx[7:0]};
- 3'd5 : data_tx_div = 8'b00001101;
- 3'd6 : data_tx_div = 8'b00001010;
- endcase
- end
- endmodule
复制代码
这个代码可以自行修改,发送10位数据都是可以的。由于篇幅限制,完整的代码我会开源的,压缩包的形式自行下载(stm32部分代码后续有需求的话会上传)。
整体效果展示
|
|
|
|