–作者:肖肖肖
本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
随着生活质量的不断提高,加强家庭防盗安全变得非常重要,但传统机械锁的构造过于简单,很容易被打开,从而降低了安全性。数字密码锁因为它的保密性很高,安全系数也非常高,再加上其不需要携带避免了丢失的可能,省去了因钥匙丢失而需要换锁的麻烦,受到了越来越多的人的欢迎。随看人们对高科技产品也越来越推崇,在当今社会科技的高度集中和创新,人们对日常生活中保护自身及财产安全的物品非常追捧,对其安全性的要求也非常的高。为了达到人们对锁具安全性的高要求,加强锁具的安全保密性,用密码锁来取代传统机械锁的锁具是必然趋势。数字密码锁比传统机械锁具更加的安全 。在本案例的设计过程中,应用了至简设计法、状态机模板应用等,在经过逐步改进、调试等一系列工作之后,最终达到了设计目标。
基于明德扬至简设计法和明德扬设计规范,设计一个基于FPGA的密码锁、并将数值显示在数码管上,然后根据输入的键值判断密码是否正确。
1.1.2 设计目标
实现电子密码锁的功能,具体功能要求如下:
1.密码4位,初始密码2345。
2.密码锁状态:LOCKED和OPEN,初始状态为LOCKED。
1)当在LOCKED状态时,连续两次输入正确密码,状态变为OPEN状态。当输入错误密码时(包括第一次就输入错误;或者第一次输入正确,第二次输入错误的情况),数码管显示ERROR 2秒后重新显示原来的状态(LOCKED)。
2)当在OPEN状态时,一次输入错误密码,状态变为LOCKED状态。当输入正确密码时,数码管无显示, 10秒后重新显示原来的状态(OPEN)。
3)不管在何状态,当输入4位密码或者某几位密码,但未按下确认键,并超过10S时,返回原来的状态。(即输入密码超时,返回原状态)
对于点拨开发板,使用矩阵按键输入(本文以点拨603开发板为例)。
对于Mp801开发板,密码显示及确认:无论在OPEN,还是LOCKED状态下,均可以通过拨码开关输入密码。当有拨码开关拨动时,数码管当前显示的OPEN或LOCKED消失,并显示当前输入的密码,暂未输入的密码位不显示。4位密码输入完毕后,再拨动拨码开关时视为无效输入,当前显示的密码不改变。4位密码输入完毕后,按下确认键后,系统判断密码是否正确。
拨码开关及按键:初始状态下,拨码开关全部往下拨。当拨码开关向上拨后,再向下拨(回到初始状态),表示一个数字的有效输入。按键每按下一次(会自动弹起),为一次有效输入(复位/确认)。
1.1.3 系统结构框图
系统结构框图如下图一所示:
图一
1.1.4 模块功能
按键检测模块实现功能
1、检测按键的数值
控制模块实现功能
1、对接收到的按键数值进行判断和控制对应的密码锁状态,实现对输入密码的正误判断和对密码锁的开启和闭合控制。
数码管显示模块实现功能
1、显示输入的密码数值;
2、显示当前密码锁的状态(开启状态或者闭锁状态);
3、提示密码输入错误的状态。
1.1.5 顶层信号
信号名 | I/O | 位宽 | 定义 |
---|---|---|---|
clk | I | 1 | 系统工作时钟 50M |
rst_n | I | 1 | 系统复位信号,低电平有效 |
key_col | I | 4 | 矩阵键盘列信号 |
key_row | O | 4 | 矩阵键盘行信号 |
seg_sel | O | 6 | 6位数码管位选信号 |
segment | O | 8 | 8位数码管段选信号 |
1.1.6 参考代码
下面是使用工程的顶层代码:
1.module top_mdyPwdlock_keyscan(
2. clk ,
3. rst_n ,
4.
5. key_col ,
6. key_row ,
7.
8. seg_sel ,
9. segment
10.
11. );
12.
13. input clk ;
14. input rst_n ;
15. input [3:0] key_col ;
16.
17. output[5:0] seg_sel ;
18. output[7:0] segment ;
19. output[3:0] key_row ;
20.
21. wire [5:0] seg_sel ;
22. wire [7:0] segment ;
23. wire [3:0] key_row ;
24.
25. wire [3:0] key_out ;
26. wire key_vld ;
27. wire [6*5-1:0] seg_dout ;
28. wire [5:0] seg_dout_vld ;
29.
30.
31. key_scan u_key_scan(
32. .clk (clk ),
33. .rst_n (rst_n ),
34. .key_col (key_col ),
35. .key_row (key_row ),
36. .key_out (key_out ),
37. .key_vld (key_vld )
38. );
39.
40.
41. control u_ctrl(
42. .clk (clk ),
43. .rst_n (rst_n ),
44.
45. .key_num (key_out ),
46. .key_vld (key_vld ),
47.
48. .seg_dout (seg_dout ),
49. .seg_dout_vld (seg_dout_vld )
50. );
51.
52.
53. seg_display u_segment(
54. .clk (clk ),
55. .rst_n (rst_n ),
56.
57. .din (seg_dout ),
58. .din_vld (seg_dout_vld ),
59.
60. .segment (segment ),
61. .seg_sel (seg_sel )
62. );
63.
64.
65. endmodule
1.2 按键检测模块设计
1.2.1 接口信号
信号名 | I/O | 位宽 | 定义 |
---|---|---|---|
clk | I | 1 | 系统工作时钟 50M |
rst_n | I | 1 | 系统复位信号,低电平有效 |
key_col | I | 4 | 矩阵按键列信号 |
key_row | O | 4 | 矩阵按键行信号 |
key_out | O | 4 | 输出的按键有效数值 |
key_vld | O | 1 | 按键有效指示信号 |
1.2.2 设计思路
在前面的案例中已经有矩阵按键检测模块的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=310&highlight=%BE%D8%D5%F3
其中,按键的功能面板如下图所示:
1.2.3 参考代码
1.module key_scan(
2. clk ,
3. rst_n ,
4. key_col,
5. key_row,
6. key_out,
7. key_vld
8. );
9.
10. parameter KEY_W = 4 ;
11. parameter CHK_COL = 0 ;
12. parameter CHK_ROW = 1 ;
13. parameter DELAY = 2 ;
14. parameter WAIT_END = 3 ;
15. parameter COL_CNT = 16;
16. parameter TIME_20MS= 1000000;
17.
18. input clk ;
19. input rst_n ;
20. input [3:0] key_col;
21.
22. output key_vld;
23. output[3:0] key_out;
24. output[KEY_W-1:0] key_row;
25.
26. reg [3:0] key_out;
27. reg [KEY_W-1:0] key_row;
28. reg key_vld;
29.
30. reg [3:0] key_col_ff0;
31. reg [3:0] key_col_ff1;
32. reg [1:0] key_col_get;
33. wire shake_flag ;
34. reg shake_flag_ff0;
35. reg[3:0] state_c;
36. reg [19:0] shake_cnt;
37. reg[3:0] state_n;
38. reg [1:0] row_index;
39. reg[15:0] row_cnt;
40. wire chk_col2chk_row ;
41. wire chk_row2delay ;
42. wire delay2wait_end ;
43. wire wait_end2chk_col;
44.
45.
46.always @(posedge clk or negedge rst_n)begin
47. if(rst_n==1'b0)begin
48. key_col_ff0 <= 4'b1111;
49. key_col_ff1 <= 4'b1111;
50. end
51. else begin
52. key_col_ff0 <= key_col ;
53. key_col_ff1 <= key_col_ff0;
54. end
55.end
56.
57.
58.wire add_shake_cnt ;
59.always @(posedge clk or negedge rst_n) begin
60. if (rst_n==0) begin
61. shake_cnt <= 0;
62. end
63. else if(add_shake_cnt) begin
64. if(shake_flag)
65. shake_cnt <= 0;
66. else
67. shake_cnt <= shake_cnt+1 ;
68. end
69.end
70.assign add_shake_cnt = key_col_ff1!=4'hf;
71.assign shake_flag = add_shake_cnt && shake_cnt == TIME_20MS-1 ;
72.
73.
74.always @(posedge clk or negedge rst_n)begin
75. if(rst_n==1'b0)begin
76. state_c <= CHK_COL;
77. end
78. else begin
79. state_c <= state_n;
80. end
81.end
82.
83.always @(*)begin
84. case(state_c)
85. CHK_COL: begin
86. if(shake_flag && shake_flag_ff0==1'b0)begin
87. state_n = CHK_ROW;
88. end
89. else begin
90. state_n = CHK_COL;
91. end
92. end
93. CHK_ROW: begin
94. if(row_index==3 && row_cnt==0)begin
95. state_n = DELAY;
96. end
97. else begin
98. state_n = CHK_ROW;
99. end
100. end
101. DELAY : begin
102. if(row_cnt==0)begin
103. state_n = WAIT_END;
104. end
105. else begin
106. state_n = DELAY;
107. end
108. end
109. WAIT_END: begin
110. if(key_col_ff1==4'hf)begin
111. state_n = CHK_COL;
112. end
113. else begin
114. state_n = WAIT_END;
115. end
116. end
117. default: state_n = CHK_COL;
118. endcase
119.end
120.
121.assign chk_col2chk_row = shake_flag && shake_flag_ff0 ==1'b0;
122.assign chk_row2delay = row_index==3 && row_cnt==0;
123.assign delay2wait_end = row_cnt==0;
124.assign wait_end2chk_col= key_col_ff1==4'hf;
125.
126.always @(posedge clk or negedge rst_n)begin
127. if(rst_n==1'b0)begin
128. key_row <= 4'b0;
129. end
130. else if(state_c==CHK_ROW)begin
131. key_row <= ~(1'b1 << row_index);
132. end
133. else begin
134. key_row <= 4'b0;
135. end
136.end
137.
138.always @(posedge clk or negedge rst_n)begin
139. if(rst_n==1'b0)begin
140. row_index <= 0;
141. end
142. else if(state_c==CHK_ROW)begin
143. if(row_cnt==0)begin
144. if(row_index==3)
145. row_index <= 0;
146. else
147. row_index <= row_index + 1;
148. end
149. end
150. else begin
151. row_index <= 0;
152. end
153.end
154.
155.
156.wire add_row_cnt ;
157.wire end_row_cnt ;
158.always @(posedge clk or negedge rst_n) begin
159. if (rst_n==0) begin
160. row_cnt <= COL_CNT;
161. end
162. else if(add_row_cnt) begin
163. if(end_row_cnt)
164. row_cnt <= COL_CNT;
165. else
166. row_cnt <= row_cnt-1 ;
167. end
168. else begin
169. row_cnt <= COL_CNT;
170. end
171.end
172.assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
173.assign end_row_cnt = add_row_cnt && row_cnt == 0 ;
174.
175.
176.
177.always @(posedge clk or negedge rst_n)begin
178. if(rst_n==1'b0)begin
179. shake_flag_ff0 <= 1'b0;
180. end
181. else begin
182. shake_flag_ff0 <= shake_flag;
183. end
184.end
185.
186.always @(posedge clk or negedge rst_n)begin
187. if(rst_n==1'b0)begin
188. key_col_get <= 0;
189. end
190. else if(state_c==CHK_COL && shake_flag==1'b1 && shake_flag_ff0==1'b0) begin
191. if(key_col_ff1==4'b1110)
192. key_col_get <= 0;
193. else if(key_col_ff1==4'b1101)
194. key_col_get <= 1;
195. else if(key_col_ff1==4'b1011)
196. key_col_get <= 2;
197. else
198. key_col_get <= 3;
199. end
200.end
201.
202.
203.always @(posedge clk or negedge rst_n)begin
204. if(rst_n==1'b0)begin
205. key_out <= 0;
206. end
207. else if(state_c==CHK_ROW && row_cnt==0)begin
208. key_out <= {row_index,key_col_get};
209. end
210. else begin
211. key_out <= 0;
212. end
213.end
214.
215.always @(posedge clk or negedge rst_n)begin
216. if(rst_n==1'b0)begin
217. key_vld <= 1'b0;
218. end
219. else if(state_c==CHK_ROW && row_cnt==0 && key_col_ff1[key_col_get]==1'b0)begin
220. key_vld <= 1'b1;
221. end
222. else begin
223. key_vld <= 1'b0;
224. end
225.end
226.
227.endmodule
1.3 控制模块设计
1.3.1 接口信号
信号名 | I/O | 位宽 | 定义 |
---|---|---|---|
clk | I | 1 | 系统工作时钟 50M |
rst_n | I | 1 | 系统复位信号,低电平有效 |
key_num | I | 4 | 输入的按键号 |
key_vld | I | 1 | 按键有效指示信号 |
seg_dout | O | 30 | 30bit的数码管显示数据,每5bit为一个字符(对应一个数码管),一共表示6个数码管的显示数据。 |
seg_dout_vld | O | 6 | 数码管显示数据有效指示信号,seg_dout_vld [0]为1时,seg_dout[4:0]有效;seg_dout_vld [1]为1时,seg_dout[9:5]有效,以此为推。 |
1.3.2 设计思路
状态机架构
本模块的主要功能是根据输入的按键信息进行不同状态的判断和切换当前工作状态。根据项目功能要求,一共有四种工作状态:密码锁开启状态(open)、密码锁闭合状态(clocked)、输入密码状态(password)和提示输入错误状态(error)。
以下为本模块的状态跳转图:
复位后,状态机进入LOCKED的状态,即初始状态为LOCKED;
在LOCKED状态下:
A.有按键按下,跳到PASSWORD状态;
B.否则,保持LOCKED状态不变;
在OPEN状态下:
A.有按键按下,跳到PASSWORD状态;
B.否则,保持OPEN状态不变;
在PASSWORD状态下:
A.有密码输入但超过10秒没有确认,跳到原来的LOCKED状态或者OPEN状态;
B.密码正确输入并确认两次,跳到OPEN状态;
C.密码错误输入并确认,跳到ERROR状态;
D.否则,保持PASSWORD状态不变;
在ERROR状态下:
A.提示输入错误2秒,跳到LOCKED状态;
B.否则,保持ERROR状态不变;
无论当前处于什么状态,只要不满足状态间的跳转条件就跳到LOCKED状态。
计数器架构
本模块的某些状态跳转之间存在一定的时间间隔,根据项目功能要求,一共有两种时间的间隔:10秒的等待输入时间间隔和2秒的显示提示时间间隔。
以下为计数器的架构示意图:
10秒计数器cnt_10s_nvld:用于计算10秒的时间。加一条件为state_c == PASSWORD,表示进入密码输入状态就开始计数。结束条件为数500_000_000个,系统时钟为50M,一个时钟周期为20ns,500_000_000个时钟周期就是10秒。
2秒计数器cnt_2s:用于计算2秒的时间。加一条件为state_c==ERROR,表示进入提示输入错误状态就开始计数。结束条件为数100_000_000个,系统时钟为50M,一个时钟周期为20ns,100_000_000个时钟周期就是2秒。
1.3.3 参考代码
1.module control(
2. clk ,
3. rst_n ,
4.
5. key_num ,
6. key_vld ,
7.
8. seg_dout ,
9. seg_dout_vld
10.
11. );
12.
13. parameter PASSWORD_INI = 16'h2345 ;
14. parameter CHAR_O = 5'h10 ;
15. parameter CHAR_P = 5'h11 ;
16. parameter CHAR_E = 5'h12 ;
17. parameter CHAR_N = 5'h13 ;
18. parameter CHAR_L = 5'h14 ;
19. parameter CHAR_C = 5'h15 ;
20. parameter CHAR_K = 5'h16 ;
21. parameter CHAR_D = 5'h17 ;
22. parameter CHAR_R = 5'h18 ;
23. parameter NONE_DIS = 5'h1F ;
24.
25. parameter C_10S_WID = 29 ;
26. parameter C_10S_NUM = 500_000_000 ;
27. parameter C_2S_WID = 27 ;
28. parameter C_2S_NUM = 100_000_000 ;
29. parameter C_PWD_WID = 3 ;
30.
31. input clk ;
32. input rst_n ;
33. input [3:0] key_num ;
34. input key_vld ;
35.
36. output[6*5-1:0] seg_dout ;
37. output[5:0] seg_dout_vld ;
38.
39. reg [6*5-1:0] seg_dout ;
40. wire [5:0] seg_dout_vld ;
41.
42. reg [1:0] state_c /* synthesis preserve */ ;
43. reg [1:0] state_n ;
44. reg lock_stata_flag ;
45. reg password_correct_twice ;
46.
47. reg [C_2S_WID-1:0] cnt_2s ;
48. reg [C_10S_WID-1:0] cnt_10s_nvld ;
49. reg [C_PWD_WID-1:0] cnt_password ;
50.
51. reg [15:0] password ;
52.
53. parameter LOCKED = 2'b00 ;
54. parameter OPEN = 2'b01 ;
55. parameter PASSWORD = 2'b10 ;
56. parameter ERROR = 2'b11 ;
57.
58. always@(posedge clk or negedge rst_n)begin
59. if(!rst_n)begin
60. state_c <= LOCKED;
61. end
62. else begin
63. state_c <= state_n;
64. end
65. end
66.
67. always@(*)begin
68. case(state_c)
69. LOCKED:begin
70. if(locked2password_switch)begin
71. state_n = PASSWORD;
72. end
73. else begin
74. state_n = state_c;
75. end
76. end
77. OPEN:begin
78. if(open2password_switch)begin
79. state_n = PASSWORD;
80. end
81. else begin
82. state_n = state_c;
83. end
84. end
85. PASSWORD:begin
86. if(password2locked_switch0)begin
87. state_n = LOCKED;
88. end
89. else if(password2open_switch0 || password2open_switch1)begin
90. state_n = OPEN;
91. end
92. else if(password2error_switch || password2locked_switch1)begin
93. state_n = ERROR;
94. end
95. else begin
96. state_n = state_c;
97. end
98. end
99. ERROR:begin
100. if(error2locked_switch0 )begin
101. state_n = LOCKED;
102. end
103. else begin
104. state_n = state_c;
105. end
106. end
107. default:begin
108. state_n = LOCKED;
109. end
110. endcase
111. end
112. assign locked2password_switch = state_c==LOCKED && lock_stata_flag && key_num<10 && key_vld;
113. assign open2password_switch = state_c==OPEN && !lock_stata_flag && key_num<10 && key_vld;
114. assign password2locked_switch0 = state_c==PASSWORD && lock_stata_flag && end_cnt_10s_nvld;
115. assign password2locked_switch1 = state_c==PASSWORD && lock_stata_flag && confirm && password!=PASSWORD_INI ;
116. assign password2open_switch0 = state_c==PASSWORD && lock_stata_flag && confirm && password==PASSWORD_INI && password_correct_twice;
117. assign password2open_switch1 = state_c==PASSWORD && !lock_stata_flag && end_cnt_10s_nvld;
118. assign password2error_switch = state_c==PASSWORD && !lock_stata_flag && confirm && password!=PASSWORD_INI;
119. assign error2locked_switch0 = state_c==ERROR && end_cnt_2s;
120.
121.
122. always @(posedge clk or negedge rst_n)begin
123. if(rst_n==1'b0)begin
124. lock_stata_flag <= 1;
125. end
126. else if(password2locked_switch0 || password2locked_switch1 || error2locked_switch0)begin
127. lock_stata_flag <= 1;
128. end
129. else if(password2open_switch0 || password2open_switch1 )begin
130. lock_stata_flag <= 0;
131. end
132. end
133.
134.
135. always @(posedge clk or negedge rst_n)begin
136. if(rst_n==1'b0)begin
137. cnt_10s_nvld <= 0;
138. end
139. else if(end_cnt_10s_nvld)begin
140. cnt_10s_nvld <= 0;
141. end
142. else if(add_cnt_10s_nvld)begin
143. cnt_10s_nvld <= cnt_10s_nvld + 1;
144. end
145. end
146. assign add_cnt_10s_nvld = state_c==PASSWORD;
147. assign end_cnt_10s_nvld = add_cnt_10s_nvld && cnt_10s_nvld==C_10S_NUM-1;
148.
149. assign confirm = key_num==10 && key_vld;
150.
151.
152. always @(posedge clk or negedge rst_n)begin
153. if(rst_n==1'b0)begin
154. password_correct_twice <= 0;
155. end
156. else if(state_c==PASSWORD && lock_stata_flag && confirm && password==PASSWORD_INI && !password_correct_twice)begin
157. password_correct_twice <= 1;
158. end
159. else if(password2locked_switch0 || password2locked_switch1 || password2open_switch0 || password2open_switch1 || password2error_switch)begin
160. password_correct_twice <= 0;
161. end
162. end
163.
164.
165. always @(posedge clk or negedge rst_n)begin
166. if(rst_n==1'b0)begin
167. cnt_2s <= 0;
168. end
169. else if(end_cnt_2s )begin
170. cnt_2s <= 0;
171. end
172. else if(add_cnt_2s )begin
173. cnt_2s <= cnt_2s + 1;
174. end
175. end
176. assign add_cnt_2s = state_c==ERROR;
177. assign end_cnt_2s = add_cnt_2s && cnt_2s==C_2S_NUM-1;
178.
179.
180. always @(posedge clk or negedge rst_n)begin
181. if(rst_n==1'b0)begin
182. seg_dout <= 0;
183. end
184. else if(state_c==OPEN)begin
185. seg_dout <= {NONE_DIS,NONE_DIS,CHAR_O,CHAR_P,CHAR_E,CHAR_N};
186. end
187. else if(state_c==LOCKED)begin
188. seg_dout <= {CHAR_L,CHAR_O,CHAR_C,CHAR_K,CHAR_E,CHAR_D};
189. end
190. else if(state_c==ERROR)begin
191. seg_dout <= {NONE_DIS,CHAR_E,CHAR_R,CHAR_R,CHAR_O,CHAR_R};
192. end
193. else if(state_c==PASSWORD)begin
194. if(cnt_password==0)
195. seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS};
196. else if(cnt_password==1)
197. seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[3:0]}};
198. else if(cnt_password==2)
199. seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[7:4]},{1'b0,password[3:0]}};
200. else if(cnt_password==3)
201. seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[11:8]},{1'b0,password[7:4]},{1'b0,password[3:0]}};
202. else if(cnt_password==4)
203. seg_dout <= {NONE_DIS,NONE_DIS,{1'b0,password[15:12]},{1'b0,password[11:8]},{1'b0,password[7:4]},{1'b0,password[3:0]}};
204. end
205. end
206.
207. assign seg_dout_vld = 6'b11_1111;
208.
209.
210. always @(posedge clk or negedge rst_n)begin
211. if(rst_n==1'b0)begin
212. cnt_password <= 0;
213. end
214. else if(end_cnt_password)begin
215. cnt_password <= 0;
216. end
217. else if(add_cnt_password)begin
218. cnt_password <= cnt_password + 1;
219. end
220. end
221. assign add_cnt_password = state_c!=ERROR && key_num<10 && key_vld && cnt_password<4;
222. assign end_cnt_password = confirm || end_cnt_10s_nvld;
223.
224.
225. always @(posedge clk or negedge rst_n)begin
226. if(rst_n==1'b0)begin
227. password <= 16'h0000;
228. end
229. else if(add_cnt_password)begin
230. password <= {password[11:0],key_num};
231. end
232. end
233.
234.
235. endmodule
1.4 数码管显示模块设计
1.4.1 接口信号
信号名 | I/O | 位宽 | 定义 |
---|---|---|---|
clk | I | 1 | 系统工作时钟 50M |
rst_n | I | 1 | 系统复位信号,低电平有效 |
din | I | 30 | 30位的输入数码管显示数据。每5bit一个字符(对应一个数码管),6个数码管则一共30bit。 |
din_vld | I | 6 | 输入数据有效指示信号,din_vld[0]为1时,din[4:0]有效;din_vld[1]为1时,din[9:5]有效,以此类推。 |
segment | O | 8 | 8位数码管段选信号 |
seg_sel | O | 6 | 6位数码管位选信号 |
1.4.2 设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=1085&fromuid=100105
其中,数码管显示的数值和英文字母对应图像如下图所示:
1.4.3 参考代码
236.module seg_display(
237. clk ,
238. rst_n ,
239. din ,
240. din_vld ,
241. segment ,
242. seg_sel
243. );
244.
245. parameter SEGMENT_NUM = 6 ;
246. parameter W_DATA = 5 ;
247.
248. parameter SEGMENT_WID = 8 ;
249. parameter TIME_300US = 15_000 ;
250.
251. parameter SEG_DATA_0 = 7'b100_0000 ;
252. parameter SEG_DATA_1 = 7'b111_1001 ;
253. parameter SEG_DATA_2 = 7'b010_0100 ;
254. parameter SEG_DATA_3 = 7'b011_0000 ;
255. parameter SEG_DATA_4 = 7'b001_1001 ;
256. parameter SEG_DATA_5 = 7'b001_0010 ;
257. parameter SEG_DATA_6 = 7'b000_0010 ;
258. parameter SEG_DATA_7 = 7'b111_1000 ;
259. parameter SEG_DATA_8 = 7'b000_0000 ;
260. parameter SEG_DATA_9 = 7'b001_0000 ;
261.
262. parameter SEG_CHAR_O = 7'b010_0011 ;
263. parameter SEG_CHAR_P = 7'b000_1100 ;
264. parameter SEG_CHAR_E = 7'b000_0110 ;
265. parameter SEG_CHAR_N = 7'b010_1011 ;
266. parameter SEG_CHAR_L = 7'b100_0111 ;
267. parameter SEG_CHAR_C = 7'b100_0110 ;
268. parameter SEG_CHAR_K = 7'b000_0101 ;
269. parameter SEG_CHAR_D = 7'b010_0001 ;
270. parameter SEG_CHAR_R = 7'b010_1111 ;
271. parameter SEG_NONE_DIS = 7'b111_1111 ;
272.
273. input clk ;
274. input rst_n ;
275. input [SEGMENT_NUM*W_DATA-1:0] din ;
276. input [SEGMENT_NUM-1:0] din_vld ;
277.
278. output[SEGMENT_WID-1:0] segment ;
279. output[SEGMENT_NUM-1:0] seg_sel ;
280.
281. reg [SEGMENT_WID-1:0] segment ;
282. reg [SEGMENT_NUM-1:0] seg_sel ;
283.
284. reg [W_DATA-1:0] segment_pre ;
285. reg [SEGMENT_NUM*W_DATA-1:0] din_get ;
286. reg [14:0] cnt_300us ;
287. reg [2:0] cnt_sel ;
288. wire dot ;
289.
290.
291.wire add_cnt_300us ;
292.wire end_cnt_300us ;
293.always @(posedge clk or negedge rst_n) begin
294. if (rst_n==0) begin
295. cnt_300us <= 0;
296. end
297. else if(add_cnt_300us) begin
298. if(end_cnt_300us)
299. cnt_300us <= 0;
300. else
301. cnt_300us <= cnt_300us+1 ;
302. end
303.end
304.assign add_cnt_300us =1;
305.assign end_cnt_300us = add_cnt_300us && cnt_300us == TIME_300US-1 ;
306.
307.
308.
309.wire add_cnt_sel ;
310.wire end_cnt_sel ;
311.always @(posedge clk or negedge rst_n) begin
312. if (rst_n==0) begin
313. cnt_sel <= 0;
314. end
315. else if(add_cnt_sel) begin
316. if(end_cnt_sel)
317. cnt_sel <= 0;
318. else
319. cnt_sel <= cnt_sel+1 ;
320. end
321.end
322.assign add_cnt_sel = end_cnt_300us;
323.assign end_cnt_sel = add_cnt_sel && cnt_sel == SEGMENT_NUM-1 ;
324.
325.
326.reg [SEGMENT_NUM-1:0] din_vvld;
327.always @(posedge clk or negedge rst_n)begin
328. if(rst_n==1'b0)begin
329. din_vvld <= 0 ;
330. end
331. else begin
332. din_vvld <= din_vld ;
333. end
334.end
335.
336.
337.reg [ 2:0] cnt ;
338.wire add_cnt ;
339.wire end_cnt ;
340.always @(posedge clk or negedge rst_n) begin
341. if (rst_n==0) begin
342. cnt <= 0;
343. end
344. else if(add_cnt) begin
345. if(end_cnt)
346. cnt <= 0;
347. else
348. cnt <= cnt+1 ;
349. end
350.end
351.assign add_cnt = 1;
352.assign end_cnt = add_cnt && cnt == SEGMENT_NUM-1 ;
353.
354.always @(posedge clk or negedge rst_n)begin
355. if(rst_n==1'b0)begin
356. din_get <= 0;
357. end
358. else if(din_vvld[cnt])begin
359. din_get[W_DATA*(cnt+1)-1 -:W_DATA] <= din[W_DATA*(cnt+1)-1 -:W_DATA];
360. end
361.end
362.
363.
364. always @(*)begin
365. segment_pre = din_get[W_DATA*(cnt_sel+1)-1 -:W_DATA];
366. end
367.
368. always @(posedge clk or negedge rst_n)begin
369. if(rst_n==1'b0)begin
370. segment <= {dot,SEG_NONE_DIS};
371. end
372. else if(add_cnt_300us && cnt_300us ==10-1)begin
373. case(segment_pre)
374. 5'h00: segment <= {dot,SEG_DATA_0};
375. 5'h01: segment <= {dot,SEG_DATA_1};
376. 5'h02: segment <= {dot,SEG_DATA_2};
377. 5'h03: segment <= {dot,SEG_DATA_3};
378. 5'h04: segment <= {dot,SEG_DATA_4};
379. 5'h05: segment <= {dot,SEG_DATA_5};
380. 5'h06: segment <= {dot,SEG_DATA_6};
381. 5'h07: segment <= {dot,SEG_DATA_7};
382. 5'h08: segment <= {dot,SEG_DATA_8};
383. 5'h09: segment <= {dot,SEG_DATA_9};
384. 5'h10: segment <= {dot,SEG_CHAR_O};
385. 5'h11: segment <= {dot,SEG_CHAR_P};
386. 5'h12: segment <= {dot,SEG_CHAR_E};
387. 5'h13: segment <= {dot,SEG_CHAR_N};
388. 5'h14: segment <= {dot,SEG_CHAR_L};
389. 5'h15: segment <= {dot,SEG_CHAR_C};
390. 5'h16: segment <= {dot,SEG_CHAR_K};
391. 5'h17: segment <= {dot,SEG_CHAR_D};
392. 5'h18: segment <= {dot,SEG_CHAR_R};
393. 5'h1F: segment <= {dot,SEG_NONE_DIS};
394. default:segment <= {dot,SEG_NONE_DIS};
395. endcase
396. end
397. end
398. assign dot = 1'b1;
399.
400. always@(posedge clk or negedge rst_n)begin
401. if(rst_n==1'b0)begin
402. seg_sel <= {SEGMENT_NUM{1'b0}};
403. end
404. else begin
405. seg_sel <= ~(1'b1<<cnt_sel);
406. end
407. end
408.
409.
410. endmodule
1.5 效果和总结
下图是该工程在db603开发板上的现象——密码锁初始状态和闭合状态
下图是该工程在db603开发板上的现象——提示输入错误状态
下图是该工程在db603开发板上的现象——密码锁开启状态
下图是该工程在db603开发板上的现象——输入密码状态
下图是该工程在mp801开发板上的现象——密码锁初始状态和闭合状态
下图是该工程在mp801开发板上的现象——提示输入错误状态
下图是该工程在mp801开发板上的现象——密码锁开启状态
下图是该工程在mp801开发板上的现象——输入密码状态
下图是该工程在ms980开发板上的现象——密码锁初始状态和闭合状态
下图是该工程在ms980开发板上的现象——提示输入错误状态
下图是该工程在ms980开发板上的现象——密码锁开启状态
下图是该工程在ms980开发板上的现象——输入密码状态
由于该项目的上板现象是在数码管上显示输入的密码,并且判断密码是否正确:正确则在数码管上显示OPEN,错误则在数码管上显示ERROR并提示输入错误2秒,然后数码管显示LOCKED。想观看完整现象的朋友可以看一下上板演示的视频。
感兴趣的朋友也可以访问mdy论坛进行FPGA相关工程设计学习,也可以看一下我们往期的文章。