基于FPGA的密码锁开发——(1)密码锁驱动
发布日期:2021-05-07 22:56:21 浏览次数:16 分类:精选文章

本文共 12983 字,大约阅读时间需要 43 分钟。

最近在搞一个课程设计,题目很简单,用FPGA做一个密码锁。开发过程中遇到了一系列的错误,现在被一一解决。就通过写几篇博文来记录一下开发过程吧

首先在构思框架时目标就很明确。整个系统至少要有这么几个模块:密码锁驱动、键盘驱动、数码管驱动;这是最最最基本的,在完成这几个模块后可以进行功能和外设的扩展。

这一篇就记录密码锁的开发。

首先,构建密码锁框架时,考虑到这么几个需求:

1、密码锁需要与键盘构建接口,则需要有4位的数据接口和一个触发接口
2、密码锁需要与数码管构建接口,这个设计默认密码位数是6位的,所以需要6个4位的数据接口

有这么个思路就很好构建代码框架了

module	lock_dri(	//================System Signal================	input				clk											,	input				rst_n										,	//================Interface====================	output	reg	[3:0]	num0										,	output	reg	[3:0]	num1										,	output	reg	[3:0]	num2										,	output	reg	[3:0]	num3										,	output	reg	[3:0]	num4										,	output	reg	[3:0]	num5										,	input				key_trig									,	input		[3:0]	num											,	output				flag_lock									,);

接下来,分析如下需求:

1、密码是6位10进制数,则输密码部分就需要对矩阵键盘进行6次采样,然后在进入输入模式前还需要一个Enter命令;再加上密码判断,成功解锁和解锁失败这几种状态,就需要设计一个至少10个状态的状态机

下面是状态定义,定义在全局文件里了

`define			S_LOCK						10'b00_0000_0001`define			S_NUM0_INPUT				10'b00_0000_0010`define			S_NUM1_INPUT				10'b00_0000_0100`define			S_NUM2_INPUT				10'b00_0000_1000`define			S_NUM3_INPUT				10'b00_0001_0000`define			S_NUM4_INPUT				10'b00_0010_0000`define			S_NUM5_INPUT				10'b00_0100_0000`define			S_CHECK				   		10'b00_1000_0000`define			S_SUCESS					10'b01_0000_0000`define			S_FAIL						10'b10_0000_0000

接下来,先考虑输密码的行为

这个行为实际上就是键盘的按键行为触发密码锁模块对键盘输出数据的采样并锁存,最终和设定密码进行比较的行为,因此首先需要考虑对键触发的捕获,方法如下:
我们是将键盘和密码锁模块以4位数据线和一个触发脉冲作为接口
也就是这两个信号,再用一个寄存器锁存num

input				key_trig									,	input		[3:0]	num											,

捕获方法很简单

reg 				key_trig_t0									;	reg 				key_trig_t1									;	reg 				key_trig_t2									;	reg 				key_trig_t3									;	wire				trig_key_trig								;	wire				trig_key_trig_t1							;	//key_trig	always	@(posedge clk)begin		key_trig_t0 <= key_trig;		key_trig_t1 <= key_trig_t0;		key_trig_t2 <= key_trig_t1;		key_trig_t3 <= key_trig_t2;	end	assign	trig_key_trig = key_trig_t1&(~key_trig_t2)	;	assign	trig_key_trig_t1 = key_trig_t2&(~key_trig_t3);

将脉冲信号打3拍,然后通过这种写法可以得到触发的上升沿。只要在触发上升沿到来时采样4位数据就可以,而这里为了数据稳定,num信号的采样提前于状态机的状态转移,因此在trig_key_trig高时采样

always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			num_reg <= 'd0;		else if(trig_key_trig==1'b1)			num_reg <= num;	end

接下来就可以考虑写状态机了,我们采用比较规范的三段式状态机,我们首先声明一下几个宏定义,可以少些一些代码

//指令集`define         ENTER						4'ha`define         CLEAR						4'hb`define         LOCK						4'hc`define         BACK						4'hd`define			PASSWORD_SET				4'he
localparam		CMD_ENTER = (trig_key_trig_t1==1'b1&&num_reg==`ENTER);

三段式状态机代码

//第一段	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			cur_state <= `S_LOCK;		else			cur_state <= next_state;	end//第二段	always	@(*)begin		next_state = `S_LOCK;		case(cur_state)			`S_LOCK:begin				if(CMD_ENTER)					next_state = `S_NUM0_INPUT;				else					next_state = `S_LOCK;			end			`S_NUM0_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM1_INPUT;				else					next_state = `S_NUM0_INPUT;			end			`S_NUM1_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM2_INPUT;				else					next_state = `S_NUM1_INPUT;			end			`S_NUM2_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM3_INPUT;				else					next_state = `S_NUM2_INPUT;			end			`S_NUM3_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM4_INPUT;				else					next_state = `S_NUM3_INPUT;			end			`S_NUM4_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM5_INPUT;				else					next_state = `S_NUM4_INPUT;			end			`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_CHECK;				else					next_state = `S_NUM5_INPUT;			end			/*			`S_CHECK:begin			end			`S_SUCCESS:begin							end			`S_FAIL:begin						end			`S_PASSWORD_SET:begin						end			*/			default:next_state = `S_LOCK;		endcase	end//第三段	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)begin				end		case(cur_state)			`S_LOCK:begin			end			`S_NUM0_INPUT:begin			end			`S_NUM1_INPUT:begin			end			`S_NUM2_INPUT:begin			end			`S_NUM3_INPUT:begin			end			`S_NUM4_INPUT:begin			end			`S_NUM5_INPUT:begin			end			/*			`S_CHECK:begin			end			`S_SUCCESS:begin							end			`S_FAIL:begin						end			`S_PASSWORD_SET:begin						end			*/		endcase		default:begin				end	end

这样状态机框架就搭好了

接下来要往状态机里加条件了,首先完善密码判断状态。考虑如下:在密码输入过程中通过移位操作将6位10进制数锁存下来,因此需要定义一个24位的密码寄存器。由于num_reg寄存器在trig_key_trig条件下跳变,因此移位操作也在这个条件进行

//password_reg	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			password_reg <= 'd0;		else case(cur_state)begin			`S_NUM0_INPUT,`S_NUM1_INPUT,`S_NUM2_INPUT,`S_NUM3_INPUT,`S_NUM4_INPUT,`S_NUM5_INPUT:begin				if(trig_key_trig==1'b1)					password_reg <= {   password_reg[19:0],num_reg};			end			`S_LOCK:password_reg <= 'd0;			default:;		end	end

那么CHECK状态下的转移条件就可以写出来了

`S_CHECK:begin				if(password_reg==`PASSWORD)					next_state = `S_SUCCESS				else					next_state = `S_FAIL;			end

现在考虑一下输入过程中的条件转移细节:如果在输入过程中一段时间无操作,状态就应该跳回空闲状态。所以设定在输入密码过程中无操作满10s就回到LOCK·状态。这样就需要定义两个计数器,一个位1s的计时器,一个是计满10的计数器,这样就能解决10s的计时

reg[25:0]	cnt_1s;	reg[3:0]	cnt_10times;	localparam			CNT_1S		= 49_999_999;
//cnt_1s	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			cnt_1s <= 'd0;		else case(cur_state)			`S_NUM0_INPUT,`S_NUM1_INPUT,`S_NUM2_INPUT,`S_NUM3_INPUT,`S_NUM4_INPUT,`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)//有操作即清零					cnt_1s <= 'd0;				else if(cnt_1s==CNT_1S)//计满清零					cnt_1s <= 'd0;				else					cnt_1s <= cnt_1s + 1'b1;			end			default:cnt_1s <= 'd0;		endcase	end	//cnt_10times	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			cnt_10times <= 'd0;		else case(cur_state)			`S_NUM0_INPUT,`S_NUM1_INPUT,`S_NUM2_INPUT,`S_NUM3_INPUT,`S_NUM4_INPUT,`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					cnt_10times <= 'd0;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					cnt_10times <= 'd0;				else if(cnt_1s==CNT_1S)					cnt_10times <= cnt_10times + 1'b1;			end			default:cnt_10times <= 'd0;		endcase	end

这样以后,状态转移继续完善

`S_NUM0_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM1_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM0_INPUT;			end			`S_NUM1_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM2_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM1_INPUT;			end			`S_NUM2_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM3_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM2_INPUT;			end			`S_NUM3_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM4_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM3_INPUT;			end			`S_NUM4_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM5_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM4_INPUT;			end			`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_CHECK;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM5_INPUT;			end

接下来还需处理num0-5这6个与数码管显示模块连线的接口和flag_lock这一锁住标志,处理方法很简单,num0-5在状态机中处理。我们认定num5是最左边的数字

always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)begin			{   num5,num4,num3,num2,num1,num0} <= 24'h000_000;		end		case(cur_state)			`S_LOCK:begin				{   num5,num4,num3,num2,num1,num0} <= 24'h000_000;			end			`S_NUM0_INPUT:begin				if(trig_key_trig_t1==1'b1)					num5 <= num_reg;			end			`S_NUM1_INPUT:begin				if(trig_key_trig_t1==1'b1)					num4 <= num_reg;			end			`S_NUM2_INPUT:begin				if(trig_key_trig_t1==1'b1)					num3 <= num_reg;			end			`S_NUM3_INPUT:begin				if(trig_key_trig_t1==1'b1)					num2 <= num_reg;			end			`S_NUM4_INPUT:begin				if(trig_key_trig_t1==1'b1)					num1 <= num_reg;			end			`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					num0 <= num_reg;			end			`S_CHECK:begin			end			`S_SUCCESS:begin							end			`S_FAIL:begin						end			/*`S_PASSWORD_SET:begin						end*/		endcase		default:begin				end	end

flag_lock就很简单,只要不是成功解锁即SUCCESS状态就都是锁定的,用assign写即可

assign	flag_lock = ~(cur_state==`S_SUCCESS);

这样一来,具有基本功能的密码锁驱动就完成了

全部代码

//==================defines=====================`define SIMmodule	lock_dri2(	//================System Signal================	input				clk			,	input				rst_n		,	//================Interface====================	input		[3:0]	num			,	output	reg	[3:0]	num0		,	output	reg	[3:0]	num1		,	output	reg	[3:0]	num2		,	output	reg	[3:0]	num3		,	output	reg	[3:0]	num4		,	output	reg	[3:0]	num5		,	output				flag_lock	);	//================parameters===================	`ifndef SIM	localparam			CNT_1S		= 49_999_999;		`else	localparam			CNT_1S		= 49;		`endif	localparam			CMD_ENTER 	= (trig_key_trig_t1==1'b1&&num_reg==`ENTER);	//================System regs==================	reg[9:0]			cur_state;	reg[9:0]			next_state;	reg[3:0]			num_reg;	wire				trig_key_trig;	wire				trig_key_trig_t1;	reg[23:0]			password_reg;	reg[25:0]			cnt_1s;	reg[3:0]			cnt_10times;	//================Main Codes===================	//num_reg	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			num_reg <= 'd0;		else if(trig_key_trig==1'b1)			num_reg <= num;	end		//**************state machine one******************	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			cur_state <= `S_LOCK;		else			cur_state <= next_state;	end	//**************state machine two******************	always	@(*)begin		next_state = `S_LOCK;		case(cur_state)			`S_LOCK:begin				if(CMD_ENTER)					next_state = `S_NUM0_INPUT;				else					next_state = `S_LOCK;			end			`S_NUM0_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM1_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM0_INPUT;			end			`S_NUM1_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM2_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM1_INPUT;			end			`S_NUM2_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM3_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM2_INPUT;			end			`S_NUM3_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM4_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM3_INPUT;			end			`S_NUM4_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_NUM5_INPUT;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM4_INPUT;			end			`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					next_state = `S_CHECK;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					next_state = `S_LOCK;				else					next_state = `S_NUM5_INPUT;			end			`S_CHECK:begin				if(password_reg==`PASSWORD)					next_state = `S_SUCCESS				else					next_state = `S_FAIL;			end			/*			`S_SUCCESS:begin							end			`S_FAIL:begin						end			`S_PASSWORD_SET:begin						end			*/			default:next_state = `S_LOCK;		endcase	end	//	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)begin			{   num5,num4,num3,num2,num1,num0} <= 24'h000_000;		end		case(cur_state)			`S_LOCK:begin				{   num5,num4,num3,num2,num1,num0} <= 24'h000_000;			end			`S_NUM0_INPUT:begin				if(trig_key_trig_t1==1'b1)					num5 <= num_reg;			end			`S_NUM1_INPUT:begin				if(trig_key_trig_t1==1'b1)					num4 <= num_reg;			end			`S_NUM2_INPUT:begin				if(trig_key_trig_t1==1'b1)					num3 <= num_reg;			end			`S_NUM3_INPUT:begin				if(trig_key_trig_t1==1'b1)					num2 <= num_reg;			end			`S_NUM4_INPUT:begin				if(trig_key_trig_t1==1'b1)					num1 <= num_reg;			end			`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					num0 <= num_reg;			end			`S_CHECK:begin			end			`S_SUCCESS:begin							end			`S_FAIL:begin						end			/*`S_PASSWORD_SET:begin						end*/		endcase		default:begin				end	end	//password_reg	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			password_reg <= 'd0;		else case(cur_state)begin			`S_NUM0_INPUT,`S_NUM1_INPUT,`S_NUM2_INPUT,`S_NUM3_INPUT,`S_NUM4_INPUT,`S_NUM5_INPUT:begin				if(trig_key_trig==1'b1)					password_reg <= {   password_reg[19:0],num_reg};			end			`S_LOCK:password_reg <= 'd0;			default:;		end	end	//cnt_1s	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			cnt_1s <= 'd0;		else case(cur_state)			`S_NUM0_INPUT,`S_NUM1_INPUT,`S_NUM2_INPUT,`S_NUM3_INPUT,`S_NUM4_INPUT,`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)//有操作即清零					cnt_1s <= 'd0;				else if(cnt_1s==CNT_1S)//计满清零					cnt_1s <= 'd0;				else					cnt_1s <= cnt_1s + 1'b1;			end			default:cnt_1s <= 'd0;		endcase	end	//cnt_10times	always	@(posedge clk or negedge rst_n)begin		if(rst_n==1'b0)			cnt_10times <= 'd0;		else case(cur_state)			`S_NUM0_INPUT,`S_NUM1_INPUT,`S_NUM2_INPUT,`S_NUM3_INPUT,`S_NUM4_INPUT,`S_NUM5_INPUT:begin				if(trig_key_trig_t1==1'b1)					cnt_10times <= 'd0;				else if(cnt_1s==CNT_1S&&cnt_10times==9)					cnt_10times <= 'd0;				else if(cnt_1s==CNT_1S)					cnt_10times <= cnt_10times + 1'b1;			end			default:cnt_10times <= 'd0;		endcase	end	assign	flag_lock = ~(cur_state==`S_SUCCESS);endmodule
上一篇:基于FPGA的密码锁开发——(2)定制化数码管显示模块驱动
下一篇:FPGA的矩阵键盘驱动( 修正版)

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2025年03月28日 03时14分35秒