触摸按键可分为四大类:电阻式、电容式、红外感应式、表面声波式
电容式触摸按键主要由按键IC部分和电容部分构成;按键IC用于将电容的变化转换为电信号;电容部分指的是由电容极板、地、隔离区等组成触摸按键的电容环境。
模块绘制
波形图绘制:按键未按下时保持高电平,按键按下保持低电平;按下时间与低电平保持时间相同,松开按键返回高电平
使用按键下降沿控制灯的点亮和熄灭
touch_ctrl_led.v
`timescale 1ns/1ns
module touch_ctrl_led
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire touch_key , //触摸按键信号
output reg led //led输出信号
);
//wire define
wire touch_flag ; //触摸使能信号
//reg define
reg touch_key_dly1 ; //touch_key延迟一个时钟信号
reg touch_key_dly2 ; //touch_key延迟两个时钟信号
//对touch_key信号延迟两个时钟周期用来产生触摸按键信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
touch_key_dly1 <= 1'b1;
touch_key_dly2 <= 1'b1;
end
else
begin
touch_key_dly1 <= touch_key;
touch_key_dly2 <= touch_key_dly1;
end
//根据触摸按键信号的下降沿判断触摸了触摸按键
assign touch_flag = ((touch_key_dly1 == 1'b0) && (touch_key_dly2 == 1'b1));
//根据触摸使能信号控制led状态
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led <= 1'b1;
else if(touch_flag == 1'b1)
led <= ~led;
else
led <= led;
endmodule
`timescale 1ns/1ns
module tb_touch_ctrl_led();
//wire define
wire led ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg touch_key ;
//sys_clk,sys_rst_n初始赋值,模拟触摸按键信号值
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
touch_key <= 1'b1 ;
#20
sys_rst_n <= 1'b1 ;
#200
touch_key <= 1'b0 ;
#2000
touch_key <= 1'b1 ;
#1000
touch_key <= 1'b0 ;
#3000
touch_key <= 1'b1 ;
end
//clk:产生时钟
always #10 sys_clk = ~sys_clk ;
//------------- touch_ctrl_led_inst -------------
touch_ctrl_led touch_ctrl_led_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低电平有效
.touch_key (touch_key ), //触摸按键信号
.led (led ) //led输出信号
);
endmodule
water_led.v
`timescale 1ns/1ns
module water_led
#(
parameter CNT_MAX = 25'd24_999_999
)
(
input wire sys_clk , //系统时钟50Mh
input wire sys_rst_n , //全局复位
output wire [3:0] led_out //输出控制led灯
);
//reg define
reg [24:0] cnt ;
reg cnt_flag ;
reg [3:0] led_out_reg ;
//cnt:计数器计数500ms
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt == CNT_MAX)
cnt <= 25'b0;
else
cnt <= cnt + 1'b1;
//cnt_flag:计数器计数满500ms标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == CNT_MAX - 1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
//led_out_reg:led循环流水
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out_reg <= 4'b0001;
else if(led_out_reg == 4'b1000 && cnt_flag == 1'b1)
led_out_reg <= 4'b0001;
else if(cnt_flag == 1'b1)
led_out_reg <= led_out_reg << 1'b1; //左移
assign led_out = ~led_out_reg;
endmodule
tb_water_led.v
`timescale 1ns/1ns
module tb_water_led();
//wire define
wire [3:0] led_out ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
//初始化系统时钟、全局复位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
//-------------------- water_led_inst --------------------
water_led
#(
.CNT_MAX (25'd24)
)
water_led_inst
(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.led_out (led_out ) //output [3:0] led_out
);
endmodule
LED灯低电平点亮,高电平熄灭。
breath_led.v
`timescale 1ns/1ns
module breath_led
#(
parameter CNT_1US_MAX = 6'd49 ,
parameter CNT_1MS_MAX = 10'd999 ,
parameter CNT_1S_MAX = 10'd999
)
(
input wire sys_clk , //系统时钟50Mhz
input wire sys_rst_n , //全局复位
output reg led_out //输出信号,控制led灯
);
//reg define
reg [5:0] cnt_1us ;
reg [9:0] cnt_1ms ;
reg [9:0] cnt_1s ;
reg cnt_1s_en ; //方便点亮到熄灭,熄灭到点亮的取反
//cnt_1us:1us计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1us <= 6'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1us <= 6'b0;
else
cnt_1us <= cnt_1us + 1'b1;
//cnt_1ms:1ms计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1ms <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1ms <= 10'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1ms <= cnt_1ms + 1'b1;
else
cnt_1ms <= cnt_1ms;
//cnt_1s:1s计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s <= 10'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1s <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1s <= cnt_1s + 1'b1;
else
cnt_1s <= cnt_1s;
//cnt_1s_en:1s计数器使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s_en <= 1'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1s_en <= ~cnt_1s_en;
else
cnt_1s_en <= cnt_1s_en;
//led_out:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b1;
else if((cnt_1s_en == 1'b0 && cnt_1ms <= cnt_1s) || (cnt_1s_en == 1'b1 && cnt_1ms > cnt_1s))
led_out <= 1'b0;
else
led_out <= 1'b1;
endmodule
tb_breath_led.v
`timescale 1ns/1ns
module tb_breath_led();
//wire define
wire led_out ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
//初始化系统时钟、全局复位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
//-------------------- breath_led_inst --------------------
breath_led
#(
.CNT_1US_MAX(6'd4 ),
.CNT_1MS_MAX(10'd9 ),
.CNT_1S_MAX (10'd9 )
)
breath_led_inst
(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.led_out (led_out ) //output led_out
);
endmodule
输入:投入1元硬币
输出:出可乐,不出可乐
状态:投入0元,投入1元,投入2元,投入3元
`timescale 1ns/1ns
module simple_fsm
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire pi_money , //投币方式可以为:不投币(0)、投1元(1)
output reg po_cola //po_cola为1时出可乐,po_cola为0时不出可乐
);
//parameter define
//只有三种状态,使用独热码
parameter IDLE = 3'b001;
parameter ONE = 3'b010;
parameter TWO = 3'b100;
//reg define
reg [2:0] state;
//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE; //任何情况下只要按复位就回到初始状态
else case(state)
IDLE : if(pi_money == 1'b1)//判断输入情况
state <= ONE;
else
state <= IDLE;
ONE : if(pi_money == 1'b1)
state <= TWO;
else
state <= ONE;
TWO : if(pi_money == 1'b1)
state <= IDLE;
else
state <= TWO;
//如果状态机跳转到编码的状态之外也回到初始状态
default : state <= IDLE;
endcase
//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_cola <= 1'b0;
else if((state == TWO) && (pi_money == 1'b1))
po_cola <= 1'b1;
else
po_cola <= 1'b0;
endmodule
tb_simple_fsm.v
`timescale 1ns/1ns
module tb_simple_fsm();
//reg define
reg sys_clk;
reg sys_rst_n;
reg pi_money;
//wire define
wire po_cola;
//初始化系统时钟、全局复位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
always #10 sys_clk = ~sys_clk;
//pi_money:产生输入随机数,模拟投币1元的情况
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_money <= 1'b0;
else
pi_money <= {$random} % 2; //取模求余数,产生非负随机数0、1
//------------------------------------------------------------
//将RTL模块中的内部信号引入到Testbench模块中进行观察
wire [2:0] state = simple_fsm_inst.state;
initial begin
$timeformat(-9, 0, "ns", 6);
$monitor("@time %t: pi_money=%b state=%b po_cola=%b", $time, pi_money, state, po_cola);
end
//------------------------------------------------------------
//------------------------simple_fsm_inst------------------------
simple_fsm simple_fsm_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.pi_money (pi_money ), //input pi_money
.po_cola (po_cola ) //output po_cola
);
endmodule
输入:0.5、1
输出:不出可乐/不找零、出可乐/不找零、出可乐/找零
状态:0、0.5、1、1.5、2、2.5、3
complex_fsm.v
`timescale 1ns/1ns
module complex_fsm
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire pi_money_one , //投币1元
input wire pi_money_half , //投币0.5元
output reg po_money , //po_money为1时表示找零
//po_money为0时表示不找零
output reg po_cola //po_cola为1时出可乐
//po_cola为0时不出可乐
);
//parameter define
//只有五种状态,使用独热码
parameter IDLE = 5'b00001;
parameter HALF = 5'b00010;
parameter ONE = 5'b00100;
parameter ONE_HALF = 5'b01000;
parameter TWO = 5'b10000;
//reg define
reg [4:0] state;
//wire define
wire [1:0] pi_money;
//pi_money:为了减少变量的个数,我们用位拼接把输入的两个1bit信号拼接成1个2bit信号
//投币方式可以为:不投币(00)、投0.5元(01)、投1元(10),每次只投一个币
assign pi_money = {pi_money_one, pi_money_half};
//第一段状态机,描述当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE; //任何情况下只要按复位就回到初始状态
else case(state)
IDLE : if(pi_money == 2'b01) //判断一种输入情况
state <= HALF;
else if(pi_money == 2'b10)//判断另一种输入情况
state <= ONE;
else
state <= IDLE;
HALF : if(pi_money == 2'b01)
state <= ONE;
else if(pi_money == 2'b10)
state <= ONE_HALF;
else
state <= HALF;
ONE : if(pi_money == 2'b01)
state <= ONE_HALF;
else if(pi_money == 2'b10)
state <= TWO;
else
state <= ONE;
ONE_HALF: if(pi_money == 2'b01)
state <= TWO;
else if(pi_money == 2'b10)
state <= IDLE;
else
state <= ONE_HALF;
TWO : if((pi_money == 2'b01) || (pi_money == 2'b10))
state <= IDLE;
else
state <= TWO;
//如果状态机跳转到编码的状态之外也回到初始状态
default : state <= IDLE;
endcase
//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_cola <= 1'b0;
else if((state==TWO && pi_money==2'b01) || (state==TWO && pi_money==2'b10) || (state==ONE_HALF && pi_money==2'b10))
po_cola <= 1'b1;
else
po_cola <= 1'b0;
//第二段状态机,描述当前状态state和输入pi_money如何影响po_money输出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_money <= 1'b0;
else if((state == TWO) && (pi_money == 2'b10)) //只有一种情况需要找零
po_money <= 1'b1;
else
po_money <= 1'b0;
endmodule
`timescale 1ns/1ns
module tb_complex_fsm();
//reg define
reg sys_clk;
reg sys_rst_n;
reg pi_money_one;
reg pi_money_half;
reg random_data_gen;
//wire define
wire po_cola;
wire po_money;
//初始化系统时钟、全局复位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
always #10 sys_clk = ~sys_clk;
//random_data_gen:产生非负随机数0、1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
random_data_gen <= 1'b0;
else
random_data_gen <= {$random} % 2;
//pi_money_one:模拟投入1元的情况
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_money_one <= 1'b0;
else
pi_money_one <= random_data_gen;
//pi_money_half:模拟投入0.5元的情况
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_money_half <= 1'b0;
else
//取反是因为一次只能投一个币,即pi_money_one和pi_money_half不能同时为1
pi_money_half <= ~random_data_gen;
//------------------------------------------------------------
//将RTL模块中的内部信号引入到Testbench模块中进行观察
wire [4:0] state = complex_fsm_inst.state;
wire [1:0] pi_money = complex_fsm_inst.pi_money;
initial begin
$timeformat(-9, 0, "ns", 6);
$monitor("@time %t: pi_money_one=%b pi_money_half=%b pi_money=%b state=%b po_cola=%b po_money=%b", $time, pi_money_one, pi_money_half, pi_money, state, po_cola, po_money);
end
//------------------------------------------------------------
//------------------------complex_fsm_inst------------------------
complex_fsm complex_fsm_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.pi_money_one (pi_money_one ), //input pi_money_one
.pi_money_half (pi_money_half ), //input pi_money_half
.po_cola (po_cola ), //output po_money
.po_money (po_money ) //output po_cola
);
endmodule
蜂鸣器按其结构可分为电磁式蜂鸣器和压电式蜂鸣器两种类型;蜂鸣器按其是否带有信号源又分为有源蜂鸣器和无源蜂鸣器。
有源蜂鸣器的内部装有集成电路,不需要音频驱动电路,只需要接通直流电压就能直接发出声响;而无源蜂鸣器只有外加音频驱动信号才能发出声响。
无源蜂鸣器与有源蜂鸣器不同,因其内部不带震荡源,所以其无法像有源蜂鸣器那样直接用直流信号驱动,这里需要使用PWM方法才能驱动其发生。
输入不同频率和占空比的PWM方法发出的声音是不同的,其中频率对音频有影响,占空比对音量大小有影响。
`timescale 1ns/1ns
module beep
#(
parameter TIME_500MS = 25'd24999999, //0.5s计数值
parameter DO = 18'd190839 , //"哆"音调分频计数值(频率262)
parameter RE = 18'd170067 , //"来"音调分频计数值(频率294)
parameter MI = 18'd151514 , //"咪"音调分频计数值(频率330)
parameter FA = 18'd143265 , //"发"音调分频计数值(频率349)
parameter SO = 18'd127550 , //"梭"音调分频计数值(频率392)
parameter LA = 18'd113635 , //"拉"音调分频计数值(频率440)
parameter XI = 18'd101214 //"西"音调分频计数值(频率494)
)
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //系统复位,低有效
output reg beep //输出蜂鸣器控制信号
);
//reg define
reg [24:0] cnt ; //0.5s计数器
reg [17:0] freq_cnt ; //音调计数器
reg [2:0] cnt_500ms ; //0.5s个数计数
reg [17:0] freq_data ; //音调分频计数值
//wire define
wire [16:0] duty_data ; //占空比计数值
//cnt:0.5s循环计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if(cnt == TIME_500MS )
cnt <= 25'd0;
else
cnt <= cnt + 1'b1;
//cnt_500ms:对500ms个数进行计数,每个音阶鸣叫时间0.5s,7个音节一循环
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_500ms <= 3'd0;
else if(cnt == TIME_500MS && cnt_500ms == 6)
cnt_500ms <= 3'd0;
else if(cnt == TIME_500MS)
cnt_500ms <= cnt_500ms + 1'b1;
else
cnt_500ms <= cnt_500ms; //时序逻辑中可以不写,组合逻辑一定要写
//freq_cnt:当计数到音阶计数值或跳转到下一音阶时,开始重新计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_cnt <= 18'd0;
else if(freq_cnt == freq_data || cnt == TIME_500MS)
freq_cnt <= 18'd0;
else
freq_cnt <= freq_cnt + 1'b1;
//不同时间鸣叫不同的音阶
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_data <= DO;
else case(cnt_500ms)
0: freq_data <= DO;
1: freq_data <= RE;
2: freq_data <= MI;
3: freq_data <= FA;
4: freq_data <= SO;
5: freq_data <= LA;
6: freq_data <= XI;
default: freq_data <= DO;
endcase
//设置50%占空比:音阶分频计数值的一半即为占空比的高电平数
assign duty_data = freq_data >> 1'b1;
//beep:输出蜂鸣器波形
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
beep <= 1'b0;
else if(freq_cnt >= duty_data)
beep <= 1'b1;
else
beep <= 1'b0;
endmodule
tb_beep.v
`timescale 1ns/1ns
module tb_beep();
//reg define
reg sys_clk ; //时钟
reg sys_rst_n ; //复位
//对时钟,复位信号赋初值
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#100
sys_rst_n <= 1'b1;
end
//产生时钟信号
always #10 sys_clk = ~sys_clk;
beep
#(
.TIME_500MS(25'd24999), //0.5s计数值
.DO (18'd190 ), //"哆"音调分频计数值(频率262)
.RE (18'd170 ), //"来"音调分频计数值(频率294)
.MI (18'd151 ), //"咪"音调分频计数值(频率330)
.FA (18'd143 ), //"发"音调分频计数值(频率349)
.SO (18'd127 ), //"梭"音调分频计数值(频率392)
.LA (18'd113 ), //"拉"音调分频计数值(频率440)
.XI (18'd101 ) //"西"音调分频计数值(频率494)
)
beep_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //系统复位,低有效
.beep (beep ) //输出蜂鸣器控制信号
);
endmodule