--作者:喝喝
本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
学习了明德扬至简设计法和明德扬设计规范,本人用FPGA设计了一个测距系统。该系统采用超声波进行测量距离再在数码管上显示。在本案例的设计过程中包括了超声波的驱动、三线式数码管显示等技术。经过逐步改进、调试等一系列工作后,最终完成了此设计,并进行上板验证,下面将完整的设计记录与大家分享。
1.1.2 设计目标
此系统将实时显示前方障碍与装置之间的距离。
1.1.3 系统结构框图
系统结构框图如下所示:
![8c73e8f73c19cf6aab302ea46222ed76.png](https://i-blog.csdnimg.cn/blog_migrate/c13745e8b40e90c01184f372899b9710.png)
1.1.4 模块功能
Ø hc_sr04模块实现功能
该模块通过控制触发信号trig(10us的TTL)使内部循环发出8个40KHZ脉冲即驱动超声波,接收回响信号echo,通过echo得到距离。
Ø 显示模块实现功能
该模块完成了对所测距离通过数码管对其显示。
1.1.5 顶层信号
![2bcfdecf01087dc0772069301094bf7c.png](https://i-blog.csdnimg.cn/blog_migrate/da485cd790ff2eb256dd3c2e9e7dd700.png)
1.1.6 顶层代码
1. module top(
2. clk ,
3. rst_n ,
4. echo ,
5.
6. trig ,
7. sel,
8. seg
9. );
10.
11.
12. input clk ;
13. input rst_n ;
14. input echo ;
15.
16.
17. output trig ;
18.
19.
20. wire [3:0] s_g ;
21. wire [3:0] s_s ;
22. wire [3:0] s_b ;
23. wire [3:0] s_q ;
24. output [7:0] sel ;
25. output [7:0] seg ;
26.
27. hc_sr04 hc_sr04_1(
28. .clk (clk) ,
29. .rst_n (rst_n) ,
30. .echo (echo) ,
31.
32. .trig (trig) ,
33. .s_g (s_g ),
34. .s_s (s_s ),
35. .s_b (s_b ),
36. .s_q (s_q )
37. );
38.
39. seg_disp u_seg_disp(
40. .clk (clk ),
41. .rst_n (rst_n),
42. .segment_data({s_q,s_b,s_s,s_g}),
43. .segment (seg ),
44. .seg_sel (sel )
45. );
46.
47. endmodule
1.2 hc_sr04模块设计
1.2.1 接口信号
![f0cbe529b3f775c40d6811bdb1cb8019.png](https://i-blog.csdnimg.cn/blog_migrate/e5aaf6e8d32613179c02e5e703f722e5.jpeg)
1.2.2 设计思路
我们只需要提供一个短期的10uS脉冲触发信号trig,该模块内部将发出8个40kHz周期电平并检测回波,一旦检测到有回波信号则输出回响信号,回响信号echo是一个脉冲的宽度成正比的距离变量,可通过发射信号到收到的回响信号时间间隔可以计算得到距离。建议测量周期为60ms以上,以防止发射信号对回响信号的影响,这里我们采用的是1s测量一次。
时钟计数器 cnt0:用于计算 1 秒的时钟个数,加一条件为 1,表示一直计数;结束条件为数到 TIME_1S ,表示数到 1 秒就清零。
距离计数器 h_cnt:用于计算 flag为高电平的宽度的时间,如果flag为1,h_cnt就加一;每完成1秒计数后h_cnt就变为0,此外h_cnt等于h_cnt。
Ø
![7be0e784a7196d901b48521dae0a2475.png](https://i-blog.csdnimg.cn/blog_migrate/4da104bb218360612399ecfe5c327b74.png)
1.2.3 参考代码
1. module hc_sr04(
2. clk ,
3. rst_n ,
4. echo ,
5.
6. trig ,
7. s_g ,
8. s_s ,
9. s_b ,
10. s_q
11. );
12.
13.
14. parameter DATA_W = 14 ;
15. parameter TIME_1S = 50_000_000;
16.
17. input clk ;
18. input rst_n ;
19. input echo ;
20.
21. output trig ;
22. output[ 3:0] s_g ;
23. output[ 3:0] s_s ;
24. output[ 3:0] s_b ;
25. output[ 3:0] s_q ;
26.
27.
28. wire trig ;
29. reg [ 3:0] s_g ;
30. reg [ 3:0] s_s ;
31. reg [ 3:0] s_b ;
32. reg [ 3:0] s_q ;
33. reg [DATA_W-1:0] distance;
34.
35.
36. reg [25:0] cnt0 ;
37. reg [20:0] h_cnt ;
38. reg echo_2 ;
39. reg echo_1 ;
40. wire add_cnt0;
41. wire end_cnt0;
42. wire flag_h ;
43. wire flag_l ;
44.
45.
46.
47. always @(posedge clk or negedge rst_n)begin
48. if(!rst_n)begin
49. cnt0 <= 0;
50. end
51. else if(add_cnt0)begin
52. if(end_cnt0)
53. cnt0 <= 0;
54. else
55. cnt0 <= cnt0 + 1'b1;
56. end
57. end
58.
59. assign add_cnt0 = 1;
60. assign end_cnt0 = add_cnt0 && cnt0 == TIME_1S - 1;
61.
62.
63.
64. assign trig = (cnt0>=500&&cnt0<1000)?1:0;
65.
66.
67. always @(posedge clk or negedge rst_n)begin
68. if(rst_n==1'b0)begin
69. echo_1 <= 0;
70. echo_2 <= 0;
71. end
72. else begin
73. echo_1 <= echo ;
74. echo_2 <= echo_1;
75. end
76. end
77.
78. always @(posedge clk or negedge rst_n)begin
79. if(!rst_n)begin
80. h_cnt <= 0;
81. end
82. else if(add_h_cnt)begin
83. if(end_h_cnt)
84. h_cnt <= 0;
85. else
86. h_cnt <= h_cnt + 1;
87. end
88. else if(end_cnt0)begin
89. h_cnt <= 0;
90. end
91. end
92.
93. assign add_h_cnt = echo_2;
94. assign end_h_cnt = 0 ;
95.
96.
97.
98. always @(posedge clk or negedge rst_n)begin
99. if(rst_n==1'b0)begin
100. distance <= 0;
101. end
102. else if(add_cnt0 && cnt0 == 45_000_000-1)begin
103. distance <= h_cnt*34/10000;
104. end
105. end
106.
107.
108.
109. always @(posedge clk or negedge rst_n)begin
110. if(rst_n==1'b0)begin
111. s_g <= 0;
112. end
113. else begin
114. s_g <= distance%10;
115. end
116. end
117.
118.
119. always @(posedge clk or negedge rst_n)begin
120. if(rst_n==1'b0)begin
121. s_s <= 0;
122. end
123. else begin
124. s_s <= (distance/10)%10;
125. end
126. end
127.
128.
129. always @(posedge clk or negedge rst_n)begin
130. if(rst_n==1'b0)begin
131. s_b <= 0;
132. end
133. else begin
134. s_b <= (distance/100)%10;
135. end
136. end
137.
138.
139. always @(posedge clk or negedge rst_n)begin
140. if(rst_n==1'b0)begin
141. s_q <= 0;
142. end
143. else begin
144. s_q <= (distance/1000)%10;
145. end
146.
147. end
148.
149.
150.
151.
152. endmodule
1.3 显示模块设计
1.3.1 接口信号
![ae273127c62b83007709060a6110458e.png](https://i-blog.csdnimg.cn/blog_migrate/2ec9b0103828b3e6f515c14f1d9ba01c.jpeg)
1.3.2 设计思路
该模块对数码管的位选信号sel每隔1ms的时间移位一次,也就是1ms循环亮一个灯,由于1ms的频率肉眼观察不出,我们看到的就是4个灯全亮。
对输入距离distance进行求余处理,得到每一位的数据,通过case语句,让每一位数据形成段选信号,通过位选信号的控制显示在对应的数码管上。
1.3.3 参考代码
1. module seg_disp(
2. clk ,
3. rst_n ,
4. segment_data,
5. segment ,
6. seg_sel
7. );
8.
9. parameter ZERO = 8'b1100_0000 ;
10. parameter ONE = 8'b1111_1001 ;
11. parameter TWO = 8'b1010_0100 ;
12. parameter THREE = 8'b1011_0000 ;
13. parameter FOUR = 8'b1001_1001 ;
14. parameter FIVE = 8'b1001_0010 ;
15. parameter SIX = 8'b1000_0010 ;
16. parameter SEVEN = 8'b1111_1000 ;
17. parameter EIGHT = 8'b1000_0000 ;
18. parameter NINE = 8'b1001_0000 ;
19.
20. input clk ;
21. input rst_n ;
22. input [31:0] segment_data ;
23. output [7:0 ] segment ;
24. output [7:0 ] seg_sel ;
25.
26. reg [7:0 ] segment ;
27. reg [7:0 ] seg_sel ;
28. reg [10:0] delay ;
29. reg [3:0 ] delay_time ;
30. wire add_delay_time ;
31. wire end_delay_time ;
32. wire add_delay ;
33. wire end_delay ;
34. wire [3:0 ] segment_tmp ;
35.
36.
37.
38.
39. always @(posedge clk or negedge rst_n) begin
40. if (rst_n==0) begin
41. delay <= 0;
42. end
43. else if(add_delay) begin
44. if(end_delay)
45. delay <= 0;
46. else
47. delay <= delay+1 ;
48. end
49. end
50. assign add_delay = 1;
51. assign end_delay = add_delay && delay == 2000-1 ;
52.
53.
54.
55.
56. always @(posedge clk or negedge rst_n) begin
57. if (rst_n==0) begin
58. delay_time <= 0;
59. end
60. else if(add_delay_time) begin
61. if(end_delay_time)
62. delay_time <= 0;
63. else
64. delay_time <= delay_time+1 ;
65. end
66. end
67. assign add_delay_time = end_delay;
68. assign end_delay_time = add_delay_time && delay_time == 8-1 ;
69.
70.
71. assign segment_tmp = segment_data[(1+delay_time)*4-1 -:4];
72. always @(posedge clk or negedge rst_n)begin
73. if(rst_n==1'b0)begin
74. segment <= ZERO;
75. end
76. else begin
77. case(segment_tmp)
78. 4'd0:segment <= ZERO;
79. 4'd1:segment <= ONE ;
80. 4'd2:segment <= TWO ;
81. 4'd3:segment <= THREE;
82. 4'd4:segment <= FOUR ;
83. 4'd5:segment <= FIVE ;
84. 4'd6:segment <= SIX ;
85. 4'd7:segment <= SEVEN;
86. 4'd8:segment <= EIGHT;
87. 4'd9:segment <= NINE ;
88. default:begin
89. segment <= segment;
90. end
91. endcase
92. end
93. end
94.
95.
96. always @(posedge clk or negedge rst_n)begin
97. if(rst_n==1'b0)begin
98. seg_sel <= 8'b1111_1111;
99. end
100. else begin
101. seg_sel <= ~(8'b1<<delay_time);
102. end
103. end
104.
105.
106. endmodule
1.4 效果和总结
Ø 上板验证效果
![3aace2a1db1787bd7dc58432013d62e0.png](https://i-blog.csdnimg.cn/blog_migrate/be23e8bd974adeeccc8ffd0cdada5808.jpeg)
![121b90bab8da341a230b3295eafe8e0a.png](https://i-blog.csdnimg.cn/blog_migrate/7b192a10b2d60c494c955f7875bb7fc4.jpeg)
![de6ebc1a8feb5471c49f65350bc418cf.png](https://i-blog.csdnimg.cn/blog_migrate/349676ca88b9bb0cc771e38094dd70c0.jpeg)
在这个设计中,使用明德杨的至简设计法,让我的思路非常清晰,逻辑非常严谨,虽然没有做到一遍成功,但在调试过程中我都比较快速的找到问题,并快速解决。对于学习FPGA的同学,我非常推荐使用明德杨至简设计法和明德杨模块进行学习和设计。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也欢迎大家在评论与我进行讨论!