当前位置:   article > 正文

基于FPGA,实现自动售货机Verilog_verilog自动售货机设计

verilog自动售货机设计

前言

        本文章为FPGA设计的实例,基于Verilog,来源于学习FPGA中进行的一项练习,为了能够更快更好的学习FPGA。这也是一项比较全面的一次练习。

设计思路

        平台:Quartus;仿真:Altera_modelsim
        使用状态机进行设计,通过按键进行状态的跳转,电路接口如图所示:

设计一个有A,B,C三种商品的自动售货机,通过按键进行选择,状态跳转,以及投币,找零的流程。
状态机的设计:
IDLE(空闲):数码管上显示 A3b5C1;
PICK(挑选):通过按键1进行选择ABC商品,通过按键2选择购买的数量;
BUY(购买计算):选择好商品以及数量以后,计算出需要的总金额并显示在数码管上,通过按键1,按键2进行投币;
ZL(找零):投币金额减去商品的总金额,将找零的金额显示在数码管上;
CH(出货):当投币金额>=商品总金额后,LED灯闪烁以及数码管显示FFFFFF。

代码部分

        控制模块代码

  1. /**************************************************************
  2. @File : ctrl.v
  3. @Time : 2024/03/26 10:06:48
  4. @Author : huangmo777x7
  5. @EditTool: VS Code
  6. @Font : UTF-8
  7. @Function: 自动售货机控制模块
  8. **************************************************************/
  9. module ctrl (
  10. input clk ,
  11. input rst_n ,
  12. input [2:0] key_in ,
  13. output reg [23:0] display_data ,
  14. output reg [5:0] display_point ,
  15. output reg [5:0] display_mask ,
  16. output reg [3:0] led
  17. );
  18. //中间信号定义
  19. localparam IDLE = 5'b00001, //空闲状态
  20. PICK = 5'b00010, //选择商品以及数量
  21. BUY = 5'b00100, //进行投币,key_in[1]+1 key_in[2]+5
  22. ZL = 5'b01000, //找零 投币-金额=找的钱
  23. CH = 5'b10000; //商品出货
  24. reg [4:0] state ;
  25. wire idle2pick ;
  26. wire pick2buy ;
  27. wire buy2zl ;
  28. wire buy2ch ;
  29. wire zl2ch ;
  30. wire ch2idle ;
  31. reg [5:0] pick_sel;
  32. reg [3:0] a,b,c;
  33. wire [6:0] sum;
  34. wire [3:0] sum_g,sum_s;
  35. reg [6:0] pay_sum;
  36. wire [3:0] pay_sum_g,pay_sum_s;
  37. wire [6:0] diff_sum;
  38. wire [3:0] diff_sum_g,diff_sum_s;
  39. reg [26:0] cnt_delay;
  40. wire add_delay_cnt,end_delay_cnt;
  41. parameter TIME_2S = 100_000_000;
  42. reg [24:0] cnt_mask;
  43. wire add_mask_cnt,end_mask_cnt;
  44. parameter TIME_500MS = 25_000_000;
  45. wire [23:0] display_idle ;
  46. wire [23:0] display_pick ;
  47. wire [23:0] display_buy ;
  48. wire [23:0] display_zl ;
  49. wire [23:0] display_ch ;
  50. reg [22:0] cnt_led;
  51. wire add_led_cnt,end_led_cnt ;
  52. parameter TIME_100MS = 5_000_000 ;
  53. /******************************************************************
  54. 状态机设计
  55. *******************************************************************/
  56. always @(posedge clk or negedge rst_n) begin
  57. if(!rst_n)
  58. state <= IDLE;
  59. else case(state)
  60. IDLE : if(idle2pick) state <= PICK ;
  61. PICK : if(pick2buy) state <= BUY ;
  62. BUY : if(buy2zl) state <= ZL ;
  63. else if(buy2ch) state <= CH ;
  64. ZL : if(zl2ch) state <= CH ;
  65. CH : if(ch2idle) state <= IDLE ;
  66. endcase
  67. end
  68. assign idle2pick = state == IDLE && key_in[0];
  69. assign pick2buy = state == PICK && key_in[0];
  70. assign buy2zl = state == BUY && pay_sum > sum;
  71. assign buy2ch = state == BUY && pay_sum == sum;
  72. assign zl2ch = state == ZL && end_delay_cnt ;
  73. assign ch2idle = state == CH && end_delay_cnt ;
  74. /******************************************************************
  75. 各个状态功能
  76. *******************************************************************/
  77. //通过按键1进行数码管选位
  78. always@(posedge clk or negedge rst_n)begin
  79. if(!rst_n)
  80. pick_sel <= 6'b000001;
  81. else if(key_in[1] && state == PICK)
  82. pick_sel <= {pick_sel[3:0],pick_sel[5:4]};
  83. else if(pick2buy)
  84. pick_sel <= 6'b000001;
  85. end
  86. //通过按键2进行计数
  87. always@(posedge clk or negedge rst_n)begin
  88. if(!rst_n)begin
  89. a <= 'b0;
  90. b <= 'b0;
  91. c <= 'b0;
  92. end
  93. else if(key_in[2] && state == PICK)
  94. case(pick_sel)
  95. 6'b000001 : if(c == 9) c <= 0; else c <= c + 1 ;
  96. 6'b000100 : if(b == 9) b <= 0; else b <= b + 1 ;
  97. 6'b010000 : if(a == 9) a <= 0; else a <= a + 1 ;
  98. endcase
  99. else if(ch2idle)begin
  100. a <= 'b0;
  101. b <= 'b0;
  102. c <= 'b0;
  103. end
  104. end
  105. //sum 商品的总价
  106. assign sum = 3*a + 5*b + 1*c;
  107. assign sum_g = sum % 10;
  108. assign sum_s = sum / 10;
  109. //pay_sum 投币的金额
  110. always@(posedge clk or negedge rst_n)
  111. if(!rst_n)
  112. pay_sum <= 'b0;
  113. else if(key_in[2] && state == BUY)
  114. pay_sum <= pay_sum + 5;
  115. else if(key_in[1] && state == BUY)
  116. pay_sum <= pay_sum + 1;
  117. else if(ch2idle)
  118. pay_sum <= 'b0;
  119. assign pay_sum_g = pay_sum % 10;
  120. assign pay_sum_s = pay_sum / 10;
  121. //找零=投币的金额-商品的总价
  122. assign diff_sum = pay_sum - sum;
  123. assign diff_sum_g = diff_sum % 10;
  124. assign diff_sum_s = diff_sum / 10;
  125. //ZL和CH状态的延时计数器
  126. always @(posedge clk or negedge rst_n)
  127. if(!rst_n)
  128. cnt_delay <= 'd0;
  129. else if(add_delay_cnt)begin
  130. if(end_delay_cnt)
  131. cnt_delay <= 'd0;
  132. else
  133. cnt_delay <= cnt_delay + 1'b1;
  134. end
  135. assign add_delay_cnt = ((state == ZL) || (state == CH));
  136. assign end_delay_cnt = add_delay_cnt && cnt_delay == TIME_2S - 1 ;
  137. /******************************************************************
  138. 显示数据输出
  139. *******************************************************************/
  140. //各个状态数码管所显示的
  141. always@(*)
  142. case(state)
  143. IDLE : begin display_data = display_idle ; display_point = 6'b010100; end
  144. PICK : begin display_data = display_pick ; display_point = 6'b010100; end
  145. BUY : begin display_data = display_buy ; display_point = 6'b001000; end
  146. ZL : begin display_data = display_zl ; display_point = 6'b000000; end
  147. CH : begin display_data = display_ch ; display_point = 6'b000000; end
  148. default : begin display_data = display_idle ; display_point = 6'b111111; end
  149. endcase
  150. assign display_idle = {4'd10,4'd3,4'd11,4'd5,4'd12,4'd1};
  151. assign display_pick = {4'd10,a,4'd11,b,4'd12,c};
  152. assign display_buy = {4'd13,sum_s,sum_g,4'd5,pay_sum_s,pay_sum_g};
  153. assign display_zl = {4'd1,4'd1,4'd1,4'd14,diff_sum_s,diff_sum_g};
  154. assign display_ch = {4'd15,4'd15,4'd15,4'd15,4'd15,4'd15};
  155. //掩码
  156. always@(posedge clk or negedge rst_n)
  157. if(!rst_n)
  158. display_mask <= 6'b111111;
  159. else case(state)
  160. PICK : if(end_mask_cnt)
  161. case(pick_sel)
  162. 6'b000001 : begin display_mask <= {5'b11111,~display_mask[0]}; end
  163. 6'b000100 : begin display_mask <= {3'b111,~display_mask[2],2'b11}; end
  164. 6'b010000 : begin display_mask <= {1'b1,~display_mask[4],4'b1111}; end
  165. default : ;
  166. endcase
  167. ZL : display_mask <= 6'b000111;
  168. default : display_mask <= 6'b111111;
  169. endcase
  170. //选择数码管闪烁计数器
  171. always @(posedge clk or negedge rst_n)
  172. if(!rst_n)
  173. cnt_mask <= 'd0;
  174. else if(add_mask_cnt)begin
  175. if(end_mask_cnt)
  176. cnt_mask <= 'd0;
  177. else
  178. cnt_mask <= cnt_mask + 1'b1;
  179. end
  180. assign add_mask_cnt = 1;
  181. assign end_mask_cnt = add_mask_cnt && cnt_mask == TIME_500MS - 1 ;
  182. /******************************************************************
  183. 出货时LED流水闪烁
  184. *******************************************************************/
  185. always@(posedge clk or negedge rst_n)
  186. if(!rst_n)
  187. led <= 'b0;
  188. else if(state == CH && end_led_cnt)
  189. if(led == 'b0 || led == 4'b0001)
  190. led <= 4'b1000;
  191. else
  192. led <= (led >> 1);
  193. else if(state != CH)
  194. led <= 'b0;
  195. always @(posedge clk or negedge rst_n)
  196. if(!rst_n)
  197. cnt_led <= 'd0;
  198. else if(add_led_cnt)begin
  199. if(end_led_cnt)
  200. cnt_led <= 'd0;
  201. else
  202. cnt_led <= cnt_led + 1'b1;
  203. end
  204. assign add_led_cnt = 1;
  205. assign end_led_cnt = add_led_cnt && cnt_led == TIME_100MS - 1 ;
  206. endmodule

仿真代码

  1. /**************************************************************
  2. @File : ctrl_tb.v
  3. @Time : 2024/03/26 15:46:14
  4. @Author : haungmo777x7
  5. @EditTool: VS Code
  6. @Font : UTF-8
  7. @Function: 仿真ctrl模块文件
  8. **************************************************************/
  9. `timescale 1ns/1ps
  10. module ctrl_tb ();
  11. parameter CLK_CYCLE = 20;
  12. reg sys_clk,sys_rst_n;
  13. always #(CLK_CYCLE/2) sys_clk = ~sys_clk;
  14. initial begin
  15. sys_clk = 1'b1;
  16. sys_rst_n = 1'b0;
  17. #(CLK_CYCLE*2);
  18. sys_rst_n = 1'b1;
  19. end
  20. reg [2:0] key_in; // 按键
  21. wire [3:0] led; //LED灯
  22. defparam ctrl.TIME_2S = 200; //重定义时间,让仿真快一些
  23. defparam ctrl.TIME_500MS = 50; //重定义时间,让仿真快一些
  24. defparam ctrl.TIME_100MS = 10; //重定义时间,让仿真快一些
  25. ctrl ctrl(
  26. /* input */.clk (sys_clk) ,
  27. /* input */.rst_n (sys_rst_n) ,
  28. /* input [2:0] */.key_in (key_in ) ,
  29. /* output */.led (led)
  30. );
  31. initial begin
  32. key_in = 0;
  33. #(CLK_CYCLE*20);
  34. //进入状态(激活售货机)
  35. my_key(0);
  36. //买三件C
  37. my_key(2);
  38. my_key(2);
  39. my_key(2); //3
  40. //切换到B商品
  41. my_key(1);
  42. //买两件B
  43. my_key(2);
  44. my_key(2); //10
  45. //切换到A
  46. my_key(1);
  47. //买1件A
  48. my_key(2); //3
  49. //结算
  50. my_key(0);
  51. //故意多给1块 17
  52. my_key(1);
  53. my_key(1);
  54. my_key(2);
  55. my_key(2);
  56. my_key(2);
  57. #2000;
  58. $stop;
  59. end
  60. task my_key;
  61. input [1:0] num;
  62. begin
  63. key_in[num] = 1;
  64. #(CLK_CYCLE*1);
  65. key_in[num] = 0;
  66. #(CLK_CYCLE*1000);
  67. end
  68. endtask
  69. endmodule

仿真结果

仿真结果也是调试了很多次代码,结果如图所示,将数码管驱动模块以及按键消抖模块加入即可上板进行验证。

上板验证效果:
 

111

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/744739
推荐阅读
相关标签
  

闽ICP备14008679号