【入门学习三】基于 FPGA 使用 Verilog 实现按键状态机代码及原理讲解

2023-05-16

目录

  • 一、状态机
  • 二、模块设计
  • 三、代码实现
  • 四、管脚配置及结果展示

上一篇博文:【入门学习二】基于 FPGA 使用 Verilog 实现蜂鸣器响动的代码及原理讲解

概述:前面的两篇文章,其中按键模块采用的是延时消抖的方式,本篇文章采用状态机实现按键功能,只需要一个按键模块,即可使用多个按键,当点击一个按键后,流水灯左移,点击另一个按键后,流水灯右移。

一、状态机

基本概念

  • 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。
  • 通俗的话来讲,就是用状态来表示当前信号。
  • 种类:
    1. 若输出只和状态有关而与输入无关,则称为摩尔型(Moore)状态机;
    2. 输出不仅和状态有关而且和输入有关系,则称为米勒型(Mealy)状态机。
  • 编码方式:(采用编码方式描述状态)
    1. 独热码:
      本文的按键状态为 4 个,所以独热码应该为
      初始状态——0001
      下降状态——0010
      保持状态——0100
      上升状态——1000
    2. 自然二进制;
      初始状态——00
      下降状态——01
      保持状态——10
      上升状态——11
    3. 格雷码。
      初始状态——00
      下降状态——01
      保持状态——11
      上升状态——10
  • 描述方式:
    1. 一段式:利用一个进程来描述状态的转换及输出信号的定义;
    2. 二段式:一个为时序电路主要负责状态变量的更新,此进程为同步电路,而另一个进程语句主要是描述下次态变量和输出的更新;
    3. 三段式:第一个进程主要负责状态变量的更新,第二个进程语句负责描述次态变量,而最后一个则是负责输出信号的更新。

本篇文章的状态机描述

  • 对于按键,它的电平为高低变化,有四个状态。
    在这里插入图片描述
  • 本文采用独热码来表示这四个状态,也就是:
    初始状态——0001
    下降状态——0010
    保持状态——0100
    上升状态——1000
  • 也就是当它为保持状态的时候,取电平值,可以获取按键信号。
  • 采用三段式描述,因为三段式的状态机可以易于修改以及维护。
  • 先梳理一下,要获取上升沿、下降沿标志,那么就需要一个打拍器,当收到下降沿标志后,要计数 20 ms 再获取当前电平值,所以还需要一个计数器,然后状态机的第一段描述状态转移,第二段描述状态转移规律,第三段描述输出或功能信号(打拍器及计数器的实现)。

二、模块设计

  • 这里的按键输入 key_in 的位宽为 2 位,也就是两个按键输入,当然也可以 1 个按键或者更多的按键,使用状态机就不需要重复定义每一个按键的输入了。
    在这里插入图片描述
  • 这个模块设计和第一篇文章的类似,只不过输入为两位宽,表示两个按键输入。
  • 这里就不再赘述每个管脚名的含义了。

三、代码实现

顶层模块 fsm_key.v:

  • 这里第一排定义了一个全局变量 KEY_W = 2,表示两个按键;
  • 模块例化中,“#” 后面表示传参。
module fsm_key #(parameter KEY_W = 2)(
	
	input						clk		,
	input 						rst_n	,
	
	input 	[KEY_W-1:0]			key_in	,
	output	[3:0]				led
	
	);
	
	wire		[KEY_W-1:0]		press;
	
	//模块例化
	key_filter #(.KEY_W(KEY_W), .DELAY(1000_000)) u_key_filter(
		.clk		(clk	),
		.rst_n		(rst_n	),
		.key		(key_in	),
		.press		(press	)
	);
	
	led_driver #(.KEY_W(KEY_W), .CNT_MAX(25_000_000)) u_led_driver(
		.clk		(clk	),
		.rst_n		(rst_n	),
		.en			(press	),
		.led		(led	)
	);

endmodule

按键模块 key_filter.v:

  • 使用三段式。
module key_filter #(parameter KEY_W = 1, DELAY = 1000_000)(
	
	input						clk		,
	input 						rst_n	,
	input		[KEY_W-1:0]		key		,
	
	output	reg	[KEY_W-1:0]		press
	);
	
	//参数定义
	localparam 		IDLE = 4'b0001,	//初始状态
					FALL = 4'b0010,	//下降沿状态
					HOLD = 4'b0100,	//保持低电平稳定状态
					RISE = 4'b1000;	//上升状态
					
	//信号定义
	reg		[3 :0]			state_c		;		//现态
	reg		[3 :0]			state_n		;		//次态
	
	reg		[19:0]			cnt_delay	;		//延时计数器
	reg		[KEY_W-1:0]		key_r0		;		//同步器
	reg		[KEY_W-1:0]		key_r1		;		//打拍 检测边沿
	
	wire	[KEY_W-1:0]		nedge		;		//下降沿
	wire	[KEY_W-1:0]		pedge		;		//上升沿
	
	
	wire 			        idle2fall	;
	wire 			        fall2idle	;
	wire 			        fall2hold	;
	wire 			        hold2rise	;
	wire 			        rise2idle	;
	
	//状态机
	//第一段 时序逻辑 描述状态转移
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			state_c <= IDLE;
		end
		else begin
			state_c <= state_n;
		end
	end
	
	//第二段 组合逻辑 描述状态转移规律
	always @(*) begin
		case(state_c)
			IDLE:
				begin
					if(idle2fall)
						state_n = FALL;
					else
						state_n = state_c;
				end
			FALL:
				begin
					if(fall2idle)
						state_n = IDLE;
					else if(fall2hold)
						state_n = HOLD;
					else
						state_n = state_c;
				end
			HOLD:
				begin
					if(hold2rise)
						state_n = RISE;
					else
						state_n = state_c;
				end
			RISE:
				begin
					if(rise2idle)
						state_n = IDLE;
					else
						state_n = state_c;
				end
		endcase
	end
	
	
	assign idle2fall = state_c == IDLE && nedge != 'd0;
	assign fall2idle = state_c == FALL && pedge != 'd0 && cnt_delay < DELAY - 1;
	assign fall2hold = state_c == FALL && cnt_delay == DELAY - 1;
	assign hold2rise = state_c == HOLD && pedge != 'd0;
	assign rise2idle = state_c == RISE && cnt_delay == DELAY - 1;
	
	//第三段 描述输出或者功能信号
	
	//cnt_delay
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			cnt_delay <= 0;
		end
		else if(state_c == FALL || state_c == RISE) begin
			if(cnt_delay == DELAY - 1 || pedge != 0)
				cnt_delay <= 0;
			else
				cnt_delay <= cnt_delay + 1;
		end
	end
	
	//key_r0 同步器 key_r1 打拍
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			key_r0 <= 0;
			key_r1 <= 0;
		end
		else begin
			key_r0 <= key;
			key_r1 <= key_r0;
		end
	end
	
	//nedge = {key_r1[0] == 1'b1 && key_r0[0] == 1'b0, key_r1[1] == 1'b1 && key_r0[1] == 1'b0}
	assign nedge = key_r1 & (~key_r0);//下降沿:先高后低
	assign pedge = key_r0 & (~key_r1);//上升沿:先低后高
	
	//press 输出
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			press <= 0;
		end
		else begin
			press <= (fall2hold == 1'b1) ? ~key_r0 : 'd0;
		end
	end
	
endmodule

代码执行过程:

  • 最开始的时候主要是 state_c 与 state_n 不断地迭代,两个信号只差一个时钟周期,state_c 表示当前按键电平状态,state_n 表示预测 state_c 的下一个周期的电平状态。
    在这里插入图片描述
  • 此时下降沿到来,state_n 由 IDLE 状态变为 FALL 状态,state_c 仍为 IDLE 状态。
    在这里插入图片描述
  • state_n 如何变为 FALL 状态?
  • 首先是打拍器检测到下降沿。
    在这里插入图片描述
  • 然后 IDLE 变为 FALL 的标志位就为 1 电平。
    在这里插入图片描述
  • 这个时候,根据第二段的状态转移规律,state_n = FALL;
    在这里插入图片描述
  • 下一个时钟周期,state_c 的状态就为 state_n 的状态,然后 state_n 再次预测 state_c 的状态,也就是 state_c = FALL,state_n = FALL,这里是根据第一段来的。
  • 与此同时,state_c = FALL 了,计数器就开始工作了。
    在这里插入图片描述
  • 当检测到上升沿时,FALL2IDLE 标志位就为 1 电平,此时根据 case 选择语句,state_n 就变为了 IDLE 状态,此时,state_c 仍为 FALL 状态,那么计数器就会清零。
    在这里插入图片描述
  • 然后不断重复这一过程,直至 state_c 为 FALL 状态并且计数器已经计满了 20ms,这时,state_n 才为 HOLD 状态,如下图所示:
    在这里插入图片描述
  • 此时,fall2hold 标志信号为 1 ,则输出按键信号高电平。
    在这里插入图片描述

LED 模块 led_driver.v:

  • 接收到按键控制信号后,改变 flag 的值,led 输出模式根据 flag 的改变而改变。
module led_driver #(parameter KEY_W = 2, CNT_MAX = 25_000_000)(
	
	input									clk		,
	input 								rst_n		,
	input				[KEY_W-1:0]		en			,		//控制led以不同的方式流水
	
	output	reg	[3:0]				led
	
	);
	
	reg					flag		;
	
	//信号定义
	reg	[24:0]		cnt		;
	
	//cnt
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			cnt <= 0;
		end
		else begin
			if(cnt == CNT_MAX - 1)
				cnt <= 0;
			else
				cnt <= cnt + 1;
		end
	end
		
	
	//flag
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			flag <= 0;
		end
		else if(en[0]) begin
			flag <= 0;
		end
		else if(en[1]) begin
			flag <= 1;
		end
	end
	
	//led
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			led <= 4'b0001;
		end
		else if(cnt == CNT_MAX - 1) begin
			if(flag)
				led <= {led[0], led[3:1]};//右移
			else
				led <= {led[2:0], led[3]};//左移
		end
	end
	
endmodule

四、管脚配置及结果展示

管脚配置

  • 要按照自己开发板的管脚进行配置。
    在这里插入图片描述

结果展示

  • 按 K2 流水灯左移,按 K3 流水灯右移。
    在这里插入图片描述

下一篇博文:【入门学习四】基于 FPGA 使用 Verilog 实现串口回传通信代码及原理讲解

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【入门学习三】基于 FPGA 使用 Verilog 实现按键状态机代码及原理讲解 的相关文章

  • 【教程&工具】微信同步文章到Bear

    在我日常工作中 xff0c 我会将各种互联网以及生活中产出的信息汇总到Bear xff0c 再通过Bear的云同步使我各个终端的信息保持一致 以前在使用有道云笔记的时候 xff0c 有个功能我很喜欢 xff0c 就是当看到一篇想收藏的文章的
  • CentOS7分布式部署pyspider

    1 搭建环境 xff1a 系统版本 xff1a 96 Linux centos linuxpython版本 xff1a span class typ Python span span class lit 3 5 span span clas
  • 各类python包的安装方法及设置安装路径

    python拥有非常丰富的扩展包 xff0c 下面介绍常见的扩展包安装方法 使用Anaconda集成环境 通过使用该python的集成环境 xff0c 可以解决大部分常见包的安装以相互依赖问题 下载地址为 xff1a https www c
  • 如何取出列表内元组中的字典的key和value

    目录标题 一 从列表中取出字典的key和value二 取出列表内元组中的字典的key和value 一 从列表中取出字典的key和value 例 list 1 span class token operator 61 span span cl
  • 树莓派远程桌面连接出现Connection Problem, giving up

    树莓派4B xff0c 使用官方imager刷系统 xff0c 系统为当下最新的2022 01 28 raspios bullseye armhf xff0c 结果发现使用远程桌面登录时 xff0c 在输入用户名和密码后 xff0c 界面无
  • you-get 下载bilibili视频

    bilibili查看视频清晰度 you get i url https www bilibili com video BV17z411i7er p 61 1 debug 下载视频 you get format 61 flv1080 http
  • 解决“AttributeError: ‘str‘ object has no attribute ‘decode‘”问题

    问题描述 在跑代码的时候出现报错提示 Traceback most recent call last File 34 multi detect Nerual py 34 line 4 in lt module gt import BiLST
  • NGINX 进程通信机制

    本文地址 xff1a http blog csdn net spch2008 article details 38945033 nginx的进程通信分为三种类别 xff1a linux 系统与nginx 通信 xff0c master 进程
  • Could not find main class com/intellij/idea/Main

    Could not find main class com intellij idea Main 下载完Pycharm xff0c 打开时显示Could not find main class com intellij idea Main
  • 记事本里打“联通”为什么会变成乱码?

    记事本的编码问题 xff0c 当文档中所有字符都在 C0 AA DF 80 BB BF 这个范围的时候 xff0c notepad都无法确认文档的格式 xff0c 没有自动按照UTF 8格式来 34 Display 34 34 联通 34
  • 思博伦Spirent Testcenter交换机性能测试主要技术指标_丢帧率/吞吐量/转发速率之间的关系_双极未来

    转发速率 丢帧率和吞吐量是描述交换机转发性能的主要技术指标 xff0c 这些指标的测试结果可以客观地反映出被测交换机的性能 正确理解它们之间的联系与区别对于设计吞吐量 丢帧率和转发速率的测试方法非常重要 如下图所示 xff0c X轴表示 x
  • OpenStack-Ceilometer项目功能与架构介绍

    1 OpenStack Ceilometer项目简介 OpenStack通过Telemetry项目提供计量与监控服务 xff0c 该项目旨在针对组成已部署云的物理和虚拟资源 xff0c 可靠地收集并保存各类使用数据 xff0c 以便对这些数
  • redhat 查询端口占用

    linux redhat 端口和服务的查看与终止 redhat 查询端口占用情况和杀死占用的服务 netstat anp grep lt 端口号或者程序名称 gt 查8080端口占用情况 netstat anp grep 8080 查jav
  • mapl

    business card case executive veto power anime stream radio johann strauss black layout pink star job interview question
  • Knowledge Tracing: A Survey阅读笔记

    xff08 注 xff1a 为了方便后续阅读KT论文 xff0c 文中一些名词使用英文 文中保留的序号与原论文参考文献一致 行文会在后续反刍过程中改进 xff09 原文链接 xff1a https arxiv org abs 2201 06
  • iOS_NSAttributedString 的21种属性详细介绍(图文混排)

    说明 NSAttributedString 可以非常方便的实现文字排版和图文混排功能 共有21中效果 API 本文将较详细的介绍21种的属性的使用 注 本博客由 64 凡俊编写 64 Scott 64 春雨 审核 若转载此文章 请注明出处和
  • ++和--的用法

    单独使用时 xff0c 43 43 或者 无论是放在变量的前面还是后面 xff0c 结果是一样的 参与操作时 如果 43 43 或者 在变量的后面 xff0c 先拿变量参与操作 xff0c 后变量做 43 43 或者 例如 int a 61
  • geoserver集群

    软件准备 geoservertomcat 插件 下载地址 xff1a https build geoserver org geoserver activeMQ broker plugin zipjms cluster plugin zip
  • 为moment.js正名

    说来也奇怪 xff0c 总有人在耳边说moment js对国际化日期支持不好 xff0c 坚决不要使用 xff0c 会带来很多问题之类的话 但就我个人经验来看 xff0c 还从未见到过任何一个反例 xff0c 恰好我又是个不信邪的人 xff
  • sqlite3 的二进制数据插入与获取

    sqlite3 存储和查找二进制数据对象 使用c语言接口 思路 xff1a 通过让代码执行执行sql语句进行查找 xff0c 但二进制的显示方法无法确定所以 xff0c 二进制数据对象查询语句略有不同 注意 xff1a sqlite3的数据

随机推荐

  • MySQL修改数据表中的字段名

    MySQL修改数据表中的字段名 在一张数据表中只能设置一个唯一名称的字段名 在同一张数据表中 xff0c 不能出现两个名称完全相同的字段名 因此 xff0c 数据库系统可以通过字段名来区分数据表中的不同字段 在MySQL中 xff0c AL
  • 证明:当gcd(a, b) = 1,则gcd(a + b, a) = 1

    假设 xff1a gcd a b 61 1 证明 xff1a gcd a 43 b b 61 1 反证法 xff1a 假设gcd a 43 b b 61 k 61 1 则 xff1a b 61 k r1 a 43 b 61 a 43 k r
  • 实战microPython(二)时钟和日历的使用

    实战microPython 2 时钟和日历的使用 David Zou 创客DIY乐园 对于一名创客 xff0c 自制一个个性化的时钟或闹钟啥的 xff0c 应该是比较常见的入门级任务了 通常我们制作时钟或闹钟的时候 xff0c 都需要借助专
  • 实战microPython(10)-蓝牙模块的使用

    实战microPython 10 蓝牙模块的使用 David Zou 2018 12 27 本文讲解蓝牙模块的使用 xff0c 以及通过uPyBoard来操作蓝牙模块并实现手机和uPyboard互动 正在学习和使用uPyBoard开发的小伙
  • nohup: failed to run command ‘java’: No such file or directory解决方案

    场景 xff1a Jekins实现自动化部署 问题描述 Jekins打包后端项目后发送Jar到对应的应用服务器 xff0c 通过应用服务器Shell脚本启动服务报错 nohup span class token operator span
  • System.DllNotFoundException: 无法加载DLL

    问题描述 使用VS2005在Windows Server 2003上编译C 43 43 代码 输出dll文件 把该dll放到运行机器 与编译机器的系统完全一致 上 供C 代码 web前台 调用 提示无法加载dll 分析 1 路径完全没有问题
  • Flutter学习(一)

    开始学习 为什么使用 Fultter xff1f 为什么使用 Fultter xff1f 言归正传 xff0c 亘古不变环境搭建 xff1a Android Studio 下载 xff0c 我的是这个版本 xff0c emmmm 首先安装g
  • 树莓派 ubuntu18.04 mate更换为国内镜像源

    文章目录 前言正文参考 前言 我使用树莓派3B 43 xff0c 烧写的操作系统为ubuntu mate18 04 网上的相关教程很多 xff0c 但说的很详细的不多 xff0c 本文算是做一个简单的整理 树莓派采用的是arm架构 xff0
  • Spring常见面试题总结(超详细回答)

    1 Spring是什么 Spring是一个轻量级的IoC和AOP容器框架 是为Java应用程序提供基础性服务的一套框架 xff0c 目的是用于简化企业应用程序的开发 xff0c 它使得开发者只需要关心业务需求 主要包括以下七个模块 xff1
  • 在android studio中使用kotlin

    一 安装kotlin插件 二 导入Kotlin的核心库及其扩展库Anko库 1 在项目根目录下的build gradle文件中指定kotlin插件的版本及路径 buildscript ext kotlin version 61 span c
  • sublime Text SFTP LICENSE 注册码

    34 email 34 34 xiaosong 64 xiaosong me 34 34 product key 34 34 d419f6 de89e9 0aae59 2acea1 07f92a 34 这个就是SFTP注册码 将上面的代码复
  • Aliddns插件使用:小白超详细图文教程

    Aliddns插件使用 xff1a 小白超详细图文教程 Aliddns插件 xff0c 用阿里的云解析速度是快 xff0c 天下武功为快不破 作为一个小白的我 xff0c 看这篇帖子也是一脸懵逼 xff0c http koolshare c
  • 在PyQt5中使用多进程(multiprocessing)

    multiprocessing对象要放在 main 所在的启动文件使用槽连接multiprocessing对象 import sys from multiprocessing import Pool from PyQt5 QtWidgets
  • go使用exec执行命令

    golang exec 命令执行
  • 【汇编】AT89C52点亮一盏LED灯(汇编语言)

    学习利用汇编语言写单片机程序的第一步是要学习汇编语言的相关理论知识 xff0c 那么实践操作的第一步肯定是从点灯开始啦 xff01 编译环境 xff1a keil4 编译语言 xff1a 汇编语言 内容 xff1a 一 keil4建立AT8
  • wsl-常见问题

    基于wsl2的docker如何迁移镜像文件 默认基于wsl2的docker desktop的镜像是有wsl2管理的 xff0c 而wsl2一般在c盘 当下载的镜像多了之后 xff0c 就会把C盘爆满 wsl shutdown wsl exp
  • 求0-7所组成的奇数个数

    include lt stdio h gt include lt stdlib h gt main long sum 61 4 long s 61 4 int j for j 61 2 j lt 61 8 j 43 43 printf 34
  • UITabBarController详解

    一 UITabBarController简介 一 继承关系 UITabBarController和UINavigationController类似 xff0c 也继承于UIViewController xff0c 也可以轻松地管理多个控制器
  • 关于多卡Android设备获取手机号的研究

    首先我们都知道如何获取Android手机的Sim手机号 fun getNativePhoneNumber context Context String val tm 61 context getSystemService Context T
  • 【入门学习三】基于 FPGA 使用 Verilog 实现按键状态机代码及原理讲解

    目录 一 状态机二 模块设计三 代码实现四 管脚配置及结果展示 上一篇博文 xff1a 入门学习二 基于 FPGA 使用 Verilog 实现蜂鸣器响动的代码及原理讲解 概述 xff1a 前面的两篇文章 xff0c 其中按键模块采用的是延时