数码管6位,从左往右,依次是两位的投币输入金额,两位的商品价格
以及两位的找零金额。初始显示1位,两位,1位。
位选总共六种显示状态,段选10种状态。
module key_drive (
input wire clk,
input wire rst_n,
input wire [2:0] key, //按键
output reg [19:0] value //输出到数码管显示的值
);
parameter MAX_VALUE = 20'd1024; //最大显示数字
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
value <= 20'd0;
end
else if (value >= MAX_VALUE) begin
value <= 20'd0;
end
// 根据按下的键进行对应的操作
else begin
if(key[0]) begin
value <= 2;
end
else if (key[1]) begin
value <= 3;
end
else if (key[2]) begin
value <= 4;
end
else begin
value <= value;
end
end
end
endmodule
module machine_drive(
input wire clk , //时钟信号
input wire rst_n , //复位信号
input wire [2:0] key , //3个按键 KEY4-key[2]:1元 KEY3-key[1]:0.5元 KEY2-key[0]:更换商品价格
output reg [3:0] led_value , //对应led显示效果的类型
output reg [6:0] price_put , //用户投入的钱
output reg [6:0] price_need , //商品价格
output reg [6:0] price_out //找零
);
//四种商品对应价格
parameter P1 = 7'd5;
parameter P2 = 7'd15;
parameter P3 = 7'd24;
parameter P4 = 7'd30;
parameter MAX_TIME = 28'd100_000_000; //退款过程持续时间
reg [1 :0] price_tmp ; //当前商品价格
reg [27:0] cnt_time ; //用作退款过程计时
reg flag_can_operation ; //可以继续投币和切换商品
reg flag_is_retreat_end ; //结算完毕
wire flag_is_retreat ; //开始结算
wire flag_price_is_enough ; //可以买下商品
reg [6:0] price_put_last ; //结算前的投币数
//结算倒计时模块
always @(posedge clk,negedge rst_n) begin
if(!rst_n)begin
cnt_time <= 28'd0; //初始设置计数器为0
flag_can_operation <= 1'b1 ;//设置KEY键可操作
flag_is_retreat_end <= 1'b0;//设置结算完毕标志为0
end
else begin
if (flag_is_retreat) begin //开始结算
cnt_time <= MAX_TIME; //计数器设置最大值
flag_can_operation <= 1'b0 ;//当前处于结算状态,设置KEY键不可用
flag_is_retreat_end <= 1'b0;//设置结算未完毕
end
else if(cnt_time > 28'd1) begin //计数器数值大于1
cnt_time <= cnt_time - 28'd1;//倒计时
flag_can_operation <= 1'b0; //当前处于结算状态,设置KEY键不可操作
flag_is_retreat_end <= 1'b0; //设置结算未完毕
end
else if(cnt_time == 28'd1) begin//计数器倒计时数到1
cnt_time <= 28'd0; //计数器清零
flag_can_operation <= 1'b1; //结算完毕,设置KEY键可操作
flag_is_retreat_end <= 1'b1; //设置结算完毕
end
else begin
cnt_time <= cnt_time;
flag_can_operation <= flag_can_operation;
flag_is_retreat_end <= 1'b0;
end
end
end
//按下KEY2-key[0]切换商品价格
always@(posedge clk,negedge rst_n)begin
if(!rst_n)begin
price_tmp<= 2'd0; //初始设置商品价格为1号商品
end
else if(flag_can_operation) begin//当前按键可操作
// 当没有投币的时候按下 KEY2-key[0] 为切换商品
if(key[0] && ! price_put) begin
price_tmp <= (price_tmp + 2'd1) % 4;//%操作,循环切换
end
else begin
price_tmp <= price_tmp; //没有按下切换按键,当前商品价格保持
end
end
else begin
price_tmp <= price_tmp;
end
end
// 切换商品价格
always@(posedge clk,negedge rst_n)begin
if(!rst_n)
price_need<=P1;
else
case(price_tmp)
2'b00 : price_need <= P1;
2'b01 : price_need <= P2;
2'b10 : price_need <= P3;
2'b11 : price_need <= P4;
default:price_need <= P1;
endcase
end
//切换商品价格时led灯光效果
always @(posedge clk,negedge rst_n) begin
if(!rst_n)
led_value <= 4'd1;
//退款的时候判断是 补差价 或者 全额退款
//补差价为流水灯效果
//全额退款为闪烁效果
else if(flag_is_retreat)begin//开始结算
led_value <= price_put_last >= price_need ? 4'd6 : 4'd7 ;//当前投币数大于等于商品价格,LED状态为6,否则状态为7
end
//正常操作状态根据当前选择商品亮起对应商品led
else if(flag_can_operation)begin
case(price_tmp)
2'b00 : led_value <= 4'd2;
2'b01 : led_value <= 4'd3;
2'b10 : led_value <= 4'd4;
2'b11 : led_value <= 4'd5;
default:led_value <= 4'd2;
endcase
end
else
led_value <= led_value;
end
//用户通过按键进行投币
always@(posedge clk,negedge rst_n)begin
if(!rst_n)begin
price_put_last<=7'd0;//累计投币数初始化为0
end
else if(flag_can_operation) begin//当前按键可操作
//超过100 或者
if(price_put_last>=7'd100 || flag_is_retreat) begin//当前投币大于10超过可显示数字或开始结算,投币清零
price_put_last<=7'd0;
end
/*
按下 key[2] 投币 + 10
按下 key[1] 投币 + 5
*/
else begin
if(key[2])//KEY4-key[2]投币1元
price_put_last<=price_put_last+7'd10;
else if(key[1])//KEY3-key[1]投币0.5元
price_put_last<=price_put_last+7'd5;
else
price_put_last<=price_put_last;
end
end
else begin
price_put<=price_put;
end
end
//投币数码显示保持2s
always @(posedge clk,negedge rst_n) begin
if(!rst_n)begin
price_put<=7'd0;
end
else if(!flag_can_operation)//按键不可操作
price_put <= price_put;//输出投币保持
else
price_put <= price_put_last;//否则将当前投币值赋值给输出投币寄存器
end
// 输出找零
always @(posedge clk,negedge rst_n) begin
if(!rst_n)begin
price_out <= 7'd0;//初始找零0元
end
//结算完毕,归零
else if(flag_is_retreat_end) begin
price_out =7'd0 ;
end
//当退款标志到来,计算退款金额为 补差价 或者 全额退款
else if(flag_is_retreat) begin//开始结算
price_out <= price_put_last >= price_need ? price_put_last - price_need : price_put_last ;//当前投币大于商品价格则找零差值,否则找零为0
end
else begin
price_out <= price_out;
end
end
//当投币可以买下商品
assign flag_price_is_enough = price_put_last >= price_need;//当前投币数大于等于商品价格,投币足够标志置1
//为了保证在结算前得到最后一次投币数量
// assign price_put_last = price_put;
// 当币足够 或者 在投币过程选择切换商品则开始退款
assign flag_is_retreat = flag_price_is_enough || (price_put && key[0]);//投币足够或退款,结算处理标志置1
endmodule
module led_drive (
input wire clk ,//时钟信号
input wire rst_n,//复位信号
input wire [3:0] value,//LED显示状态
output reg [3:0] led //4个LED输出
);
/*
value 效果
0 全灭
1 全亮
2 只亮led[0]
3 只亮led[1]
4 只亮led[2]
5 只亮led[3]
6 流水灯
7 闪烁
*/
parameter MAX_TIME_RUNNING = 28'd4_000_000; //流水灯频率0.08s
parameter MAX_TIME_FLASH = 28'd10_000_000; //闪烁频率0.2s
reg [27:0] cnt_time_running ; //流水灯计时器
reg [27:0] cnt_time_flash; //闪烁灯计时器
reg [7:0] led_running; //流水灯状态寄存器
reg [3:0] led_flash; //闪烁灯状态寄存器
//流水灯计数器0.08s
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_time_running <=28'd1;
else if(value == 4'd6) begin
if(cnt_time_running == MAX_TIME_RUNNING)
cnt_time_running <=28'd1;
else
cnt_time_running <= cnt_time_running+28'd1;
end
else
cnt_time_running <= 28'd1;
end
//闪烁灯计数器0.2s
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_time_flash <=28'd1;
else if (value == 4'd7) begin
if(cnt_time_flash == MAX_TIME_FLASH )
cnt_time_flash <=28'd1;
else
cnt_time_flash <= cnt_time_flash+28'd1;
end
else
cnt_time_flash <= 28'd1;
end
//流水灯状态切换 间隔0.08s
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
led_running <= 8'b00001111;
else if(cnt_time_running == MAX_TIME_RUNNING)begin
led_running <= {led_running[0],led_running[7:1]};
end
else
led_running <=led_running;
end
//闪烁状态切换 间隔0.2s
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
led_flash <= 4'b0000;
else if(cnt_time_flash == MAX_TIME_FLASH)begin
led_flash <= ~led_flash;
end
else
led_flash <=led_flash;
end
//根据value值输出对应灯效果
always @(*) begin
case(value)
4'd0: begin
led = 4'b0000;//默认状态LED全灭
end
4'd1:begin
led = 4'b1111;//
end
4'd2:begin
led = 4'b0001;//选择第一种商品
end
4'd3:begin
led = 4'b0010;//选择第二种商品
end
4'd4:begin
led = 4'b0100;//选择第三种商品
end
4'd5:begin
led = 4'b1000;//选择第四种商品
end
4'd6: begin
led = led_running[3:0];//购买成功找零不找零,流水灯
end
4'd7:begin
led = led_flash;//取消订单,闪烁
end
default : led = 4'b0000;
endcase
end
endmodule
位选
module sel_drive(
input wire clk ,
input wire rst_n ,
input wire [6:0] price_put , //投入的钱
input wire [6:0] price_need , //商品的价格
input wire [6:0] price_out , //找零的钱
output reg [5:0] sel //数码管位选
);
//状态
localparam state0 = 3'd0;
localparam state1 = 3'd1;
localparam state2 = 3'd2;
localparam state3 = 3'd3;
localparam state4 = 3'd4;
localparam state5 = 3'd5;
parameter MAX_NUM = 1_000;//计数器最大计数值 刷新频率20微秒
reg [2 :0] current_state;
reg [2 :0] next_state;
reg [20:0] cnt; //时钟分频计数器
reg flag;
//计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
cnt <= 0;
end
else if(cnt == 0)begin//一轮计数完毕
flag <= 1'b1;
cnt <= 1;
end
else begin
flag <= 1'b0;
cnt <= (cnt + 1'b1) % MAX_NUM;//循环+1
end
end
// 状态跳转
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
current_state <= state0;
end
else if(flag) begin
current_state <= next_state;
end
else
current_state <= current_state;
end
//状态判断
always @(*) begin
if(!rst_n) begin
next_state <= state0;
end
else if(flag) begin
case(current_state)
state0: begin
next_state <= state1;
end
state1: begin
next_state <= state2;
end
state2: begin
next_state <= state3;
end
state3: begin
next_state <= state4;
end
state4: begin
next_state <= state5;
end
state5: begin
next_state <= state0;
end
default:begin
next_state <= state0;
end
endcase
end
else begin
next_state <= next_state;
end
end
//根据value的值确定需要亮几位
always@(current_state) begin
case (current_state)
state0: begin //右第一位数码管显示
sel <= 6'b011111;
end
state1: begin //右第二位数码管显示
if (price_out >=1) begin
sel <= 6'b101111;
end
else begin
sel <= 6'b111111;
end
end
state2: begin //右第三位数码管显示
if (price_need >=0) begin
sel <= 6'b110111;
end
else begin
sel <= 6'b111111;
end
end
state3: begin //右第四位数码管显示
if (price_need >=1) begin
sel <= 6'b111011;
end
else begin
sel <= 6'b111111;
end
end
state4: begin //右第五位数码管显示
if (price_put >=0) begin
sel <= 6'b111101;
end
else begin
sel <= 6'b111111;
end
end
state5: begin //右第五位数码管显示
if (price_put >=1) begin
sel <= 6'b111110;
end
else begin
sel <= 6'b111111;
end
end
default:begin
sel <= 6'b111111;
end
endcase
end
endmodule
段选
module seg_drive(
input wire clk ,
input wire rst_n , //复位
input wire [5:0] sel , //数码管位选
input wire [6:0] price_put , //投入的钱
input wire [6:0] price_need , //商品的价格
input wire [6:0] price_out , //找零的钱
output reg [7:0] seg //数码管段选
);
reg [3:0] num;
always@(*) begin
case(sel)
//投入的钱
6'b111_110: num = (price_put % 100) / 10; //十位
6'b111_101: num = price_put % 10; //个位
//需要的钱
6'b111_011: num = (price_need % 100) / 10; //十位
6'b110_111: num = price_need % 10; //个位
//找回的钱
6'b101_111: num = (price_out % 100) / 10; //十位
6'b011_111: num = price_out % 10; //个位
default:num = 4'd0;
endcase
end
always @ (*) begin
//需要显示小数点
if(!sel[1] || !sel[3] || !sel[5]) begin
case(num)
4'd0: seg = 8'b1100_0000; //匹配到后参考共阳极真值表
4'd1: seg = 8'b1111_1001;
4'd2: seg = 8'b1010_0100;
4'd3: seg = 8'b1011_0000;
4'd4: seg = 8'b1001_1001;
4'd5: seg = 8'b1001_0010;
4'd6: seg = 8'b1000_0010;
4'd7: seg = 8'b1111_1000;
4'd8: seg = 8'b1000_0000;
4'd9: seg = 8'b1001_0000;
default : seg = 8'b1100_0000;
endcase
end
else begin
case(num)
4'd0: seg = 8'b0100_0000; //匹配到后参考共阳极真值表
4'd1: seg = 8'b0111_1001;
4'd2: seg = 8'b0010_0100;
4'd3: seg = 8'b0011_0000;
4'd4: seg = 8'b0001_1001;
4'd5: seg = 8'b0001_0010;
4'd6: seg = 8'b0000_0010;
4'd7: seg = 8'b0111_1000;
4'd8: seg = 8'b0000_0000;
4'd9: seg = 8'b0001_0000;
default : seg = 8'b0100_0000;
endcase
end
end
endmodule
module freq_select
(
input wire clk,
input wire rst_n,
output reg status, //蜂鸣器1/0
output reg [2:0] spec_flag//音符
);
parameter NOTE_NUM=6'd50; //50个音符
//中
parameter DO = 20'd95600 ;//1
parameter RE = 20'd83150 ;//2
parameter MI = 20'd75850 ;//3
parameter FA = 20'd71600 ;//4
parameter SO = 20'd63750 ;//5
parameter LA = 20'd56800 ;//6
parameter XI = 20'd50600 ;//7
//高
parameter HDO = 16'd47750 ;//1
parameter HRE = 16'd42250 ;//2
parameter HMI = 16'd37900 ;//3
parameter HFA = 16'd37550 ;//4
parameter HSO = 16'd31850 ;//5
parameter HLA = 16'd28400 ;//6
parameter HXI = 16'd25400 ;//7
//低
parameter LDO = 20'd190800 ;//1
parameter LRE = 20'd170050 ;//2
parameter LMI = 20'd151500 ;//3
parameter LFA = 20'd143250 ;//4
parameter LSO = 20'd127550 ;//5
parameter LLA = 20'd113600 ;//6
parameter LXI = 20'd101200 ;//7
reg [25:0] inte_cnt; //300ms,间隔
reg [19:0] note_cnt; //音符持续时间计时
reg [5:0] spec_cnt; //音谱个数计数
reg [19:0] spec_data; //音符频率
reg [25:0] continue_time; //持续时间
reg [27:0] blank_time; //空白时间
wire[18:0] duty_data; //占空比数据
wire end_note; //音符结束时间
wire end_spectrum; //音谱结束时间
//音符之间间隔时间计数
always@(posedge clk,negedge rst_n)begin
if(!rst_n)
inte_cnt<=26'b0;
else if(inte_cnt==continue_time+blank_time)
inte_cnt<=26'b0;
else begin
inte_cnt<=inte_cnt+1'b1;
end
end
//单个音符频率计数
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
note_cnt <= 20'd0;//20
end
else if(end_note)begin
note_cnt <= 20'd0;
end
else begin
note_cnt <= note_cnt + 1'd1;
end
end
//音符数计时
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
spec_cnt <= 6'd0;
end
else if(end_spectrum)begin
spec_cnt <= 6'd0;
end
else if(inte_cnt == continue_time+blank_time)begin
spec_cnt <= spec_cnt + 1'd1;
end
else begin
spec_cnt <= spec_cnt;
end
end
always@(posedge clk or negedge rst_n)begin
case(spec_cnt)
6'd0: continue_time<=26'd10_000_000;//你爱我
6'd1: continue_time<=26'd_000_000;
6'd2: continue_time<=26'd10_000_000;
6'd3: continue_time<=26'd20_000_000;
6'd4: continue_time<=26'd10_000_000;//我爱你蜜雪
6'd5: continue_time<=26'd10_000_000;
6'd6: continue_time<=26'd20_000_000;
6'd7: continue_time<=26'd10_000_000;
6'd8: continue_time<=26'd10_000_000;
6'd9: continue_time<=26'd10_000_000;//冰城甜蜜
6'd10:continue_time<=26'd15_000_000;
6'd11:continue_time<=26'd10_000_000;
6'd12:continue_time<=26'd9_000_000;
6'd13:continue_time<=26'd25_000_000;//蜜
6'd14:continue_time<=26'd10_000_000;//你爱我
6'd15:continue_time<=26'd10_000_000;
6'd16:continue_time<=26'd10_000_000;
6'd17:continue_time<=26'd20_000_000;
6'd18:continue_time<=26'd10_000_000;//我爱你蜜雪
6'd19:continue_time<=26'd10_000_000;
6'd20:continue_time<=26'd20_000_000;
6'd21:continue_time<=26'd10_000_000;
6'd22:continue_time<=26'd10_000_000;
6'd23:continue_time<=26'd10_000_000;//冰城甜蜜
6'd24:continue_time<=26'd15_000_000;
6'd25:continue_time<=26'd10_000_000;
6'd26:continue_time<=26'd9_000_000;
6'd27:continue_time<=26'd25_000_000;//蜜
6'd28:continue_time<=26'd20_000_000;//你爱
6'd29:continue_time<=26'd20_000_000;
6'd30:continue_time<=26'd20_000_000;//我呀
6'd31:continue_time<=26'd10_000_000;
6'd32:continue_time<=26'd10_000_000;
6'd33:continue_time<=26'd20_000_000;//我爱
6'd34:continue_time<=26'd10_000_000;
6'd35:continue_time<=26'd10_000_000;
6'd36:continue_time<=26'd50_000_000;//你
6'd37:continue_time<=26'd10_000_000;//你爱我
6'd38:continue_time<=26'd10_000_000;
6'd39:continue_time<=26'd10_000_000;
6'd40:continue_time<=26'd20_000_000;
6'd41:continue_time<=26'd10_000_000;//我爱你蜜雪
6'd42:continue_time<=26'd10_000_000;
6'd43:continue_time<=26'd20_000_000;
6'd44:continue_time<=26'd10_000_000;
6'd45:continue_time<=26'd10_000_000;
6'd46:continue_time<=26'd10_000_000;//冰城甜蜜
6'd47:continue_time<=26'd25_000_000;
6'd48:continue_time<=26'd10_000_000;
6'd49:continue_time<=26'd9_000_000;
6'd50:continue_time<=26'd25_000_000;//蜜
default: continue_time<=26'd24_000_000;
endcase
end
//空白时间
always@(spec_cnt)begin
case(spec_cnt)
6'd0: blank_time<=26'd2_000_000;//你爱我
6'd1: blank_time<=26'd2_000_000;
6'd2: blank_time<=26'd2_000_000;
6'd3: blank_time<=26'd5_000_000;
6'd4: blank_time<=26'd2_000_000; //我爱你蜜雪
6'd5: blank_time<=26'd2_000_000;
6'd6: blank_time<=26'd5_000_000;
6'd7: blank_time<=26'd2_000_000;
6'd8: blank_time<=26'd2_000_000;
6'd9: blank_time<=26'd2_000_000; //冰城甜蜜
6'd10:blank_time<=26'd5_000_000;
6'd11:blank_time<=26'd2_000_000;
6'd12:blank_time<=26'd2_000_000;
6'd13:blank_time<=26'd5_000_000;//蜜
6'd14:blank_time<=26'd2_000_000;//你爱我
6'd15:blank_time<=26'd2_000_000;
6'd16:blank_time<=26'd2_000_000;
6'd17:blank_time<=26'd2_000_000;
6'd18:blank_time<=26'd2_000_000;//我爱你蜜雪
6'd19:blank_time<=26'd2_000_000;
6'd20:blank_time<=26'd5_000_000;
6'd21:blank_time<=26'd2_000_000;
6'd22:blank_time<=26'd2_000_000;
6'd23:blank_time<=26'd2_000_000;//冰城甜蜜
6'd24:blank_time<=26'd5_000_000;
6'd25:blank_time<=26'd2_000_000;
6'd26:blank_time<=26'd2_000_000;
6'd27:blank_time<=26'd5_000_000;//蜜
6'd28:blank_time<=26'd2_000_000;//你爱
6'd29:blank_time<=26'd5_000_000;
6'd30:blank_time<=26'd2_000_000;//我呀
6'd31:blank_time<=26'd2_000_000;
6'd32:blank_time<=26'd5_000_000;
6'd33:blank_time<=26'd2_000_000;//我爱
6'd34:blank_time<=26'd2_000_000;
6'd35:blank_time<=26'd5_000_000;
6'd36:blank_time<=26'd10_000_000;//你
6'd37:blank_time<=26'd2_000_000;//你爱我
6'd38:blank_time<=26'd2_000_000;
6'd49:blank_time<=26'd2_000_000;
6'd40:blank_time<=26'd5_000_000;
6'd41:blank_time<=26'd2_000_000; //我爱你蜜雪
6'd42:blank_time<=26'd2_000_000;
6'd43:blank_time<=26'd5_000_000;
6'd44:blank_time<=26'd2_000_000;
6'd45:blank_time<=26'd2_000_000;
6'd46:blank_time<=26'd2_000_000; //冰城甜蜜
6'd47:blank_time<=26'd5_000_000;
6'd48:blank_time<=26'd2_000_000;
6'd49:blank_time<=26'd2_000_000;
6'd50:blank_time<=26'd5_000_000;//蜜
default:blank_time<=26'd1_000_000;
endcase
end
always@(posedge clk,negedge rst_n)begin
if(!rst_n)
spec_data<=DO;
else
case(spec_cnt)
6'd0: spec_data <= MI;//你爱我
6'd1: spec_data <= SO;
6'd2: spec_data <= SO;
6'd3: spec_data <= LA;
6'd4: spec_data <= SO; //我爱你蜜雪
6'd5: spec_data <= MI;
6'd6: spec_data <= DO;
6'd7: spec_data <= DO;
6'd8: spec_data <= RE;
6'd9: spec_data <= MI; //冰城甜蜜
6'd10:spec_data <= MI;
6'd11:spec_data <= RE;
6'd12:spec_data <= DO;
6'd13:spec_data <= RE; //蜜
6'd14:spec_data <= MI; //你爱我
6'd15:spec_data <= SO;
6'd16:spec_data <= SO;
6'd17:spec_data <= LA;
6'd18:spec_data <= SO; //我爱你蜜雪
6'd19:spec_data <= MI;
6'd20:spec_data <= DO;
6'd21:spec_data <= DO;
6'd22:spec_data <= RE;
6'd23:spec_data <= MI; //冰城甜蜜
6'd24:spec_data <= MI;
6'd25:spec_data <= RE;
6'd26:spec_data <= RE;
6'd27:spec_data <= DO; //蜜
6'd28:spec_data <= FA; //你爱
6'd29:spec_data <= FA;
6'd30:spec_data <= FA; //我呀
6'd31:spec_data <= LA;
6'd32:spec_data <= LA;
6'd33:spec_data <= SO; //我爱
6'd34:spec_data <= SO;
6'd35:spec_data <= MI;
6'd36:spec_data <= RE; //你
6'd37:spec_data <= MI; //你爱我
6'd38:spec_data <= SO;
6'd39:spec_data <= SO;
6'd40:spec_data <= LA;
6'd41:spec_data <= SO; //我爱你蜜雪
6'd42:spec_data <= MI;
6'd43:spec_data <= DO;
6'd44:spec_data <= DO;
6'd45:spec_data <= RE;
6'd46:spec_data <= MI; //冰城甜蜜
6'd47:spec_data <= MI;
6'd48:spec_data <= RE;
6'd49:spec_data <= RE;
6'd50:spec_data <= DO; //蜜
default:spec_data <= DO;
endcase
end
//当前音符spec_flag
always@(posedge clk,negedge rst_n)begin
if(!rst_n)
spec_flag<=3'd0;
else
case(spec_data)
DO:spec_flag<=3'd1;
RE:spec_flag<=3'd2;
MI:spec_flag<=3'd3;
FA:spec_flag<=3'd4;
SO:spec_flag<=3'd5;
LA:spec_flag<=3'd6;
XI:spec_flag<=3'd7;
default:spec_flag<=3'd0;
endcase
end
assign duty_data = spec_data >> 4;
assign end_note = note_cnt== spec_data; //spec_dara对音谱计数
assign end_spectrum = spec_cnt == NOTE_NUM && inte_cnt == continue_time;
//pwm信号产生模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
status <= 1'b0;
end
else
status <= (note_cnt >= duty_data) ? 1'b1 : 1'b0;
end
endmodule
module beep_drive (
input wire clk,
input wire rst_n,
input wire flag, //蜂鸣器开始鸣叫
input wire status,
output reg beep
);
parameter MAX_TIME = 24'd10_000_000; //鸣叫时间
parameter MAX_TIME_MUSIC = 28'd250_000_000; //音乐播放时间
reg [23:0] cnt_time; //计时
reg [27:0] cnt_time_music; //音乐播放计时器
reg flag_beep_time_out; // 计时是否结束
//音乐播放计时
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_time_music <= 28'd0;
end
else if(cnt_time_music < MAX_TIME_MUSIC) begin
cnt_time_music <= cnt_time_music + 28'd1;
end
else
cnt_time_music <= cnt_time_music;
end
//蜂鸣器输出
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_time <= 0;
beep <= 1;
flag_beep_time_out <= 1;
end
else if(!status && cnt_time_music < MAX_TIME_MUSIC) begin
beep <= 0;
end
else if(status && cnt_time_music < MAX_TIME_MUSIC) begin
beep <= 1;
end
else if(flag && flag_beep_time_out) begin //开始鸣叫
cnt_time <= MAX_TIME;
flag_beep_time_out <= 0;
end
else if(cnt_time >=1 && !flag_beep_time_out) begin
cnt_time <= cnt_time -24'd1;
beep <= 0;
end
else if(cnt_time == 0) begin//计时结束
beep <= 1;
flag_beep_time_out <= 1;
end
else begin
cnt_time <= cnt_time ;
beep <= beep;
flag_beep_time_out <= flag_beep_time_out;
end
end
endmodule
module automatic_machine_top(
input wire clk , //时钟 50M
input wire rst_n , //复位
input wire [2:0] key , //按键
output wire beep , //蜂鸣器
output wire [3:0] led , //售货机状态灯效
output wire [5:0] sel , //数码管位选
output wire [7:0] seg , //数码管段选
output wire [6:0] lan_led //音乐播放灯效
);
wire status; //音乐播放驱使蜂鸣器标志
wire [2:0] key_flag; //按键消抖完成标志
wire [2:0] key_value; //按键消抖完成后的按键值
wire [4:0] led_value; //售货机驱使led模块效果的值
wire [6:0] price_put; //售货机输出到数码管的投币值
wire [6:0] price_need; //售货机输出到数码管的商品价格
wire [6:0] price_out; //售货机输出到数码管的退款
wire [2:0] spec_flag; //音乐模块输出的音符,用于音乐灯效
//数码管位选模块
sel_drive inst_sel_drive(
.clk (clk) ,
.rst_n (rst_n) ,
.price_put (price_put) ,
.price_need (price_need) ,
.price_out (price_out) ,
.sel (sel)
);
//数码管段选模块
seg_drive inst_seg_drive(
.clk (clk) ,
.rst_n (rst_n) ,
.price_put (price_put) ,
.price_need (price_need) ,
.price_out (price_out) ,
.sel (sel) ,
.seg (seg)
);
//售货机模块
machine_drive inst_machine_drive(
.clk (clk) ,
.rst_n (rst_n) ,
.key ({key_value[2] && key_flag[2], key_value[1] && key_flag[1], key_value[0] && key_flag[0] }),
.led_value (led_value) ,
.price_put (price_put) ,
.price_need (price_need) ,
.price_out (price_out)
);
//led模块
led_drive inst_led(
.clk (clk) ,
.rst_n (rst_n) ,
.value (led_value) ,
.led (led)
);
//音乐模块
freq_select inst_freq_select
(
.clk (clk ) ,
.rst_n (rst_n ) ,
.status (status) ,
.spec_flag (spec_flag)
);
//音乐灯效模块
lanterns inst_lanterns(
.clk (clk ) ,
.rst_n (rst_n ) ,
.spec_flag (spec_flag) ,
.lan_led (lan_led)
);
//蜂鸣器
beep_drive inst_beep_drive(
.clk (clk) ,
.rst_n (rst_n) ,
.flag ((key_value[2] && key_flag[2]) || ( key_value[1] && key_flag[1]) || (key_value[0] && key_flag[0])),
.status (status) ,
.beep (beep)
);
//按键消抖
key_debounce inst_key_debounce_key0(
.clk (clk) ,
.rst_n (rst_n) ,
.key (key[0]) ,
.flag (key_flag[0]) ,
.key_value (key_value[0])
);
key_debounce inst_key_debounce_key1(
.clk (clk) ,
.rst_n (rst_n) ,
.key (key[1]) ,
.flag (key_flag[1]) ,
.key_value (key_value[1])
);
key_debounce inst_key_debounce_key2(
.clk (clk) ,
.rst_n (rst_n) ,
.key (key[2]) ,
.flag (key_flag[2]) ,
.key_value (key_value[2])
);
endmodule