数字电路实验 电子节拍器 VHDL源代码
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
八、源代码及注释
--电子节拍器
--分频器组模块dividers源代码
--分频器组模块通过对50MHz的外部时钟进行分频,
--产生其他模块所需的各种时钟频率并输出,
--有60Hz、5000Hz、3000Hz、1500Hz、1000Hz,分别用5个进程实现。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity dividers is --定义实体dividers
port(
clk : in std_logic; --外部时钟输入
clk_out60: out std_logic; --60Hz频率输出
clk_out5000 : out std_logic; --5000Hz频率输出
clk_out3000 : out std_logic; --3000Hz频率输出
clk_out1500 : out std_logic; --1500Hz频率输出
clk_out1000 : out std_logic); --1000Hz频率输出
end dividers;
architecture div of dividers is
signal tmp5000 : integer range 0 to 4999; --分频得5000hz 的计数器大小
signal tmp3000 : integer range 0 to 8332; --3000hz 3x10^9=50x10^6x60 use 3000hz signal tmp1500 : integer range 0 to 16665; --1500hz
signal tmp1000 : integer range 0 to 49999; --1000hz
signal tmp60 : integer range 0 to 416666; --60hz
signal clktmp5000 : std_logic; --分频得5000hz 的时钟信号
signal clktmp3000 : std_logic;
signal clktmp1500 : std_logic;
signal clktmp1000 : std_logic;
signal clktmp60 : std_logic;
begin
p5000: process(clk) --分频得5000hz 的分频器进程
begin
if clk'event and clk='1' then --对50MHz进行10000分频得5000hz 的频率if tmp5000=4999 then --达到5000hz 的计数器大小时归零
tmp5000<=0;
clktmp5000<=not clktmp5000; --5000hz 的时钟信号翻转一次
else
tmp5000<=tmp5000+1; ----未达到5000hz 的计数器模值时计数器加一end if;
end if;
end process p5000; --分频得5000hz 的分频器进程结束
p1500: process(clk) --以下各分频器原理同上
begin
if clk'event and clk='1' then
if tmp1500=16665 then
tmp1500<=0;
clktmp1500<=not clktmp1500;
else
tmp1500<=tmp1500+1;
end if;
end if;
end process p1500;
p1000: process(clk)
begin
if clk'event and clk='1' then
if tmp1000=49999 then
tmp1000<=0;
clktmp1000<=not clktmp1000;
else
tmp1000<=tmp1000+1;
end if;
end if;
end process p1000;
p3000: process(clk)
begin
if clk'event and clk='1' then
if tmp3000=8332 then
tmp3000<=0;
clktmp3000<=not clktmp3000;
else
tmp3000<=tmp3000+1;
end if;
end if;
end process p3000;
p60: process(clk)
begin
if clk'event and clk='1' then
if tmp60=416666 then
tmp60<=0;
clktmp60<=not clktmp60;
else
tmp60<=tmp60+1;
end if;
end if;
end process p60;
clk_out5000 <= clktmp5000; --输出分频得到的5000hz 频率
clk_out3000 <= clktmp3000;
clk_out1500 <= clktmp1500;
clk_out1000 <= clktmp1000;
clk_out60 <= clktmp60;
end div; ———————————————————————————————————————
--电子节拍器
--速度设置模块set_speed源代码
--速度设置模块实现速度在40至120内连续可调
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity set_speed is
port(
clk60 : in std_logic; --分频器60hz时钟输入
vadd : in std_logic; --加按键输入
vdec : in std_logic; --减按键输入
clear : in std_logic; --置位键输入
beat_v : out std_logic_vector(6 downto 0); --速度输出
v_out0 : out std_logic_vector(3 downto 0); --速度个位二进制输出(对应0号数码管)v_out1 : out std_logic_vector(3 downto 0); --速度十位二进制输出(对应1号数码管)v_out2 : out std_logic_vector(3 downto 0)); --速度百位二进制输出(对应2号数码管)end set_speed;
architecture set of set_speed is
signal cn : integer range 40 to 120; --速度记录信号
signal change_a : std_logic; --加按键输入按下记录信号
signal change_d : std_logic; --减按键输入按下记录信号
signal count_add : integer:=0; --加按键输入按下时长记录信号
signal count_dec : integer:=0; --减按键输入按下时长记录信号
signal a10 : integer range 0 to 99; --速度减去百位的数的记录信号
signal a0 : integer range 0 to 9; --速度个位记录信号
signal a1 : integer range 0 to 9; --速度十位记录信号
signal a2 : integer range 0 to 1; --速度百位记录信号
begin
process(clk60,vadd,vdec)
begin
if clk60'event and clk60='1' then
if (vadd='1') then --若加键按下
change_a<='1'; --加按键输入按下记录信号置1
count_add<=count_add+1; --加按键输入按下时长记录信号加1
if (count_add>60 and cn<=115) then --加按键输入按下时长记录信号大于60
--即加按键按下时长大于1s(长按)
--若速度小于115
cn<=cn+5; --则速度连加5
count_add<=count_add-60; --同时加按键输入按下时长记录信号减60 elsif (count_add>60 and cn>115) then --若速度大于115
cn<=120; --速度只能增加到设置的上限120
count_add<=0; --加按键输入按下时长记录信号置0
change_a<='0'; --加按键输入按下记录信号置0
end if;
elsif (vdec='1') then --原理同加按键
change_d<='1';
count_dec<=count_dec+1;
if (count_dec>60 and cn>=45) then
cn<=cn-5;
count_dec<=count_dec-60;
elsif (count_dec>60 and cn<45) then
cn<=40;
count_dec<=0;
change_d<='0';
end if;
elsif(vadd='0' and vdec='0') then --检测到无键按下时
if (change_a='1' and count_add<60 and cn<120 ) then --若加键按下
--且加按键输入按下时长记录信号小于60(短按)
--且速度小于120
cn<=cn+1; --则速度加1
count_add<=0;
change_a<='0';
elsif (change_a='1' and count_add>=60 ) then --若加键长按
count_add<=0;
change_a<='0';
if (cn<=115 ) then --原理同上
cn<=cn+5;
elsif (cn>115 ) then
cn<=120;
end if;
elsif (change_d='1' and count_dec<60 and cn>40 ) then --原理同加按键cn<=cn-1;
count_dec<=0;
change_d<='0';
elsif (change_d='1' and count_dec>=60 ) then
count_dec<=0;
change_d<='0';
if (cn>=45 ) then
cn<=cn-5;
elsif (cn<45 ) then
cn<=40;
end if;
end if;
end if;
if clear ='1' then --实现置位功能
cn<=80;
count_add<=0;
count_dec<=0;
end if;
if cn>=100 then
a2<=1;
a10<=cn-100;
else
a2<=0;
a10<=cn;
end if;
a1<= a10/10;
a0<= a10-a1*10;
end if;
end process;
v_out2<=conv_std_logic_vector(a2,4); --将速度的百位(十进制)转换为二进制后输出v_out1<=conv_std_logic_vector(a1,4);
v_out0<=conv_std_logic_vector(a0,4);
beat_v<=conv_std_logic_vector(cn,7); --将速度(十进制)转换为二进制后输出
end set;
--电子节拍器
--节拍型选择模块set_rhythm源代码
--节拍型选择模块可设置的节拍有1/4、2/4、3/4、4/4、3/8、6/8可选,
--通过sel 按键选择,送到2个数码管显示,并把选好的节拍型输出到相关模块。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity set_rhythm is
port(
clear : in std_logic; --置位键输入
clk60 : in std_logic; --分频器60hz时钟输入
sel : in std_logic; --选择键输入
rhy_out4 : out std_logic_vector(3 downto 0); --节拍型每小节节拍数的二进制输出
rhy_out5 : out std_logic_vector(3 downto 0); --节拍型中一拍的音符型的二进制输出tempo : out std_logic_vector(2 downto 0)); --节拍型编码输出
end set_rhythm;
architecture sel_rhy of set_rhythm is
signal kind : integer range 1 to 6; --节拍型编码序号
signal change: std_logic; --选择键按下记录信号
signal count : integer range 0 to 120; --选择键按下时长记录信号
begin
process(sel,clear,clk60)
begin
if (clk60'event and clk60='1') then
if clear='1' then --实现置位功能
kind<=2;
elsif (sel='1') then --按键防抖,原理同加按键长、短按下处理程序if (count<5) then
change<='0';
count<=count+1;
else
change<='1';
count<=count+1;
end if;
elsif (sel='0') then
if (change='1' and count>=5 and count<=120 ) then
if kind<6 and kind>=1 then
kind<=kind+1;
else
kind<=1;
end if;
count<=0;
change<='0';
else
count<=0;
change<='0';
end if;
end if;
end if; --得到节拍型对应的编码序号
case kind is --由节拍型对应的编码序号得到该节拍型对应输出when 1 => rhy_out5 <= "0001"; --1
when 2 => rhy_out5 <= "0010"; --2
when 3 => rhy_out5 <= "0011"; --3
when 4 => rhy_out5 <= "0100"; --4
when 5 => rhy_out5 <= "0011"; --3
when 6 => rhy_out5 <= "0110"; --6
when others => rhy_out5 <= "1111";
end case;
case kind is
when 1 => rhy_out4 <= "0100"; --4
when 2 => rhy_out4 <= "0100";
when 3 => rhy_out4 <= "0100";
when 4 => rhy_out4 <= "0100";
when 5 => rhy_out4 <= "1000"; --8
when 6 => rhy_out4 <= "1000";
when others => rhy_out4 <= "1111";
end case;
case kind is
when 1 => tempo <= "001"; --1/4
when 2 => tempo <= "010"; --2/4
when 3 => tempo <= "011"; --3/4
when 4 => tempo <= "100"; --4/4
when 5 => tempo <= "101"; --3/8
when 6 => tempo <= "110"; --6/8
when others => tempo <= "000";
end case;
end process;
end sel_rhy;
--电子节拍器
--数码管显示模块seg5源代码
--5个数码管动态扫描显示
library ieee;
use ieee.std_logic_1164.all;
entity seg5 is
port(
clk1500: in std_logic; --分频器1500hz时钟输入(扫描频率)
show0 : in std_logic_vector(3 downto 0); --数码管组对应管的数据输入
show1 : in std_logic_vector(3 downto 0);
show2 : in std_logic_vector(3 downto 0);
show4 : in std_logic_vector(3 downto 0);
show5 : in std_logic_vector(3 downto 0);
show_out : out std_logic_vector(6 downto 0); --输出数据到数码管数据输入端
cat : out std_logic_vector(5 downto 0)); --数码管选通信号输出end seg5;
architecture show of seg5 is
signal i : integer range 0 to 4; --循环信号i
begin
process(clk1500,show0,show1,show2,show4,show5 )
begin
if clk1500'event and clk1500='1' then
if i=4 then -- i=4,归零,一次循环完成
i<=0;
cat<="011111"; --5号数码管亮
case show5 is --将5号数码管对应管的数据转换成数码管显示码后显示when "0001" => show_out <="0110000"; --1
when "0010" => show_out <="1101101"; --2
when "0011" => show_out <="1111001"; --3
when "0100" => show_out <="0110011"; --4
when "0110" => show_out <="1011111"; --6
when others => show_out <="0000000";--null
end case;
elsif i=3 then
i<=4; --循环信号加1,其他原理同上
cat<="101111";
case show4 is --注:show4只有两种情况
when "0100" => show_out <="0110011"; --4
when "1000" => show_out <="1111111"; --8
when others => show_out <="0000000";--null
end case;
elsif i=2 then
i<=3;
cat<="111011";
case show2 is
when "0000" => show_out <="1111110"; --0
when "0001" => show_out <="0110000"; --1
when others => show_out <="0000000";--null
end case;
elsif i=1 then
i<=2;
cat<="111101";
case show1 is
when "0000" => show_out <="1111110"; --0
when "0001" => show_out <="0110000"; --1
when "0010" => show_out <="1101101"; --2
when "0011" => show_out <="1111001"; --3
when "0100" => show_out <="0110011"; --4
when "0101" => show_out <="1011011"; --5
when "0110" => show_out <="1011111"; --6
when "0111" => show_out <="1110000"; --7
when "1000" => show_out <="1111111"; --8
when "1001" => show_out <="1111011"; --9
when others => show_out <="0000000";--null
end case;
elsif i=0 then
i<=1;
cat<="111110" ;
case show0 is
when "0000" => show_out <="1111110"; --0
when "0001" => show_out <="0110000"; --1
when "0010" => show_out <="1101101"; --2
when "0011" => show_out <="1111001"; --3
when "0100" => show_out <="0110011"; --4
when "0101" => show_out <="1011011"; --5
when "0110" => show_out <="1011111"; --6
when "0111" => show_out <="1110000"; --7
when "1000" => show_out <="1111111"; --8
when "1001" => show_out <="1111011"; --9
when others => show_out <="0000000";--null
end case;
end if;
end if;
end process;
end show;
--电子节拍器
--分频比计算模块calculator 源代码
--根据设置的节拍速度计算对应八分音符节拍频率分频器所用计数器模值v k 450008 。
library ieee;
use ieee.std_logic_1164.all;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
Use ieee.std_logic_arith.all;
entity calculator is
port(
beat_v: in std_logic_vector(6 downto 0); --设置的节拍速度输入
k8 : out std_logic_vector(11 downto 0)); --计算出的计数器模值
end calculator;
architecture cal of calculator is
signal a : integer range 40 to 120; --除数即设置的节拍速度输入的十进制数 signal k_out : integer; --计算出的计数器模值的十进制数
begin
process(beat_v)
begin
a<=conv_integer(beat_v);
k_out<=45000/a; --(3000*60)/(a*2*2) */8
--具体分析见3.5.1分频比计算模块calculator 的①设计思路 end process;
k8<=conv_std_logic_vector(k_out,12); --将计算结果(十进制)转换为二进制后输出 end cal;
--电子节拍器
--节拍分频模块dvd 源代码
--一个简单分频器,计数器的模值由外部输入。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity dvd is
port(
clk3000 : in std_logic; --分频器提供的3000hz 基频
k8 : in std_logic_vector(11 downto 0); --外部输入的计数器模值
clk8 : out std_logic); --分频输出(是设置的速度对应的八分音符节拍频率) end dvd;
architecture handle_beat of dvd is
signal tmp : std_logic_vector(11 downto 0) ; --分频计数信号
signal k : std_logic_vector(11 downto 0); --计数器模值减一的信号
signal clktmp : std_logic; --输出频率信号
begin
process(clk3000,k8) --原理同分频器组模块dividers
begin
if clk3000'event and clk3000='1' then
k<=k8-1;
if tmp=k then
tmp<="000000000000";
clktmp<=not clktmp;
else
tmp<=tmp+1;
end if;
clk8<=clktmp;
end if;
end process ;
end handle_beat;
--电子节拍器
--二分频模块div2源代码
--一个二分频器,对节拍分频模块dvd输出频率分频,得到四分音符节拍频率clk4。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity div2 is
port(
clk8 : in std_logic; --输入是节拍分频模块dvd输出的频率
clk4: out std_logic); --输出是二分频后四分音符节拍频率
end div2;
architecture div of div2 is
signal clktmp4 : std_logic; --输出频率信号
begin
process(clk8)
begin
if clk8'event and clk8='1'then
clktmp4<=not clktmp4;
clk4<=clktmp4;
end if;
end process;
end div;
--电子节拍器
--选择节拍频率模块choice源代码
--根据设置的节拍型选择八分或四分音符节拍频率,
--当选择的是1/4、2/4、3/4、4/4、时选择四分音符节拍频率,
--当选择的是3/8、6/8时选择八分音符节拍频率。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity choice is
port(
clk3000: in std_logic; --分频器提供的3000hz时钟
beat4: in std_logic; --四分音符节拍频率
beat8: in std_logic; --八分音符节拍频率
tempo : in std_logic_vector(2 downto 0); --节拍型选择模块set_rhythm输出的节拍型编号
beat_clk : out std_logic); --输出选择使用的频率
end choice;
architecture cho of choice is
begin
process(clk3000,tempo)
begin
if clk3000'event and clk3000='1' then
if tempo="101" or tempo="110" then --若设置的是3/8拍或6/8拍
beat_clk<=beat8; --则使用八分音符节拍频率
else --否则
beat_clk<=beat4; --则使用四分音符节拍频率
end if;
end if;
end process ;
end cho;
---------------------------------------------------------------------------------------------------------------------- --电子节拍器
--生成音调模块create源代码
--根据设置的节拍型控制不同的强弱拍循环,具体见表2
library ieee;
use ieee.std_logic_1164.all;
entity create is
port(
beat_clk :in std_logic; --选择出的四分音符或八分音符节拍频率
pause :in std_logic; --暂停键输入
tempo :in std_logic_vector(2 downto 0); --节拍型编码输入
tone : out std_logic_vector(1 downto 0)); 强弱编码输出
end create;
architecture create_beat of create is
signal c2 : integer range 1 to 2; --ci是循环信号,c2表示2/4拍的强弱编码输出signal c3 : integer range 1 to 3;
signal c4 : integer range 1 to 4;
signal c5 : integer range 1 to 3; --c5表示3/8拍的强弱编码输出
signal c6 : integer range 1 to 6;
begin
process(beat_clk,tempo,pause )
begin
if (beat_clk'event and beat_clk='1') then
if pause='0'or tempo="000" then --实现暂停功能
tone<="00";
elsif tempo="001" then --1/4拍
tone<="01";
elsif tempo="010" then --2/4拍有强→弱→强→弱的循环
if c2=1 then
tone<="01"; --强
c2<=2;
elsif c2=2 then
tone<="11"; --弱
c2<=1;
end if;
elsif tempo="011" then --3/4拍
if c3=1 then
tone<="01";
c3<=2;
elsif c3=2 then
tone<="11";
c3<=3;
elsif c3=3 then
tone<="11";
c3<=1;
end if;
elsif tempo="100" then --4/4拍
if c4=1 then
tone<="01";
c4<=2;
elsif c4=2 then
tone<="11";
c4<=3;
elsif c4=3 then
tone<="10"; --次强
c4<=4;
elsif c4=4 then
tone<="11";
c4<=1;
end if;
elsif tempo="101" then --3/8拍
if c5=1 then
tone<="01";
c5<=2;
elsif c5=2 then
tone<="11";
c5<=3;
elsif c5=3 then
tone<="11";
c5<=1;
end if;
elsif tempo="110" then --6/8拍
if c6=1 then
tone<="01";
c6<=2;
elsif c6=2 or c6=3 or c6=5 then
tone<="11";
c6<=c6+1;
elsif c6=4 then
tone<="10";
c6<=5;
elsif c6=6 then
tone<="11";
c6<=1;
end if;
end if;
end if;
end process;
end create_beat;
--电子节拍器
--节拍输出模块beat_out源代码
--根据节拍强弱码和节拍频率输出声音和灯光提示,具体见表3 library ieee;
use ieee.std_logic_1164.all;
entity beat_out is
port(
clk5000 : in std_logic; --分频器提供的5000Hz时钟
hclk : in std_logic; --分频器提供的高频3000Hz
mclk : in std_logic; --分频器提供的中频1500Hz
lclk : in std_logic; --分频器提供的低频1000Hz
beat_clk : in std_logic; --节拍频率
tone : in std_logic_vector(1 downto 0); --节拍强弱
buzzer:out std_logic; --蜂鸣器输出
light:out std_logic_vector(2 downto 0)); --三色发光二极管输出end beat_out;
architecture beat of beat_out is
begin
process(clk5000,beat_clk,tone)
begin
if clk5000'event and clk5000='1' then
if beat_clk='1' then --以节拍频率
if tone="01" then --强拍
buzzer<= hclk; --蜂鸣器响
light<="100"; --红灯亮
elsif tone="10" then --次强拍
buzzer<=mclk;
light<="010";
elsif tone="11" then --弱拍
buzzer<=lclk;
light<="001";
elsif tone="00" then --不响
buzzer<='0';
light<="000";
end if;
elsif beat_clk='0' then --节拍频率后半周期
buzzer<='0';
light<="000";
end if;
end if;
end process;
end beat;。