西南交通大学【数电课设】

                                                                  数字电子技术B

课程设计报告

(2023-2024第1学期)

                                                         课题名称:饮料自动贩卖机

 

一、系统总体设计方案

1.1功能描述

自动售饮料机,此饮料机仅提供一种饮料,每瓶售价1.5元,有按键,每按一次表示购买瓶数加一。按键之前确保复位信号处于高电平状态,否则按键无效。投币器只能接受1元和5角硬币,每按一次1元或5角按键表示1元或5角的个数加一。具有找零功能,当投币数和饮料数输入完毕后,拨动购买开关,如果钱数和购买瓶数合理则提示用户取走饮料和找零的指示灯亮起,并且会显示投币的钱数和找零数在数码管上。如果钱数和购买瓶数不合理,则代表出错的指示灯亮起,钱数全数退回,找零数为投币钱数。购买完毕后,将购买开关拨回,复位信号拨到低电平复位后拨回高电平,结束购买。

1.2 设计原理及设计思路

初始状态有一个复位信号,低电平有效。故执行流程时需把复位信号处于高电平。首先通过3个防抖动按键将元的个数、角的个数和饮料的个数通过计数器得到它们的二进制数形式。然后得这些二进制数的8421bcd码的形式(便于后面的计算),使用一个12位二进制数存储投币的钱和找零的钱,[11: 8] 存储十位,[7: 4] 存储个位,[3: 0] 存储小数,且全部用8421bcd码存储。得到投币的钱,再计算找零的钱,存储方式和存储投币的钱相同。购买开关关闭的时候,只有投币的钱数会正常显示,找零的钱部分显示的是00.0。当有了投币的钱和找零的钱后,拨动购买开关,首先进行判断,如果投币的钱少于购买饮料所需要的钱,则出错指示灯亮起,此次购买失败;如果投币的钱可以购买则提示用户取走饮料喝找零的指示灯亮起。然后会通过模6计数器、分频器、译码器等分配数据,得到位选信号和对应的段选信号,并且如果是个位的时候,小数点dp会亮起。从而在数码管上显示投币钱数和找零钱数。


1.3  verilog程序代码设计

curriculum_design模块

module curriculum_design(

input clk,  // 50MHz时钟信号

input yuan_en,  // 元 角 饮料的使能信号

input jiao_en,

input cola_num_en,

input buy,  // 买信号

input rst,  // 复位信号

output reg take_cola,   // 拿走饮料信号

output reg take_change, // 拿走找零信号

output reg error,       // 出错信号

output reg [7: 0] SEG,  // 数码管

output [6: 1] DIG       // 位选信号

);





wire [2: 0] yuan_num;   // 元数目的二进制大小

wire [2: 0] jiao_num;   // 角数目的二进制大小

wire [2: 0] cola_num;   // 饮料数目的二进制大小

wire [11: 0] change;    // 找零 每四位存储一个8421bcd码 分别表示十位个位小数位

wire [7: 0] change_yuan;// 存储找零的整数部分



wire clk_1kHz;      // 分频获得的1kHz时钟信号

wire [2: 0] SEL;        // 选择信号

wire [11: 0] money;     // 钱 每四位存储一个8421bcd码 分别表示十位个位小数位



wire [3: 0] yuan;       // 元的8421bcd码

wire [3: 0] jiao;       // 角的8421bcd码

wire [3: 0] cola;       // 饮料的8421bcd码



reg [3: 0] data;        // 存储显示的数据

reg [2: 0] jiao_to_yuan;  // 角转换成元并只取整数部分





get_num module1(clk, yuan_en, yuan_num, rst);  // 通过按钮防抖动获取元的二进制大小

get_num module2(clk, jiao_en, jiao_num, rst);  // 通过按钮防抖动获取角的二进制大小

get_num module3(clk, cola_num_en, cola_num, rst);  // 通过按钮防抖动获取饮料的二进制大小



get_1kHz get(clk, clk_1kHz);  // 得到分频的1kHz时钟信号





// 将角转换成元

always@(posedge clk or negedge rst)

begin

if(!rst)

jiao_to_yuan <= 3'b000;

else

jiao_to_yuan <= jiao_num>>1;

end



// 获取各个数据

to_bcd t1(yuan_num, yuan, clk);  // 获取元的8421bcd码

to_bcd t2(jiao_to_yuan, jiao, clk);  // 获取角的8421bcd码

to_money t5(jiao_num, yuan, jiao, money, clk);  // 获取总钱数

to_bcd t3(cola_num, cola, clk);  // 获取饮料的8421bcd码





to_change t4(cola_num, yuan_num, jiao_num, change[3:0], change_yuan);  // 获取找零整数数量及小数数量

_4bit_to_8bcd t6(change_yuan, change[11:4], clk);  //将找零整数部分的二进制转为bcd编码





// 初步判断是否购买出错

always@(posedge clk or negedge rst)

begin

if(!rst)

begin

error <= 1'b0;

take_cola <= 1'b0;

take_change <= 1'b0;

end

else

begin

// 确定购买

if(buy)

begin

// 如果总钱数少于价钱

if((2 * yuan_num + jiao_num) < (cola_num * 3))

begin

error <= 1'b1;

take_cola <= 1'b0;

take_change <= 1'b0;

end

else

// 总钱数大于等于价钱

begin

error <= 1'b0;

take_cola <= 1'b1;

take_change <= 1'b1;

end

end

else

// 未确定购买

begin

error <= 1'b0;

take_cola <= 1'b0;

take_change <= 1'b0;

end

end

end





counter6 c(clk_1kHz, SEL);





// 分配数据

always@(SEL)

begin

case(SEL)

3'b000:

begin

// 未确定购买 找零部分为0

if(!buy)

data <= 4'b0000;

else

begin

// 如果出错 找零为总钱数

if(error)

begin

data <= money[3: 0];  // 小数部分

end

else

data <= change[3: 0];

end

end

3'b001:

begin

if(!buy)

data <= 4'b0000;

else

begin

if(error)

begin

data <= money[7: 4];  // 个位部分

end

else

begin

data <= change[7: 4];

end

end

end

3'b010:

begin

if(!buy)

data <= 4'b0000;

else

begin

if(error)

begin

data <= money[11: 8];  // 十位部分

end

else

begin

data <= change[11: 8];

end

end

end

3'b011:

begin

data <= money[3: 0];  // 小数部分

end

3'b100:

begin

data <= money[7: 4];  // 个位部分

end

3'b101:

begin

data <= money[11: 8];  // 十位部分

end

endcase

end





// 将数据译成数码管数据

always@(data)

begin

case(data)

4'b0000: SEG <= 8'b1111_1100;  // 0

4'b0001: SEG <= 8'b0110_0000;  // 1

4'b0010: SEG <= 8'b1101_1010;  // 2

4'b0011: SEG <= 8'b1111_0010;  // 3

4'b0100: SEG <= 8'b0110_0110;  // 4

4'b0101: SEG <= 8'b1011_0110;  // 5

4'b0110: SEG <= 8'b1011_1110;  // 6

4'b0111: SEG <= 8'b1110_0000;  // 7

4'b1000: SEG <= 8'b1111_1110;  // 8

4'b1001: SEG <= 8'b1111_0110;  // 9

default: SEG <= 8'bx;

endcase

// 如果是总钱和找零的个位 小数点应亮起

if(SEL == 3'b001 || SEL == 3'b100)

SEG[0] <= 1'b1;

end





// 确定位选

choose ch(SEL, DIG);

endmodule

_4bit_to_8bcd模块

module _4bit_to_8bcd(

input [7: 0] bin_in,

output reg [7: 0] bcd_out,

input clk

);



reg [3: 0] ones;

reg [3: 0] tens;



integer i;



always@(*)

begin

ones = 4'd0;

tens = 4'd0;



for(i = 7; i >= 0; i = i - 1)

begin

if(ones >= 4'd5)  ones = ones + 4'd3;

if(tens >= 4'd5)  tens = tens + 4'd3;



tens = {tens[2: 0] , ones[3]};

ones = {ones[2: 0], bin_in[i]};

end



bcd_out = {tens, ones};

end

endmodule

choose模块

module choose(SEL, DIG);  // 确定位选

input [2: 0] SEL;

output reg [6: 1] DIG;

always@(SEL)

begin

case(SEL)

3'b000: DIG <= 6'b000001;

3'b001: DIG <= 6'b000010;

3'b010: DIG <= 6'b000100;

3'b011: DIG <= 6'b001000;

3'b100: DIG <= 6'b010000;

3'b101: DIG <= 6'b100000;

default: DIG <= 6'bx;

endcase

end

endmodule

counter6模块

module counter6(clk_1kHz, SEL);  // 模6计数器

input clk_1kHz;

output reg [2: 0] SEL;

reg [2: 0] counter;

always@(posedge clk_1kHz)

begin

if(counter < 3'b101)

begin

counter <= counter + 1'b1;

end

else

begin

counter <= 3'b000;

end

SEL <= counter;

end

endmodule

get_1kHz模块

module get_1kHz(clk, clk_1kHz);  // 分频

input clk;

output reg clk_1kHz;



reg [15: 0] counter;



parameter num = 50000;  // 1kHz一个周期有50MHz50000个周期



always@(posedge clk)

begin

if(counter < num / 2)

begin

counter <= counter + 16'b1;

clk_1kHz <= 1'b0;

end

else if(counter < num - 1)

begin

counter <= counter + 16'b1;

clk_1kHz <= 1'b1;

end

else

counter <= 16'b0;

end

endmodule

get_num模块

module get_num(clk, key_in, num, rst_n);  // 获取个数



input clk;  // 时钟50MHz

input key_in;  // 按键输入

input rst_n;

reg key_state;  // 按键状态 高电平为按下 低电平为未按下状态

reg key_flag;  // 经消抖后可确认的按下动作

output reg [2: 0] num = 0;





parameter   UP = 2'b00,  // 松开稳定时

            FILTER0 = 2'b01,  // 按下毛刺时

            DOWN = 2'b10,  // 按下稳定时

            FILTER1 = 2'b11;  // 松开毛刺时







// 20ms计数器

reg en_counter;  // 计数使能

reg [19:0] cnt;  // 计数

reg cnt_full;  // 计数器是否满



//只有当计数使能为高电平的时候 计数器才会计数

always@(posedge clk or negedge rst_n)

begin

if(!rst_n)

cnt <= 0;

else if(en_counter)

cnt <= cnt + 1'b1;

else

cnt <= 0;

end



// 计数满信号

always@(posedge clk or negedge rst_n)  // 当clk接50MHz的信号时 相当于clk在1s内进行了50M次计数 相邻上升沿相间(1/50M)s

begin

if(!rst_n)

cnt_full <= 1'b0;

else if(cnt == 20'd1000000)

cnt_full <= 1'b1;

else

cnt_full <= 1'b0;

end







// 判断边沿模块

reg key_tmp0, key_tmp1;  // 将输入信号寄存一个节拍 分别按键上升沿和下降沿的产生



always@(posedge clk or negedge rst_n)

begin

if(!rst_n)

begin

key_tmp0 <= 1'b1;

key_tmp1 <= 1'b1;

end

else

begin

key_tmp0 <= key_in;  // key_in按键输入

key_tmp1 <= key_tmp0;

end

end



wire pedge, nedge;

assign nedge = (!key_tmp0) & key_tmp1;  // 下降沿(下一clk时为0,上一clk时为1)

assign pedge = key_tmp0 & (!key_tmp1);  // 上升沿(下一clk时为1,上一clk时为0)







// 状态机模块

reg [1:0] state;  // 所有状态由2位二进制数存储



always@(posedge clk or negedge rst_n)

begin

if(!rst_n)

begin

state <= UP;

en_counter <= 1'b0;  // 计数器归零

key_state <= 1'b0;  // 按键未按下

key_flag <= 1'b0;

end

else

begin

case(state)

UP:  // 低电平(松开稳定)

begin

key_flag <= 1'b0;  // 按键未按下,不计

key_state <= 1'b0;  // 按键松开状态

en_counter <= 1'b0;  // 计数器归零

if(pedge)  // 检测到上升沿 进入下一个状态同时打开计数器

begin

state <= FILTER0;

en_counter <= 1'b1;  // 计数器使能

end

else

state <= state; //保持目前状态    

end

FILTER0:  // 上升沿抖动(按下毛刺)

begin

if(cnt_full)  // 计数满 说明达到稳定状态 关闭计数器 进入下一个状态

begin

state <= DOWN;

en_counter <= 1'b0;  // 计数器归零

key_flag <= 1'b1;  // 按键可确认已按下

key_state <= 1'b1;  // 按键按下状态

end

else if(nedge)  // 检测到下将沿(毛刺) 跳回UP状态同时关闭计数器

begin

en_counter <= 1'b0; //计数器归零

state <= UP;

end

else

state <= state;  // 保持目前状态

end

DOWN:  // 高电平(按下稳定)

begin

key_flag <= 1'b0;   // 一个按键周期测到一次就行 现在可清零了(后面代码编写只在意其posedge)

if(nedge)  // 检测到下降沿 进入下一个状态同时打开计数器

begin

state <= FILTER1;

en_counter <= 1'b1;  // 计数器使能

end

      else

state <= state;  // 保持目前状态

end

         FILTER1:  // 下降沿抖动(松开毛刺)

begin

if(cnt_full)

begin

state <= UP;

key_state <= 1'b0;

end

else

begin

if(pedge)

begin

en_counter <= 1'b0;  // 计数器归零

state <= DOWN;  

end

else

state <= state;  // 保持目前状态

end

end

default:

state <= UP;

endcase

end

end





// 计数值输出与译码器模块

always@(posedge key_flag,negedge rst_n)  // key_flag:经消抖后可确认的按下动作

begin

if(!rst_n)

num <= 0;

else if(num < 3'b111)

num <= num + 1'b1;

end

endmodule

to_bcd模块

module to_bcd(

input [2:0] bin_in,

output reg [3:0] bcd_out,

input clk

    );  // 转为8421bcd码



reg [3:0] ones;



integer i;



always @(*) begin

ones = 4'd0;



for(i = 2; i >= 0; i = i - 1)

begin

if (ones >= 4'd5) ones = ones + 4'd3;



ones  = {ones[2:0],bin_in[i]};

end



bcd_out = {ones};

end

endmodule

to_change模块

module to_change(cola_num, yuan_num, jiao_num, change, change_yuan);  // 获取找零整数数量及小数数量

input [2: 0] cola_num; //饮料数量的二进制码

input [2: 0] yuan_num; //投入1元数量的二进制码

input [2: 0] jiao_num; //投入5角的二进制码





reg [7:0] change_temp;    //找零(多少个5角)

output reg [3: 0] change; //找零的小数位8421bcd码

output [7:0] change_yuan;





always@(yuan_num or jiao_num or cola_num)

begin

if(2 * yuan_num + jiao_num >= 3 * cola_num)

change_temp = 2 * yuan_num + jiao_num - 3 * cola_num;  //找多少个5角

else

change_temp = 2 * yuan_num + jiao_num;

change[3:0] = (change_temp[0]==1)?4'b0101:4'b0000;

change_temp = change_temp>>1;  //找多少个1元

end



assign change_yuan = change_temp;

endmodule

to_money模块

module to_money(jiao_num, yuan, jiao, money, clk);  // 获取money

input [2: 0] jiao_num;

input [3: 0] yuan;

input [3: 0] jiao;

input clk;

output reg [11: 0] money;



always@(posedge clk)

begin

// 如果角数目为偶数

if(jiao_num[0] == 0)

begin

money[3: 0] <= 4'b0000;

end

else

money[3: 0] <= 4'b0101;

// 如果产生进位

if(yuan[3: 0] + jiao[3: 0] > 4'b1001)

begin

money[11: 8] <= 4'b1010;

money[7: 4] <= yuan[3: 0] + jiao[3: 0] - 4'b1010;

end

else

begin

money[11: 8] <= 4'b0000;

money[7: 4] <= yuan[3: 0] + jiao[3: 0];

end

end

endmodule

1.4  状态转换图与状态转换表

状态转换图

状态转换表(1)

状态转换表(2)

 

1.5  状态编码方案

本程序一共有四个状态,分别用二进制编码00、01、10、11来代表四个状态。在时钟clk上升沿和复位信号rst_n下降沿触发的always语句块中,根据当前State值的不同来执行不同的程序逻辑:当rst_n处于低电平时,将state赋为UP,计数器归零且按键状态赋为高电平未按下状态;rst_n处于高电平,state为00也就是UP时,按键属于松开稳定状态且将计数器归零,按键状态赋为高电平未按下状态,当检查到上升沿时进入下一个状态同时打开计数器,否则保持当前状态不变。state为01也就是FILTER0时,按键处于上升沿抖动状态,当计数器记满后说明按键达到稳定状态并且关闭计数器,进入下一个状态同事计数器归零,按键状态赋为低电平按下状态,同时按键确认按下动作赋值为高电平;计数器没满且检测到下降沿毛刺时跳回UP状态同时关闭计数器,其他时刻保持当前状态不变。当按下稳定时,state为10也就是DOWN时,按键确认按下动作清零,如果检测到下降沿时进入下一个状态同时打开计时器,否则保持当前状态不变。state为11也就是FILTER1时,按键处于下降沿抖动的情况,如果计数器记满说明松开状态稳定,按键状态赋为高电平松开状态;如果计数器没计满且检测到上升沿毛刺时跳回DOWN状态且将计数器归零,否则保持当前状态不变。

1.6  输出表

输出信号

输出信号作用

take_cola

提醒用户拿取饮料

take_change

提醒用户拿取零钱

error

零钱不够,报错

SEG[7: 0]

用于数码管显示

DIG[6: 1]

数码管位选信号

1.7  总体电路图

 


二、调试步骤

仿真代码:

`timescale 10 ns/ 1 ns

module curriculum_design_vlg_tst();

reg buy;

reg clk;

reg cola_num_en;

reg jiao_en;

reg yuan_en;    

reg rst;                          

wire [6:1]  DIG;

wire [7:0]  SEG;

wire error;

wire take_change;

wire take_cola;

                     

curriculum_design i1 (

.DIG(DIG),

.SEG(SEG),

.buy(buy),

.rst(rst),

.clk(clk),

.cola_num_en(cola_num_en),

.error(error),

.jiao_en(jiao_en),

.take_change(take_change),

.take_cola(take_cola),

.yuan_en(yuan_en)

);

initial                                                

begin                                                                                             



clk = 1'b0;

yuan_en = 1'b0;

jiao_en = 1'b0;

cola_num_en = 1'b0;

buy = 1'b0;



end          

                                          

always                                                         

begin                                                   

#1

clk = ~clk;  //50MHz时钟                 

end                       



always

begin



rst = 1;

#1_000_000;





cola_num_en = 1; #3_000_000;  //选了三瓶饮料

cola_num_en = 0; #3_000_000;



cola_num_en = 1; #3_000_000;

cola_num_en = 0; #3_000_000;



cola_num_en = 1; #3_000_000;

cola_num_en = 0; #3_000_000;





yuan_en = 1; #3_000_000; //投入1元

yuan_en = 0; #3_000_000;  

yuan_en = 1; #3_000_000; //投入1元

yuan_en = 0; #3_000_000;  

yuan_en = 1; #3_000_000; //投入1元

yuan_en = 0; #3_000_000;  

yuan_en = 1; #3_000_000; //投入1元

yuan_en = 0; #3_000_000;  

yuan_en = 1; #3_000_000; //投入1元

yuan_en = 0; #3_000_000;  



  

jiao_en = 1; #3_000_000;   //投入5角

jiao_en = 0; #3_000_000;   

jiao_en = 1; #3_000_000;   //投入5角

jiao_en = 0; #3_000_000;   

jiao_en = 1; #3_000_000;   //投入5角

jiao_en = 0; #3_000_000;   

jiao_en = 1; #3_000_000;   //投入5角

jiao_en = 0; #3_000_000;   

jiao_en = 1; #3_000_000;   //投入5角

jiao_en = 0; #3_000_000;   





buy = 1; #3_000_000;       //按下购买

buy = 0; #3_000_000;



end

                                          

endmodule


测试数据波形及分析说明

波形图总览:(本波形图rst未呈现,始终保持rst = 1)

cola_num_en为选择饮料使能开关,每一个上升沿说明购买瓶数+1;

yuan_en为投1元硬币使能开关,每一个上升沿说明投币1元;

jiao_en为投5元硬币使能开关,每一个上升沿说明投币5角;

buy为购买按钮,高电平说明决定购买,下降沿说明购买结束;

change为找零值,用3位十六进制表示8421BCD码,当buy为1时在数码管上译码显示相应的找零值。

波形分析:

刚开始,按下三次cola_num_en,表示要购买3瓶饮料。

投币前,数码管上六位均显示数字“0”(SEG信号为11111100)。

投币1元后,数码管左边3位显示“01.0”(SEG信号为01100001),表示投币1元。由于还未按下buy,投币需要4.5才能找零,数码管右边三位(找零的三位数字)显示“000”。

投币2元后,数码管左边3位显示“02.0”(SEG信号为110111011),表示投币1元。由于还未按下buy,数码管右边三位(找零的三位数字)显示“000”。

不断投币,当投入5个1元和5个5角后,此时应当找零5+2.5-3*1.5=3元;

打开buy开关后,左侧三位数码管显示投币“07.5”,右侧三位数码管显示找零“03.0”。同时take_cola和take_change亮起,提醒用户拿走饮料和零钱;

随后关闭buy开关,表示购买结束,准备下次购买。(rst需置低电平重置数据)


、引脚分配表(电路中的信号名称->主板器件名称->引脚号PIN

信号名

主板器件

PIN

信号名

主板器件

PIN

clk

50MHz

PIN_90

SEG[4]

d

PIN_111

cola_num_en

Key8/SW8/LED16

PIN_43

SEG[5]

c

PIN_104

jiao_en

Key4/SW4/LED12

PIN_32

SEG[6]

b

PIN_100

yuan_en

Key5/SW5/LED13

PIN_42

SEG[7]

a

PIN_112

error

LED2

PIN_52

DIG[1]

SEG1

PIN_126

take_change

LED1

PIN_50

DIG[2]

SEG2

PIN_115

take_cola

LED0

PIN_46

DIG[3]

SEG3

PIN_125

buy

Key0/SW0/LED8

PIN_24

DIG[4]

SEG5

PIN_121

SEG[0]

dp

PIN_86

DIG[5]

SEG6

PIN_113

SEG[1]

g

PIN_103

DIG[6]

SEG7

PIN_120

SEG[2]

f

PIN_110

SEG[3]

e

PIN_106

五、结论

5.1  系统特点及存在的问题

特点:

此系统有操作简单、逻辑简单、功能实现模块化、可循环使用、代码有一定的健壮性等特点。用户在购买的时候只需通过五个按钮:元、角、饮料的计数按钮和购买按钮以及复位按钮即可实现购买饮料和找零的功能。实现功能的逻辑也较简单,便于后期的维护。此系统的多个功能采用模块化设计,层次分明,逻辑性强,便于后期功能的扩展和修改。此系统是可以面向多个用户循环使用的,在一个用户拨动购买按钮后,只需重新拨回购买按钮,并进行复位后。即可开始下一轮的购买。此系统的代码具有一定的健壮性,当一个用户拨动购买按钮后,如果再次按下元、角、饮料的计数按钮,这时元、角、饮料的个数是不会变的,即拨动购买按钮后,元、角、饮料的个数不会发生改变。且当拨回购买按钮后,拨动复位按钮给一个的复位信号,将上一个用户输入的元、角、饮料个数以及根据这些个数计算出来的投币钱数和找零钱数全部清零,从而实现下一个用户的功能正常实现。

问题:此系统元、角、饮料个数全部得在7个以内,超过7个便不会再计数。

5.2  功能扩展

在计数方面,还可以增加一个按钮实现对应元、角、饮料的计数减少,减到零的时候不能再减。

在个数方面,可以适当增加元、角、饮料个数的上限。

六、本系统使用说明

1.准备使用

确保购买按钮处于拨回状态,复位按钮处于高电平状态。

2.输入元、角、饮料个数阶段

按下对应的按钮,元、角、饮料个数会开始从0计数,到8后为到达最大值。数码管会左边会显示投币钱数,右边因为购买按钮还未拨动,找零分布显示的是00.0。

3.拨动购买按钮

如果投币钱数小于需要钱数,出错指示灯会亮起,此次购买失败。投币钱数全数返回。如果符合要求,则提示用户取走饮料喝找零的指示灯亮起,数码管找零部分也会显示找零钱数。

4.购买结束

拨回购买按钮,拨动复位按钮,使数据全部复位后拨回复位按钮。

  • 20
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
本实验要求以 FPGA 为硬件电路核心,设计一个 14 键单音电子琴。电子琴的每个琴键 对应一个七声音阶的简谱音符(用 C 调,可以占两个完整的八度,也可以占一个完整八度加 上前后各半个八度,具体请自行设计)。弹奏时按下任意一个琴键,电路产生对应音符的频 率信号(占空比 50%的方波)。将该方波信号接到扬声器(PIN_128)上就可以听到弹奏的乐声。 自动播放功能需要在代码中编写一个“数据表”(可以是寄存器数组,也可以用 case 语 句实现),数据表中存储着乐谱每个音符对应的分频系数。将这些分频系数以特定的时间间 隔(节拍,比如每隔 0.25 秒)输出到可控分频器,则分频器就可以产生所需音符的频率信 号。 任务 1 设计一个可控分频器,clk_in 为分频器时钟输入,Key[13:0]为琴键开关,clk_out 为分频器信号输出。在数码管上显示当前弹奏的简谱音符(高音、低音要能够区分,表示方 法请自行设计)。 仿真测试 配置好仿真参数,模拟琴键被逐个按下的状态,每个键按下的持续时间要保证至少能产 生 1 个同期的音符信号,为了便于观测,最好产生 2~3 个周期。在仿真结果中测量每个音符 的输出信号的频率(周期),并与理论值比对,误差在 1%以内为正确,否则应修改电路使工 作正常。 仿真时应当把音符的唱名(简谱符号)显示在波形图中。 实验测试 实验箱的扬声器没有连接到可供接线的 IO 端口,需要给电路增设一个输出(信号与 clk_out 相同)并锁定到空闲的 IO 端口上。将输出信号连接到实验箱的逻辑分析仪 CH0 上, 西南交通大学 电子技术实验室 观测并记录弹奏时每个音符的频率,并与理论值比对,误差在 1%以内为正确,否则应修改 电路使工作正常。 试着弹奏一首乐曲。 任务 2 在完成任务 1 的基础上,给电子琴增加自动播放功能,当电子琴切换到自动播 放模式时,能够自动循环播放一首编写在代码中的乐曲。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还有糕手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值