基于FPGA和STM32的频率计设计(100Mhz-1hz精确到0.1hz)

2023-10-24 17:10

本文主要是介绍基于FPGA和STM32的频率计设计(100Mhz-1hz精确到0.1hz),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • 1. 前言
    • 2. 设计
      • 2.1 要求
      • 2.2 说明
      • 2.3 设计方案
    • 3. 实现
      • 3.1 顶层模块
      • 3.2 预采样模块
      • 3.3高频测量模块
      • 3.4 低频测量模块
      • 3.5 定时器模块
      • 3.6 TestBench代码
      • 3.7 约束xdc文件
    • 4. 后言

1. 前言

2019年8月20日:本人一名FPGA的菜鸟,为了准备电赛,几天前完成了FPGA频率计和相位差测量的FPGA工程,这算我独立完成的FPGA工程代码,踩了很多坑,不过基本实现了入门,这个题目很适合菜鸟入门练手。现在特来分享心得用于交流。如有错误欢迎大家批评指正!。

2021年6月30日:博主目前已经参加工作,正从事FPGA的开发,会看之前的博客,有了比较多新的理解。之前很多地方不够严谨,实在抱歉。

平台:

  1. 黑金的Artix7开发板
  2. 正点原子战舰精英板
  3. Vivado2017.4
  4. Keil 5

2. 设计

2.1 要求

能够使用FPGA对由信号发生器(电平设置不能超过3.3V,与XDC文件中设置保持一致)产生100Mhz-1hz的频率的信号进行实时测量,对于低于1000hz的信号可以精确到0.1hz,将计算结果通过串口发送给stm32,显示在LCD屏幕上。上电开始测量可以自动换挡。

分为低频高频两档,高频档使用测频法,低频档使用测周法


2.2 说明

2021年6月30日:这个实验的信号输入未经过ADC,是通过FPGA的低速bank直接输入的,不过过程中加了一个IBUF。可以类似于测量低速bank输入的信号的频率。实用价值较低。


2.3 设计方案

  1. 首先对信号进行10ms的预采样,当频率高于1000hz时使用计数上升沿的方法,当频率低于1000hz时,计数两个上升沿之间的时间差,求得周期再求得倒数。

  2. 因为FPGA外部时钟只有50Mhz所以我们需要使用pll进行倍频。产生100Mhz和200Mhz的时钟,并且我们需要使用到串口,pll和串口代码由黑金附带教程的源代码稍作修改得到。stm32的代码由正点原子串口实验和LCD显示实验稍作修改得到。
    注意:

    其中还要注意一点verilog编程不同于c语言编程,verilog描述的是硬件结构,所有的硬件结构从供电起就是并行执行的,所以在预采样的处理上,我使用的方法是:将信号同时输入低频,高频,预采样模块,再根据预采样模块的输出决定顶层模块的最终输出。


3. 实现

3.1 顶层模块

  • 输入信号in_signal 我使用的是信号发生器直接发出的方波信号,黑金开发板基础时钟clk 50Mhz。
  • 顶层包含低频采样模块,高频采样模块,预采样模块,pll模块,串口模块

代码架构图如下:
工程结构


RTL图如下:

原理图


代码如下:

module fre_top2(input clk,input rst_n,input in_signal,input uart_rx,//output clk_100mhz_out,         //TB调试用//output [1:0]state_out,//output flag_10ms_out,//output [31:0]fre_cnt_out,//output [15:0]pre_cnt_num,output uart_tx);wire [1:0]state_wire;wire [31:0]fre_cnt1;        //LOW模块的最终结果wire [31:0]fre_cnt2;        //HIGH模块的最终结果 wire [31:0]fre_cnt;         //用于输出最终结果,作为串口的输出reg  [1:0]state;            //用于状态分级,01为LOW,10为高wire clk_100mhz;localparam LOW =  2'b01;localparam HIGH = 2'b10;//调用pll产生100mHz时钟pll_test pll_100mHz(.sys_clk(clk),              //system clock 50Mhz on board.rst_n(rst_n),              //reset ,low active.clk_out(clk_100mhz)  );//assign state_out=state_wire;     //tb调试用//assign fre_cnt_out=fre_cnt;//assign flag_10ms_out=flag_10ms;//预采样,确定频率计算方式 //定时10ms如果期间没有见得到两次上升沿就说明频率低于1KhzPre_sampling Pre_sampling(.clk(clk_100mhz),.rst_n(rst_n),.in_signal(in_signal),// .flag_10ms_out(flag_10ms),// .pre_cnt_num(pre_cnt_num),.state(state_wire));always @(*)beginstate <=state_wire;end////LOW频率计模块//小于1Khz的使用测量两个上升沿时间差LOW_fre_measure low_fre(.clk(clk_100mhz),.rst_n(rst_n),.in_signal(in_signal),.fre_cnt(fre_cnt1));// HIGH频率计模块//大于1Khz使用1s内计数上升沿个数的方法HIGH_fre_measure high_fre(.clk(clk_100mhz),.rst_n(rst_n),.in_signal(in_signal),.fre_cnt(fre_cnt2));assign fre_cnt= (state==LOW)? fre_cnt1:fre_cnt2; //使用串口发送结果uart_test uart_test1(.clk(clk),.rst_n(rst_n),.uart_rx(uart_rx),.fre_cnt(fre_cnt),.uart_tx(uart_tx));
endmodule

3.2 预采样模块

输出信号state,state=2’b01时为小于1000hz信号,state=2’b10时为大于1000hz信号。定时时间为10ms

module Pre_sampling(input clk,input rst_n,input in_signal,// output flag_10ms_out,             //tb调试用// output [15:0]pre_cnt_num,output reg [1:0] state);reg [15:0] fre_cnt_pre1;reg [15:0] fre_cnt_pre2;//reg low_state;wire flag_10ms;// assign flag_10ms_out=flag_10ms;      //tb调试用// assign pre_cnt_num=fre_cnt_pre2;localparam LOW =  2'b01;localparam HIGH = 2'b10;localparam nodefine = 2'b00;//对上升沿进行计数always @(posedge in_signal or negedge rst_n)beginif(!rst_n)fre_cnt_pre1 <=16'b0;else if(flag_10ms)fre_cnt_pre1 <= fre_cnt_pre1+1'b1;else //flag_1ms等于0fre_cnt_pre1 <=16'b0;end//always @(negedge flag_10ms)beginfre_cnt_pre2<=fre_cnt_pre1;end//判断上升沿个数小于10个,则说明频率为1Khz一下    //assign state=(fre_cnt_pre2 <=32'd1)? LOW:HIGH;always @(negedge flag_10ms) beginif(fre_cnt_pre2 < 32'd10)state<=LOW;else if(fre_cnt_pre2>=32'd10)state<=HIGH;elsestate<=nodefine;  end////调用定时器模块定时10msTime_10ms#(.CLK_times(28'd99_999))timer_10ms(.clk(clk),.rst_n(rst_n),.flag_10ms(flag_10ms));    endmodule

3.3高频测量模块

引入一个match模块将输入信号与时钟匹配上升沿。减少误差,定时时间1s为了方便。

注:在这里,我将输出信号的最高位设置成了1‘b1.因为我在单片机中需要判断接受到的数据是低频测量模块还是高频测量模块的输出,然后再采取不同的算法结算频率值。

//高于1khz使用此模块进行频率测量
//精确度要求
//定时时间1s
module HIGH_fre_measure(input clk,input rst_n,input in_signal,output [31:0] fre_cnt);reg[31:0]  fre_cnt_r1 = 32'b0;reg[31:0]  fre_cnt_r2 = 32'b0;wire clk_match;wire flag_1s;always@(negedge clk_match)beginfre_cnt_r2 <= fre_cnt_r1;end	always@(posedge in_signal or negedge rst_n)beginif(!rst_n)fre_cnt_r1 <= 32'b0;else if(clk_match)fre_cnt_r1 <= fre_cnt_r1 + 32'b1;elsefre_cnt_r1 <= 32'b0;endassign fre_cnt = {1'b1,fre_cnt_r2[30:0]};//调用定时模块1s     Time_1s#(.CLK_times(28'd99_999_999))timer_1s(.clk(clk),.rst_n(rst_n),.flag_1s(flag_1s));    Match match1(.in_signal(in_signal),.flag_xs(flag_1s),.clk_match(clk_match));endmodule // //在高频信号中使用同频率测量
//匹配上升沿
module Match(input flag_xs,input in_signal,output reg clk_match);always @(posedge in_signal)beginclk_match <= flag_xs;endendmodule//定时一秒

3.4 低频测量模块

在这个模块中,因为时钟频率足够高,我就直接使用记录两个上升沿之间时钟脉冲个数的方法,然后利用公式计算出频率。等于时钟频率➗计数值 (clk / cnt).

注:为达到精确到0.1hz的目的,我讲clk的实际值扩大了10倍然后再进行计算,在硬件除法器中,我们只能实现整除,余数将被抛弃,所以,我扩大10倍,使有效数字增加了一位。就达到了提高精度的方法。这个还需各位自行理解。

除法器IP核的使用请各位自行查找,csdn和其他博客中都有很详细的讲解

//低于1KHz使用此模块进行频率测量
//精确度可以达到0.1hz
module LOW_fre_measure(input clk,input rst_n,input in_signal,output [31:0] fre_cnt);reg rasing=1'b0; //捕捉上升沿reg[31:0]  fre_cnt_r1=32'd0;reg[31:0]  fre_cnt_r2=32'd0;reg[31:0]  clk_bigger_10times=32'd1_000_000_000; //时钟频率100Mh *10wire [63:0]fre_cnt_wire;                       //div输出always @(posedge in_signal)beginrasing <=~rasing;   endalways@(posedge clk or negedge rst_n)beginif(!rst_n)fre_cnt_r1 <= 32'b0;else if(rasing)fre_cnt_r1 <= fre_cnt_r1 + 32'b1;elsefre_cnt_r1 <= 32'b0;endalways @(negedge rasing)beginfre_cnt_r2<=fre_cnt_r1;    end//输出结果assign fre_cnt= fre_cnt_wire[63:32];// //除法器输出结果// div_ip div_ip1(//     .a(clk_bigger_10times_wire),        //输入50Mhz *10 最后一位为小数位//     //.a(32'd1_000_000_000),    //输入100Mhz * 10 得到的除出来结果为实际值10倍//     .b(fre_cnt_r2_wire),//     .yshang(fre_cnt_wire)// );div_gen_0 div_gen_0(.aclk                    (clk),.s_axis_divisor_tvalid   (1'b1),.s_axis_divisor_tdata    (fre_cnt_r2),.s_axis_dividend_tvalid  (1'b1),.s_axis_dividend_tdata   (clk_bigger_10times),.m_axis_dout_tvalid      (),.m_axis_dout_tdata       (fre_cnt_wire));   endmodule

3.5 定时器模块

定时器当时被bug弄烦了,弄成了两个模块,各位可以自行优化修改成一个module.

//定时一秒
module Time_1s#(parameter CLK_times = 28'd99_999_999)(input       clk,input       rst_n,output reg  flag_1s=1'd0);reg[27:0] timer;always@(posedge clk or negedge rst_n)beginif(!rst_n)timer <= 28'd0;else if(timer >= CLK_times)beginflag_1s<=~flag_1s;timer <= 28'd0;endelsetimer <= timer + 28'd1;endendmodule//定时1毫秒
module Time_10ms#(parameter CLK_times = 28'd999_999)(input       clk,input       rst_n,output reg  flag_10ms=1'd0);reg[27:0] timer;always@(posedge clk or negedge rst_n)beginif(!rst_n)timer <= 28'd0;else if(timer >= CLK_times)beginflag_10ms<=~flag_10ms;timer <= 28'd0;endelsetimer <= timer + 28'd1;endendmodule

3.6 TestBench代码

  • tb仿真文件编写十分简单,只要设置好顶层模块的输入输出就好,输入使用reg类型,输出使用wire类型,然后再对顶层模块进行例化,然后在initial模块中进行初始化。这个工程代码我都在板子上已经实验成功了,tb文件就是上板前进行逻辑的验证。
module fre_tb();reg clk;reg rst_n;reg in_signal;// reg in_signal1;// reg in_signal2;// reg enable;wire uart_tx;// wire clk_100mhz_out;wire [31:0]fre_cnt_out;//wire [1:0] state; // wire flag_10ms_out;// wire [15:0]pre_cnt_num;fre_top2 fre_top2_tb(.clk(clk),.rst_n(rst_n),.in_signal(in_signal),.uart_rx(8'b0),//.clk_100mhz_out(clk_100mhz_out),//.state_out(state),.fre_cnt_out(fre_cnt_out),//.flag_10ms_out(flag_10ms_out),//.pre_cnt_num(pre_cnt_num),.uart_tx(uart_tx));initialbeginclk=0;rst_n=0;in_signal=0;// in_signal1=0;// in_signal2=0;// enable=0;#100rst_n=  1;endalways #10 clk=~clk;// always #1000 in_signal1=~in_signal1;// always #600000 in_signal2=~in_signal2;// always #50000000 enable=~enable;// always @(posedge clk)//     begin//         if(enable)//         in_signal<=in_signal1;//         else//         in_signal<=in_signal2;//     endalways #600000 in_signal=~in_signal;
endmodule

3.7 约束xdc文件

  • 最好在我的xdc文件上修改,当初上板是出现了一些问题,查百度,后来听博主的就加入了几条仅配置IO part没有的代码。
############## NET - IOSTANDARD ##################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
#############SPI Configurate Setting##################
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] 
set_property CONFIG_MODE SPIx4 [current_design] 
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] create_clock -period 20 [get_ports sys_clk]set_property PACKAGE_PIN Y18 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN D16 [get_ports in_signal]
set_property PACKAGE_PIN F20 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports in_signal]set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets in_signal_IBUF] set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN F14 [get_ports uart_rx]
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx]
set_property PACKAGE_PIN F13 [get_ports uart_tx]
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx]

4. 后言

需要源码的朋友们点赞,评论区留言,我私发给大家

频率计是一个很好的练手项目,大家可以了解清楚原理后可以自己试试。代码的直接移植很大概率会出问题,场景不同,环境不同,需求不同。

欢迎大家的批评指正!

参考文献
基于FPGA的全同步数字频率计的设计 --包本刚

这篇关于基于FPGA和STM32的频率计设计(100Mhz-1hz精确到0.1hz)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/276630

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

SprinBoot+Vue网络商城海鲜市场的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者,全网30w+

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订