顶层模块
module top_jishuqi(
//system clock
input sys_clk , // 时钟信号
input sys_rst_n, // 复位信号
//cymometer interface
input clk_fx , // 被测信号
output clk_out , // 模拟的输出时钟 这里由b3输出
//user interface
output [5:0] sel , // 数码管位选
output [7:0] seg_led , // 数码管段选
output uart_txd //UART发送端口
);
//parameter define
parameter CLK_FS = 26'd50000000; // 基准时钟频率值 fsclk
//wire define
wire [19:0] data_fx; // 被测信号测量值
//parameter define
parameter CLK_FREQ = 50000000; //定义系统时钟频率
parameter UART_BPS = 115200; //定义串口波特率 修改波特率时候可以直接在这里修改
//*****************************************************
//** main code
//*****************************************************
//例化等精度频率计模块
cymometer #(.CLK_FS(CLK_FS) // 基准时钟频率值
) u_cymometer(
//system clock
.clk_fs (sys_clk ), // 基准时钟信号
.rst_n (sys_rst_n), // 复位信号
//cymometer interface
.clk_fx (clk_fx ), // 被测时钟信号
.data_fx (data_fx ) // 测量的被测时钟频率数值
);
//例化测试时钟模块,产生测试时钟
clk_test #(.DIV_N(12'd2000) // 分频系数 100分频
) u_clk_test(
//源时钟
.clk_in (sys_clk ), // 输入时钟
.rst_n (sys_rst_n), // 复位信号
//分频后的时钟
.clk_out (clk_out ) // 测试时钟
);
//例化数码管显示模块
seg_led u_seg_led(
//module clock
.clk (sys_clk ), // 数码管驱动模块的驱动时钟
.rst_n (sys_rst_n), // 复位信号
//seg_led interface
.sel (sel ), // 数码管位选
.seg_led (seg_led ), // 数码管段选
//user interface
.data (data_fx ), // 被测频率值
.point (6'd0 ), // 数码管显示的点控制
.en (1'b1 ), // 数码管驱动使能信号
.sign (1'b0 ) // 控制符号位显示
);
usart #( //串口发送模块
.CLK_FREQ (CLK_FREQ), //设置系统时钟频率
.UART_BPS (UART_BPS)) //设置串口发送波特率
u_uart_send(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (data_fx),
.uart_txd (uart_txd)//发送端口
);
endmodule
测试信号产生模块
//系统时钟位50M 100分频后为0.5Mhz
module clk_test #(parameter DIV_N = 12'd2000)// = 7'd1000) //分频系数
(
//源时钟
input clk_in , // 输入时钟
input rst_n , // 复位信号
//分频后的时钟
output reg clk_out // 输出时钟
);
//reg define
reg [31:0] cnt; // 时钟分频计数
//*****************************************************
//** main code
//*****************************************************
//时钟分频
always @(posedge clk_in or negedge rst_n) begin
if(rst_n == 1'b0) begin
cnt <= 0;
clk_out <= 0;
end
else begin
if(cnt == DIV_N/2 - 1'b1) begin//周期1/2时 电平反转
cnt <= 10'd0;
clk_out <= ~clk_out;
end
else
cnt <= cnt + 1'b1;
end
end
endmodule
输入捕获
module cymometer
#(parameter CLK_FS = 26'd50_000_000) // 基准时钟频率值
( //system clock
input clk_fs , // 基准时钟信号
input rst_n , // 复位信号
//cymometer interface
input clk_fx , // 被测时钟信号
output reg [19:0] data_fx // 测量的被测时钟频率值输出
);
//parameter define
localparam MAX = 6'd32; // 定义fs_cnt、fx_cnt的最大位宽
localparam GATE_TIME = 16'd5_000; // 门控时间设置
//reg define
reg gate ; // 门控信号
reg gate_fs ; // 同步到基准时钟的门控信号
reg gate_fs_r ; // 用于同步gate信号的寄存器
reg gate_fs_d0 ; // 用于采集基准时钟下gate下降沿
reg gate_fs_d1 ; //
reg gate_fx_d0 ; // 用于采集被测时钟下gate下降沿
reg gate_fx_d1 ; //
reg [ 15:0] gate_cnt ; // 门控计数
reg [MAX-1:0] fs_cnt ; // 门控时间内基准时钟的计数值
reg [MAX-1:0] fs_cnt_temp ; // fs_cnt 临时值
reg [MAX-1:0] fx_cnt ; // 门控时间内被测时钟的计数值
reg [MAX-1:0] fx_cnt_temp ; // fx_cnt 临时值
//wire define
wire neg_gate_fs; // 基准时钟下门控信号下降沿
wire neg_gate_fx; // 被测时钟下门控信号下降沿
//*****************************************************
//** main code
//*****************************************************
//边沿检测,捕获信号下降沿
assign neg_gate_fs = gate_fs_d1 & (~gate_fs_d0);
assign neg_gate_fx = gate_fx_d1 & (~gate_fx_d0);
//门控信号计数器,使用被测时钟计数
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n)
gate_cnt <= 16'd0;
else if(gate_cnt == GATE_TIME + 5'd20)//对5000+20个被测信号的周期进行计数
gate_cnt <= 16'd0;
else
gate_cnt <= gate_cnt + 1'b1;
end
//门控信号,拉高时间为GATE_TIME个实测时钟周期,创造一个门控信号
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n)
gate <= 1'b0;
else if(gate_cnt < 4'd10)//创造一个门控信号
gate <= 1'b0;
else if(gate_cnt < GATE_TIME + 4'd10)
gate <= 1'b1;
else if(gate_cnt <= GATE_TIME + 5'd20)
gate <= 1'b0;
else
gate <= 1'b0;
end
//将门控信号同步到基准时钟下,得到基准时钟下的门控信号
always @(posedge clk_fs or negedge rst_n) begin//注意这里是clk_fs
if(!rst_n) begin
gate_fs_r <= 1'b0;
gate_fs <= 1'b0;
end
else begin
gate_fs_r <= gate;
gate_fs <= gate_fs_r;
end
end
//打拍,采门控信号的下降沿(被测时钟下)
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n) begin
gate_fx_d0 <= 1'b0;
gate_fx_d1 <= 1'b0;
end
else begin
gate_fx_d0 <= gate;
gate_fx_d1 <= gate_fx_d0;
end
end
//打拍,采门控信号的下降沿(基准时钟下)
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
gate_fs_d0 <= 1'b0;
gate_fs_d1 <= 1'b0;
end
else begin
gate_fs_d0 <= gate_fs;
gate_fs_d1 <= gate_fs_d0;
end
end
//门控时间内对被测时钟计数
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n) begin
fx_cnt_temp <= 32'd0;
fx_cnt <= 32'd0;
end
else if(gate)
fx_cnt_temp <= fx_cnt_temp + 1'b1;
else if(neg_gate_fx) begin
fx_cnt_temp <= 32'd0;
fx_cnt <= fx_cnt_temp;
end
end
//门控时间内对基准时钟计数
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
fs_cnt_temp <= 32'd0;
fs_cnt <= 32'd0;
end
else if(gate_fs)
fs_cnt_temp <= fs_cnt_temp + 1'b1;
else if(neg_gate_fs) begin
fs_cnt_temp <= 32'd0;
fs_cnt <= fs_cnt_temp;
end
end
//计算被测信号频率
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
data_fx <= 20'd0;
end
else if(gate_fs == 1'b0)
data_fx <= (CLK_FS/fs_cnt)*(fx_cnt);
end
endmodule
数码管显示
//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved
//----------------------------------------------------------------------------------------
// File name: seg_led
// Last modified Date: 2018/3/13 9:03:06
// Last Version: V1.0
// Descriptions: 数码管动态显示模块
//----------------------------------------------------------------------------------------
// Created by: 正点原子
// Created date: 2018/3/13 9:03:09
// Version: V1.0
// Descriptions: The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module seg_led(
//module clock
input clk , // 时钟信号
input rst_n , // 复位信号
//seg_led interface
output reg [5:0] sel , // 动态显示被选中的数码管
output reg [7:0] seg_led, // 一个数码管中亮的灯,包含小数点
//user interface
input [19:0] data , // 6个数码管要显示的数值
input [5:0] point , // 小数点具体显示的位置,从高到低,高电平有效
input en , // 数码管使能信号
input sign // 符号位(高电平显示“-”号)
);
//parameter define
localparam MAX_NUM = 13'd5000 ; // 1ms计数值
localparam CLK_DIVIDE = 4'd10 ; // 时钟分频
//reg define
reg [12:0] cnt0 ; // 1ms计数
reg flag ; // 1ms计满标志信号
reg [2:0] cnt ; // 切换显示数码管用
reg [3:0] num1 ; // 送给要显示的数码管,要亮的灯
reg point1 ; // 要显示的小数点
reg [23:0] num ; // 24位bcd码用寄存器
reg [ 3:0] clk_cnt ; // 时钟计数
reg dri_clk ; // 驱动数码管操作的驱动时钟
//wire define
wire [3:0] data0 ; // 十万位数
wire [3:0] data1 ; // 万位数
wire [3:0] data2 ; // 千位数
wire [3:0] data3 ; // 百位数
wire [3:0] data4 ; // 十位数
wire [3:0] data5 ; // 个位数
//*****************************************************
//** main code
//*****************************************************
assign data5 = data / 17'd100000; // 十万位数
assign data4 = data / 14'd10000 % 4'd10; // 万位数
assign data3 = data / 10'd1000 % 4'd10 ; // 千位数
assign data2 = data / 7'd100 % 4'd10 ; // 百位数
assign data1 = data / 4'd10 % 4'd10 ; // 十位数
assign data0 = data % 4'd10; // 个位数
//生成数码管的驱动时钟用于驱动数码管的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dri_clk <= 1'b1;
clk_cnt <= 4'd0;
end
else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
clk_cnt <= 4'd0;
dri_clk <= ~dri_clk;
end
else
clk_cnt <= clk_cnt + 1'b1;
end
//将20位2进制数转换为8421bcd码
always @ (posedge dri_clk or negedge rst_n) begin
if (!rst_n)
num <= 24'b0;
else begin
if (data5 || point[5]) begin
num[23:20] <= data5;
num[19:16] <= data4;
num[15:12] <= data3;
num[11:8] <= data2;
num[ 7:4] <= data1;
num[ 3:0] <= data0;
end
else begin
if (data4 || point[4]) begin
num[19:0] <= {data4,data3,data2,data1,data0};
if(sign)
num[23:20] <= 4'd11;
else
num[23:20] <= 4'd10;
end
else begin
if (data3 || point[3]) begin
num[15: 0] <= {data3,data2,data1,data0};
num[23:20] <= 4'd10;
if(sign)
num[19:16] <= 4'd11;
else
num[19:16] <= 4'd10;
end
else begin
if (data2 || point[2]) begin
num[11: 0] <= {data2,data1,data0};
num[23:16] <= {2{4'd10}};
if(sign)
num[15:12] <= 4'd11;
else
num[15:12] <= 4'd10;
end
else begin
if (data1 || point[1]) begin
num[ 7: 0] <= {data1,data0};
num[23:12] <= {3{4'd10}};
if(sign)
num[11:8] <= 4'd11;
else
num[11:8] <= 4'd10;
end
else begin
num[3:0] <= data0;
if(sign)
num[23:4] <= {{4{4'd10}},4'd11};
else
num[23:4] <= {5{4'd10}};
end
end
end
end
end
end
end
//计数1ms
always @ (posedge dri_clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
flag <= 1'b0;
cnt0 <= 13'b0;
end
else if (cnt0 < MAX_NUM - 1'b1) begin
flag <= 1'b0;
cnt0 <= cnt0 + 1'b1;
end
else begin
flag <= 1'b1;
cnt0 <= 13'b0;
end
end
//计数器,用来计数6个状态(因为有6个灯)
always @ (posedge dri_clk or negedge rst_n) begin
if (rst_n == 1'b0)
cnt <= 3'b0;
else if(flag) begin
if(cnt < 3'd5)
cnt <= cnt + 1'b1;
else
cnt <= 3'b0;
end
end
//6个数码管轮流显示,完成刷新( 从右到左)
always @ (posedge dri_clk or negedge rst_n) begin
if(!rst_n) begin
sel <= 6'b000000;
num1 <= 4'b0;
end
else begin
if(en) begin
case (cnt)
3'd0 :begin
sel <= 6'b111110;
num1 <= num[3:0] ;
point1 <= ~point[0] ;
end
3'd1 :begin
sel <= 6'b111101;
num1 <= num[7:4] ;
point1 <= ~point[1] ;
end
3'd2 :begin
sel <= 6'b111011;
num1 <= num[11:8];
point1 <= ~point[2] ;
end
3'd3 :begin
sel <= 6'b110111;
num1 <= num[15:12];
point1 <= ~point[3] ;
end
3'd4 :begin
sel <= 6'b101111;
num1 <= num[19:16];
point1 <= ~point[4];
end
3'd5 :begin
sel <= 6'b011111;
num1 <= num[23:20];
point1 <= ~point[5];
end
default :begin
sel <= 6'b000000;
num1 <= 4'b0;
point1 <= 1'b1;
end
endcase
end
else
sel <= 6'b111111;
end
end
//数码管显示数据
always @ (posedge dri_clk or negedge rst_n) begin
if (!rst_n)
seg_led <= 7'h40;
else begin
case (num1)
4'd0 : seg_led <= {point1,7'b1000000};
4'd1 : seg_led <= {point1,7'b1111001};
4'd2 : seg_led <= {point1,7'b0100100};
4'd3 : seg_led <= {point1,7'b0110000};
4'd4 : seg_led <= {point1,7'b0011001};
4'd5 : seg_led <= {point1,7'b0010010};
4'd6 : seg_led <= {point1,7'b0000010};
4'd7 : seg_led <= {point1,7'b1111000};
4'd8 : seg_led <= {point1,7'b0000000};
4'd9 : seg_led <= {point1,7'b0010000};
4'd10: seg_led <= 8'b11111111;
4'd11: seg_led <= 8'b10111111;
default : seg_led <= {point1,7'b1000000};
endcase
end
end
endmodule
串口数据上传
module usart(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
input [19:0] data ,
output reg uart_txd //UART发送端口
);
reg [7:0] data_wei [5:0];
reg[19:0] data0;
//parameter define
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次
reg[31:0] i;
reg [ 31:0] Data_Count;
reg [7:0] arry [Data_Len-1:0];
parameter [ 31:0] Data_Len=32'd14;//修改此处
//reg define
reg usart_down;
reg uart_en_d0;
reg uart_en_d1;
reg [15:0] clk_cnt; //系统时钟计数器
reg [ 3:0] tx_cnt; //发送数据计数器
reg tx_flag; //发送过程标志信号'b
reg [7:0] tx_data;
reg first;
reg init=1;
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
if (init)begin
for(i=0; i<Data_Len; i=i+1'b1) begin
case(i)
0: arry[0] <= "F";
1: arry[1] <= "r";
2: arry[2] <= "e";
3: arry[3] <= ":";
10: arry[10] <= "H";
11: arry[11] <= "z";
12: arry[12] <= "\n";
13: arry[13] <= "\r";
endcase
init<=0;
end
end
end
end
//*****************************************************
//** main code
//*****************************************************
//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
end
else begin//创造usart_down的第一个上升沿 first实现
arry[4] <= data / 17'd100000+"0"; // 十万位
arry[5] <= data / 14'd10000 % 4'd10 +"0"; // 万位数
arry[6] <= data / 10'd1000 % 4'd10 +"0"; // 千位数 arry[4] <= "a";
arry[7] <= data / 7'd100 % 4'd10 +"0" ; // 百位数 arry[5] <= "g";
arry[8] <= data / 4'd10 % 4'd10 +"0" ; // 十位数 arry[6] <= "y";
arry[9] <= data % 4'd10 +"0" ; // 个位数 arry[7] <= "i";
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
usart_down <= 1'b0;
first <= 1'b1;
end
else if (first)begin//创造usart_down的第一个上升沿 first实现
usart_down <= 1;
first <= 0;
end
else if(tx_cnt == 4'd0) begin
usart_down <= 1;
end
else
usart_down <= 0;
end
//usart_down的边沿检测
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_en_d0 <= 1'b0;
uart_en_d1 <= 1'b0;
end
else begin
uart_en_d0 <= usart_down;
uart_en_d1 <= uart_en_d0;
end
end
//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
tx_flag <= 1'b0;
tx_data <= arry[0];
Data_Count <= 4'b0;
end
else if(en_flag) begin //检测到发送使能上升沿
tx_flag <= 1'b1; //进入发送过程,标志位tx_flag拉高
tx_data <= arry[Data_Count]; //寄存待发送的数据
end
else if((tx_cnt == 4'd10)&&(clk_cnt == BPS_CNT/2))
begin //计数到停止位中间时,停止发送过程
tx_flag <= 1'b0; //发送过程结束,标志位tx_flag拉低
Data_Count<=Data_Count+4'b1;
if (Data_Count==Data_Len)
Data_Count<=32'b0;
end
end
//进入发送过程后,启动系统时钟计数器与发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
clk_cnt <= 16'd0;
tx_cnt <= 4'd0;
end
else if (tx_flag) begin //处于发送过程
if (clk_cnt < BPS_CNT - 1) begin
clk_cnt <= clk_cnt + 1'b1;
tx_cnt <= tx_cnt;
end
else begin
clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1
end
end
else begin //发送过程结束
clk_cnt <= 16'd0;
tx_cnt <= 4'd0;
end
end
//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
uart_txd <= 1'b1;
else if (tx_flag)
case(tx_cnt)
4'd0: uart_txd <= 1'b0; //起始位
4'd1: uart_txd <= tx_data[0]; //数据位最低位
4'd2: uart_txd <= tx_data[1];
4'd3: uart_txd <= tx_data[2];
4'd4: uart_txd <= tx_data[3];
4'd5: uart_txd <= tx_data[4];
4'd6: uart_txd <= tx_data[5];
4'd7: uart_txd <= tx_data[6];
4'd8: uart_txd <= tx_data[7]; //数据位最高位
4'd9: uart_txd <= 1'b1; //停止位
default: ;
endcase
else
uart_txd <= 1'b1; //空闲时发送端口为高电平
end
endmodule