基于FPGA的密码锁开发——(1)密码锁驱动

最近在搞一个课程设计,题目很简单,用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 SIM
module	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基础)