【FPGA】电梯楼层显示(简易)

前言

        这是作者室友的项目,本来不管作者事儿的,但是后来听到说是室友去网上找人花了80块买了个劣质的,不仅是从CSDN上抄的,而且使用的板子还不符合室友的要求。可叹作者心软啊,顺便给室友做了。

        在代码实现部分会给出设计理念和分析,整体资源可以直接下载压缩包(手机端依然看不到,还是不知道为什么)。

题目需求及分析

需求

基于双向计数器设计一个电梯楼层显示电路

说明:
        设计多层电梯楼层显示电路。电梯每经过一层,“楼层信号”输入一个可逆计数脉冲电梯上升时“上升”为高电平,“下降”为低电平,下降时相反。

要求:
        1、电梯楼层数为2(至少2层)
        2、楼层数需使用7位译码器显示
        3、可逆计数功能需用双向计数器实现 其输入端包含上升和下降操作信号、楼层信号、校正
        4、上升下降状态过程中用小彩灯灯带变化体现

分析

1. sw1作为总开关,置0清零,置1工作

2. sw2作为楼层“上升”/“下降”控制开关:置0时呈下降状态,楼层信号发生后楼层数减一;置1时呈上升状态,楼层信号发生后楼层数加一

3. led流水灯作为上升下降的具象化表示:向上流水表示上升,向下流水表示下降

4. 辅助功能:显然对于按键,需要有按键消抖的功能;显示分数则需要数码管驱动模块;而关于时序电路中必不可少的分频器也是需要的

代码实现

由于CSDN编辑文章工具中没有VDL语言的设置,这里就用C++来显示代码了(纯黑实属难看)。

1. 楼层显示模块

(1) 中控:elevator.v

module elevator(
	  input clk,
	  input rst,					//重置键
	  input floor_signal,		//楼层信号
	  //功能选择端
	  input sw1,					//sw1作为总开关,置0清零,置1工作
	  input sw2,					//sw2作为楼层“上升”/“下降”控制开关:0下1上
	  output [8:0] segment_led_1,segment_led_2,    	  //数码管输出
	  output [7:0] led
    );
	 reg 	[7:0] floor_level;	//内部信号:当前楼层数
	 reg	[7:0] display;			//显示输出  

//电梯显示模块
always@(posedge clk_500hz)begin
	 //sw1=0,此时楼层数清零,显示FF,作为reset态
		if(sw1 == 1'b0)begin
			floor_level[7:0] <= 8'h00; 
			display[7:0] = 8'hff;
		end
	 //sw1,2=10,此时表示下降
		else if(sw1 == 1'b1 && sw2 == 1'b0)begin
			//清零
			if(!rst)begin
				floor_level[7:0] <= 8'h00;
			end
			//楼层下降
			else if(!floor_signal && key_done)begin
				if(floor_level[3:0] > 4'd0)begin
					floor_level[3:0] <= floor_level[3:0]-2'd1;
					floor_level[7:4] <= floor_level[7:4];
				end
				else if(floor_level[3:0] == 4'd0)begin
					floor_level[3:0] <= 4'h9;
					floor_level[7:4] <= floor_level[7:4]-4'h1;
				end
			end
			//将当前楼层数赋值给显示
			display[7:0] = floor_level[7:0];
		end
		//sw1,2=11,此时表示上升
		else if(sw1 == 1'b1 && sw2 == 1'b1)begin
			//清零
			if(!rst)begin
				floor_level[7:0] <= 8'h00;
			end
			//楼层上升
			else if(!floor_signal && key_done)begin
				if(floor_level[3:0] < 4'd9)begin
					floor_level[3:0] <= floor_level[3:0]+2'd1;
					floor_level[7:4] <= floor_level[7:4];
				end
				else if(floor_level[3:0] == 4'd9)begin
					floor_level[3:0] <= 4'h0;
					floor_level[7:4] <= floor_level[7:4]+4'h1;
				end
			end
			//将当前楼层数赋值给显示
			display[7:0] = floor_level[7:0];
		end
end
//例化数码管显示模块
segment
(
	.seg_data_1		(display[7:4]),  		//seg_data input
	.seg_data_2		(display[3:0]),  		//seg_data input
	.seg_led_1		(segment_led_1),  	//MSB~LSB = SEG,DP,G,F,E,D,C,B,A
	.seg_led_2		(segment_led_2)   	//MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);

//例化消抖module 
wire key_done; //有按键按下
debounce  //消抖模块
(
  .clk       (clk),
  .rst_n     (rst),
  .key_in    (floor_signal),
  .clk_500hz (clk_500hz),
  .key_done  (key_done)
);

//例化流水灯
flashled u1 (                                   
	.clk	(clk),  
	.rst	(rst),
	.led	(led),
	.sw	(sw2)
);

 
endmodule

(2) 数码管驱动:segment.v

  module segment
   (
    input  wire [3:0] seg_data_1,  //四位输入数据信号
    input  wire [3:0] seg_data_2,  //四位输入数据信号
    output wire [8:0] seg_led_1,  //数码管1,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
    output wire [8:0] seg_led_2   //数码管2,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
   );
   reg[8:0] seg [15:0];           //存储7段数码管译码数据
   initial 
	begin
		seg[0] = 9'h3f;   //  0
		seg[1] = 9'h06;   //  1
		seg[2] = 9'h5b;   //  2
		seg[3] = 9'h4f;   //  3
		seg[4] = 9'h66;   //  4
		seg[5] = 9'h6d;   //  5
		seg[6] = 9'h7d;   //  6
		seg[7] = 9'h07;   //  7
		seg[8] = 9'h7f;   //  8
		seg[9] = 9'h6f;   //  9
		seg[10]= 9'h77;   //  A
		seg[11]= 9'h7C;   //  b
		seg[12]= 9'h39;   //  C
		seg[13]= 9'h5e;   //  d
		seg[14]= 9'h79;   //  E
		seg[15]= 9'h71;   //  F
	end
   assign seg_led_1 = seg[seg_data_1];
   assign seg_led_2 = seg[seg_data_2];
  endmodule

(3) 按键消抖:debounce.v

module debounce
(
  input      clk   ,			//时钟
  input      rst_n ,			//复位键
  input      key_in,			//对应楼层信号
  output reg clk_500hz,		//分频出的500Hz时钟脉冲信号(该板使用的是12M晶振)
  output     key_done		//按键按下动作完成标志
);
reg [25:0]div_cnt;			//分频计数器

always@(posedge clk or negedge rst_n)begin	//获得500Hz时钟脉冲信号
		if(!rst_n)begin
			div_cnt <= 0;
		   clk_500hz <= 0;
	    end
      else if(div_cnt == 1999)begin			//计数两千次反转状态
	      div_cnt <= 0;
		   clk_500hz <= ~clk_500hz;
	    end
		else begin
	      div_cnt <= div_cnt + 1'b1;
		   clk_500hz <= clk_500hz;
	    end
 end


reg qout;
reg key_tmp1,key_tmp2;
parameter n = 10;
reg [25:0] cnt;

always@(posedge clk_500hz or negedge rst_n) begin
		if(!rst_n) begin
			cnt <= 0;
			qout <= 0;
		 end	  
		else if(key_in==0) begin	//按键按下
			if(cnt == n-1) begin		//持续2ms的话判定按下
			  cnt <= cnt;
			  qout <= 1;
		    end
			else begin
				cnt <= cnt+1;
				qout <= 0;
		    end
		 end
		else begin
			qout <= 0;
			cnt <= 0;
		 end
 end	


//提取前后按键信号
always@(posedge clk_500hz or negedge rst_n) begin
	if(!rst_n) begin
		key_tmp1 <= 0;
		key_tmp2 <= 0;
	 end
	else begin
		key_tmp1 <= qout;
		key_tmp2 <= key_tmp1;
	 end	  
 end
	
assign key_done = key_tmp1 & (~ key_tmp2);	
		
endmodule 

2. 流水灯模块

(1) 流水灯:flashled.v

module flashled (clk,rst,led,sw);
 
	input clk,rst;	
	input sw;	//控制正反转
	output [7:0] led;				
 
 
        reg   [2:0] cnt ;                               //定义了一个3位的计数器,输出可以作为3-8译码器的输入
 
        wire clk1h;                                     //定义一个中间变量,表示分频得到的时钟,用作计数器的触发        
 
        //例化module decode38,相当于调用
        decode38 u1 (                                   
			.sw(cnt),                       //例化的输入端口连接到cnt,输出端口连接到led  
			.led(led)
			);
 
        //例化分频器模块,产生一个1Hz时钟信号		
        divide #(.WIDTH(32),.N(12000000)) u2 (         //传递参数
			.clk(clk),
			.rst_n(rst),                   //例化的端口信号都连接到定义好的信号
			.clkout(clk1h)
			);                             
 
        //1Hz时钟上升沿触发计数器,循环计数		
        always @(posedge clk1h or negedge rst)
	     if (!rst)
				cnt <= 0;
	     else
				if(sw == 1'b0)
					cnt <= cnt +1;	//向下流水,模拟下降
				else
					cnt <= cnt -1;	//向上流水,模拟上升
 
        endmodule
 

(2) 分频器:divide.v

 module divide (clk,rst_n,clkout);
 
        input 	clk,rst_n;                       //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
        output	clkout;                          //输出信号,可以连接到LED观察分频的时钟
 
        //parameter是verilog里常数语句
	parameter	WIDTH	= 3;             //计数器的位数,计数的最大值为 2**WIDTH-1
	parameter	N	= 5;             //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
 
	reg 	[WIDTH-1:0]	cnt_p,cnt_n;     //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
	reg			clk_p,clk_n;     //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟
 
	//上升沿触发时计数器的控制
	always @ (posedge clk or negedge rst_n )         //posedge和negedge是verilog表示信号上升沿和下降沿
                                                         //当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
		begin
			if(!rst_n)
				cnt_p<=0;
			else if (cnt_p==(N-1))
				cnt_p<=0;
			else cnt_p<=cnt_p+1;             //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
		end
 
         //上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
         always @ (posedge clk or negedge rst_n)
		begin
			if(!rst_n)
				clk_p<=0;
			else if (cnt_p<(N>>1))          //N>>1表示右移一位,相当于除以2去掉余数
				clk_p<=0;
			else 
				clk_p<=1;               //得到的分频时钟正周期比负周期多一个clk时钟
		end
 
        //下降沿触发时计数器的控制        	
	always @ (negedge clk or negedge rst_n)
		begin
			if(!rst_n)
				cnt_n<=0;
			else if (cnt_n==(N-1))
				cnt_n<=0;
			else cnt_n<=cnt_n+1;
		end
 
        //下降沿触发的分频时钟输出,和clk_p相差半个时钟
	always @ (negedge clk)
		begin
			if(!rst_n)
				clk_n<=0;
			else if (cnt_n<(N>>1))  
				clk_n<=0;
			else 
				clk_n<=1;                //得到的分频时钟正周期比负周期多一个clk时钟
		end
 
        assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      //条件判断表达式
                                                                    //当N=1时,直接输出clk
                                                                    //当N为偶数也就是N的最低位为0,N(0)=0,输出clk_p
                                                                    //当N为奇数也就是N最低位为1,N(0)=1,输出clk_p&clk_n。正周期多所以是相与
endmodule     

(3) 38译码器:decode38.v

module decode38 (sw,led);
 
	input  [2:0] sw;							//开关输入信号,利用了其中3个开关作为3-8译码器的输入
	output [7:0] led;						//输出信号控制特定LED
 
	reg [7:0] led;                                                  //定义led为reg型变量,在always过程块中只能对reg型变量赋值
 
        //always过程块,括号中sw为敏感变量,当sw变化一次执行一次always中所有语句,否则保持不变
	always @ (sw)
	begin
		case(sw)                                                   //case语句,一定要跟default语句
			3'b000:	led=8'b0111_1111;                         //条件跳转,其中“_”下划线只是为了阅读方便,无实际意义  
			3'b001:	led=8'b1011_1111;                         //位宽'进制+数值是Verilog里常数的表达方法,进制可以是b、o、d、h(二、八、十、十六进制)
			3'b010:	led=8'b1101_1111;
			3'b011:	led=8'b1110_1111;
			3'b100:	led=8'b1111_0111;
			3'b101:	led=8'b1111_1011;
			3'b110:  led=8'b1111_1101;
			3'b111:	led=8'b1111_1110;
			default: ;
		endcase
	end
 
endmodule

3. 管脚配置 

        作者使用的板子是“10M02SCM153C8G”核心板,用其他板子的话就换对应的管脚就行。其中sw键是拨码开关,floor_signal和rst键是按键,led[i]是led灯,clk是时钟,segment是数码管。

 4. 可优化

        显然,这是一个很简陋的显示系统。按照最终设想,设计出来的一个项目应该包含完整的电梯操作,包含内外两面显示:1.对于外显示,应该可以预设人所在楼层,让电梯显示逐层变化到指定楼层;2.对于内显示,应该可以设置想去的任何楼层,确定后让电梯显示逐层变化到指定楼层。这个功能其实也不难,添加一个变量用于存储即可,在加个时钟用于自动变化。但是作者手上的板子不具有输入功能(很麻烦,需要一下一下按),加之只是心血来潮帮室友做个期末大作业,所以只完成了基本要求。可能哪一天有兴趣了再来完善。

        可以参考这个:使用Verilog实现FPGA双列电梯控制系统-阿里云开发者社区

后记

        相比于第一次设计篮球比赛计分器,这次设计就要熟练的多,整体设计过程大约1h。之所以这一次开发能这么快,得益于分模块开发的思想。稍加注意即可发现,这一项目中的数码管驱动,按键消抖,分频器和38译码器都是现成的。其中流水灯模块稍加修改了一下,添加的正反装的功能。

        所以说啊,电子这东西,入门难,但是入门之后很多东西都是相通的,稍加举一反三便可达到目的。最后提一嘴题外话,良好的开发习惯真的很造福开发过程,再接再厉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/253991.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【学习笔记】V8垃圾回收策略

V8 V8是一款主流的JavaScript执行引擎V8采用即时编译,速度比较快V8内存设限,64位操作系统中上限为1.5G,32位系统中不超过800M V8垃圾回收策略 采用分代回收的思想内存分为新生代\老生代针对不同对象采用不同算法 v8常用的GC算法: 分代回收、空间复制、标记清除、标记整理、…

RDD编程

目录 一、RDD编程基础 &#xff08;一&#xff09;RDD创建 &#xff08;二&#xff09;RDD操作 1、转换操作 2、行动操作 3、惰性机制 &#xff08;三&#xff09;持久化 &#xff08;四&#xff09;分区 &#xff08;五&#xff09;一个综合实例 二、键值对RDD &am…

系列九、事务

一、事务 1.1、概述 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即&#xff1a;这些操作要么同时成功&#xff0c;要么同时失败。 例如: 张三给李四转账1000块钱&…

C语言短路操作

C语言短路操作 目录 一&#xff0e; 概述二&#xff0e; 例题 一&#xff0e; 概述 C语言中常用的短路操作符有两个&#xff0c;即逻辑与&#xff08;&&&#xff09;和逻辑或&#xff08;||&#xff09;。   对于逻辑与&#xff08;&&&#xff09;操作符&…

TCP/IP详解——FTP 协议,Telnet协议

文章目录 1. FTP 协议1.1 FTP的应用1.2 FTP传输文件的过程1.3 FTP传输模式1.4 主动模式&#xff08;Active Mode&#xff09;1.5 Active Mode 抓包分析1.6 被动模式&#xff08;Passive Mode&#xff09;1.7 Passive Mode 抓包分析 2. Telnet 协议2.1 Telnet 概念2.2 Telnet 协…

Golang清晰代码指南

发挥易读和易维护软件的好处 - 第一部分 嗨&#xff0c;开发者们&#xff0c;清晰的代码是指编写易于阅读、理解和维护的软件代码。它是遵循一组原则和实践&#xff0c;优先考虑清晰性、简单性和一致性的代码。清晰的代码旨在使代码库更易管理&#xff0c;减少引入错误的可能性…

python读取excel数据 附实战代码

在Python中&#xff0c;可以使用pandas库来读取Excel文件中的数据。下面是一个简单的例子&#xff1a; import pandas as pd# 读取Excel文件 df pd.read_excel(example.xlsx)# 显示前5行数据 print(df.head())在上面的代码中&#xff0c;我们首先导入了pandas库&#xff0c;并…

C/C++常见面试知识总结(三)

C语言是一种通用计算机&#xff08;高级&#xff09;编程语言&#xff1b;面向过程&#xff1b;广泛应用于计算机系统设计以及应用程序编写&#xff1b;设计目标&#xff0c;是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行…

springcloud微服务篇--5.Nacos注册中心

目录 一、认识Nacos 二、集成nacos 1.1添加依赖 1.2 注释掉order-service和user-service中原有的eureka依赖。&#xff08;避免冲突&#xff09; 1.3 注释eureka之后&#xff0c;添加nacos的客户端依赖&#xff1a; 三、服务注册到nacos 1、修改配置文件 2、启动并测试 四…

【网络安全】网络防护之旅 - Java安全机制探秘与数字证书引爆网络防线

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《网络安全之道 | 数字征程》⏰墨香寄清辞&#xff1a;千里传信如电光&#xff0c;密码奥妙似仙方。 挑战黑暗剑拔弩张&#xff0c;网络战场誓守长。 目录 &#x1f608;1. 初识网络安…

RTOS中任务的创建与删除

我们在stm32f103c8t6单片机上验证RTOS中任务的创建与删除&#xff0c;利用stm32cube进行RTOS的配置。在选择TIM2当做RTOS的时钟&#xff0c;裸机的时钟源默认是 SysTick&#xff0c;但是开启 FreeRTOS 后&#xff0c;FreeRTOS会占用 SysTick &#xff08;用来生成1ms 定时&…

camera曝光时间

曝光和传感器读数 相机上的图像采集过程由两个不同的部分组成。第一部分是曝光。曝光完成后&#xff0c;第二步就是从传感器的寄存器中读取数据并传输&#xff08;readout&#xff09;。 曝光&#xff1a;曝光是图像传感器进行感光的一个过程&#xff0c;相机曝光时间&#xf…

JWT令牌的作用和生成

JWT令牌&#xff08;JSON Web Token&#xff09;是一种用于身份验证和授权的安全令牌。它由三部分组成&#xff1a;头部、载荷和签名。 JWT令牌的作用如下&#xff1a; 身份验证&#xff1a;JWT令牌可以验证用户身份。当用户登录后&#xff0c;服务器会生成一个JWT令牌并返回…

Python实验项目9 :网络爬虫与自动化

实验 1&#xff1a;爬取网页中的数据。 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 # 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 import urllib.r…

当心字符串连接的性能

在Java中&#xff0c;字符串连接的性能问题同样需要注意&#xff0c;尤其是在循环中进行大量连接操作时。Java中的字符串是不可变的&#xff0c;因此每次连接字符串都会产生一个新的字符串对象&#xff0c;可能导致性能下降。以下是一些示例&#xff0c;演示了不同方法的字符串…

YOLOv5改进 | SPPF篇 | FocalModulation替换SPPF(精度更高的空间金字塔池化)

一、本文介绍 本文给大家带来的改进是用FocalModulation技术来替换了原有的SPPF&#xff08;快速空间金字塔池化&#xff09;模块。FocalModulation是今年新提出的特征增强方法&#xff0c;它利用注意力机制来聚焦于图像中的关键区域&#xff0c;从而提高模型对这些区域的识别…

什么是缓存击穿、缓存穿透、缓存雪崩?

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

在计算机专业中,应该把学习的重点放在哪里?

在计算机专业中&#xff0c;应该把学习的重点放在哪里&#xff1f;这是许多计算机专业学生常常面临的关键问题。随着计算机科技的快速发展&#xff0c;学生们往往陷入了各种各样的选择和困惑中。是将时间投入到深奥的数学领域&#xff0c;还是专注于编程技能的磨练&#xff1f;…

0x17 二叉堆

0x17 二叉堆 二叉堆是一种支持插入、删除、查询最值的数据结构。它其实是一种满足“堆性质”的完全二叉树&#xff0c;树上的每一个节点带有一个权值。若树中的任意一个节点的权值都小于等于其父节点的权值&#xff0c;则称该二叉树满足“大根堆性质”&#xff0c;称其为“大根…

微信小程序:布局样式

效果 wxml <view class"layout"><view class"left"><view>1</view><view>1</view><view>1</view><view>1</view><view>1</view></view><view class"right"&…