文章目录
在上一章节当中,我们实现了按键的消抖;在本章节当中,我们将使用触摸按键控制我们的 LED 灯。
本章节的主要内容分为两个部分:仍然是理论部分和实践部分。在理论部分我们将会对触摸按键的相关知识做一个讲解;在实战部分我们将通过实验工程,使用触摸按键控制我们的 LED 灯。
首先是理论部分的学习
1 理论学习
触摸按键
触摸按键我们经常见到。它可以分为四大类:电阻式、电容式、红外感应式和表面声波式。
电阻式触摸按键的工作原理就是:使用人体迫压电阻,致使电阻大小改变,电阻的改变会致使整个电路信号出现变化,从而达到控制的效果。但是它的缺点是耐用性比较差,现在已经很少使用。
我们的红外感应式触摸按键,它主要是采用红外扫描的方式,一般使用在比较恶劣的环境当中。
我们的表面声波式触摸按键,是利用声波扫描来识别我们的按键是否按下,它一般主要用于公共场合。
电容式触摸按键主要是为了弥补电阻式触摸按键耐用性较差而产生的。电容式触摸按键主要由按键 IC 部分和电容部分构成,按键 IC 用于将电容的变化转换为电信号,按键 IC 就是个芯片;电容部分指的是由电容极板、地、隔离区等组成的触摸按键的电容环境
当我们的电容式触摸按键没有被按下时,就是左边这种状态。电容式触摸按键它的感应原理是利用人体感应电容来检测按键是否按下,当按键没有被按下时,就是左边这种情况:按键与地之间存在一定的静态电容;当按键被按下时,就是右边这种情况,那么我们人体的寄生电容将耦合到这个静态电容上,使按键的最终电容变大,我们的按键 IC 会将电容的变化转化为电信号,那么电信号的变化超过了这个阈值就判定为按键按下。
我们的征途系列开发板使用的就是电容式触摸按键,在这个位置
那么以上部分就是理论部分的学习。
下面开始我们的实战演练。
2 实战演练
2.1 实验目标
在实战演练部分我们将通过实验工程,使用触摸按键控制我们的 LED 灯。我们的 LED 灯,初始状态为熄灭状态,当按下触摸按键时它点亮,再次按下触摸按键时它熄灭。
2.2 硬件资源
2.3 程序设计
那么了解了实验目标之后,我们开始程序的设计。
首先建立文件体系
文件体系建立完成之后打开 doc 文件夹,新建一个 Visio 文件,用来绘制我们的波形图和模块框图
2.3.1 模块框图
那么首先先来绘制模块框图
那么下面绘制输入信号和输出信号。那么输入信号有时钟信号和复位信号;输入信号当中除了时钟信号和复位信号之外,还有触摸按键传入的信号
因为我们要使用触摸按键控制 LED 灯,所以说应该有一路输出信号,输出到我们的 LED 灯
2.3.2 波形绘制
这样模块框图绘制完成,下面开始波形图的绘制
时钟信号和复位信号的波形绘制完成,下面开始绘制触摸按键信号。那么按键未被按下时是保持高电平,按键按下时是低电平;它的低电平保持时间与你按下的时间是相同的,你按下多长时间它的低电平就保持多长时间;当松开触摸按键之后它又回到它的高电平,当你再次按下时它又回到低电平
那么这样,触摸按键信号的波形绘制完成。
如果我们用这个信号去控制 LED 灯,不管是保持点亮状态还是熄灭状态,我们都需要持续作用在这个按键上,才能保证我们的 LED 灯点亮或者熄灭。这显然与我们的实验目标是不符合的,那么如何实现触摸一次触摸按键,我们的 LED 灯的状态切换一次呢?
我们在想我们是否可以使用这个下降沿,来控制我们的输出信号进行取反。当触摸按键没有被按下时,我们的输出信号保持原来的值,当检测到这个下降沿,进行取反,然后保持它的电平不变;到下一个下降沿再取反,然后继续保持它的电平不变。那么这样就可以实现:触摸一下点亮、触摸一下熄灭这种情况。
那么如何采集这个信号的下降沿呢?我们可以使用我们前面学到的寄存器。
我们先声明一个变量 touch_key_sync 在时钟信号的上升沿将输入的触摸按键信号直接赋值给它,这样做的目的是将触摸按键信号与我们的时钟同步
然后再声明一个变量 touch_key_beat
在时钟的上升沿将 touch_key_sync
的值赋值给 touch_key_beat
那么这样就产生了一个延迟一拍的一个效果
那么接下来我们就使用这两个变量来提取输入触摸按键信号的下降沿,怎么提取呢?
再声明一个变量,把它定义为一个脉冲信号。我们的 touch_key_flag
信号它的初值为低电平,当我们的变量 touch_key_sync
为低电平、变量 touch_key_beat
为高电平,让我们的脉冲信号 touch_key_flag
保持一个时钟周期的高电平;然后其他时刻保持低电平
那么这样我们的脉冲信号 touch_key_flag
表示的就是输入信号的下降沿。
下面,我们使用这个脉冲信号控制输出信号。我们的实验目标是使用触摸按键控制我们的 LED 灯,那么灯的初始状态应该是熄灭状态,所以说初值应该是高电平;当检测到我们的脉冲信号有效时,对它进行一个取反,就变为了低电平,这样就点亮了我们的 LED 灯;在下一个脉冲信号有效时,再次对它进行取反,它就变为了熄灭状态。那么这样就实现了触摸按键控制 LED 灯触摸一下点亮、触摸一下熄灭这样一个实验效果
2.3.3 代码编写
那么下面参照我们绘制的波形图编写我们的代码。
那么首先是模块开始、模块名称、端口列表,然后是模块结束
那么输入信号有三路,输出信号只有一路
这儿我们要声明三个变量:那么前两个变量都是时序逻辑,使用 always 语句;那么第三个变量是组合逻辑,因为这儿没有延迟一拍,所以说它的变量类型应该是 wire 型
那么下面开始赋值。
我们使用异步复位,当我们的复位信号有效时,对 touch_key_sync
、touch_key_beat
这两个变量赋一个初值,那么初值是高电平;当我们的复位信号无效时,将输入信号 touch_key
赋值给 touch_key_sync
将 touch_key_sync
的值赋值给 touch_key_beat
。变量 touch_key_sync
将输入信号 touch_key
同步到系统时钟下,那么变量 touch_key_beat
对变量 touch_key_sync
延迟了一个时钟周期
那么下面我们使用 assign 语句对 touch__key_flag
信号进行赋值。
当变量 touch_key_sync
为低电平、变量 touch_key_beat
为高电平,那么就让它拉高一个时钟周期,那么其他时刻让它保持低电平
那么下面就是我们的输出信号
那么输出信号的初值是高电平,当检测到脉冲信号有效时给它取反,那么其他时刻保持不变。
那么在这里我们完成了输出信号的赋值。
在本模块当中,我们还有一个新的知识点要介绍一下,就是我们下降沿采集的部分使用了一个新的知识点叫做边沿检测。
那边沿检测它主要的作用是能够准确的识别出单比特信号的上升沿或者下降沿,也就是说我们希望当上升沿或者下降沿到来时能够产生一个脉冲标志信号来告诉我们,上升沿或者下降沿到来了;我们可以根据这个脉冲标志信号作为后续电路功能的启动条件。
那么边沿检测实现上升沿和下降沿的采集就是要对输入的单比特信号进行打拍,我们这儿声明了两个变量:touch_key_sync
和 touch_key_beat
对输入的单比特信号 touch_key
进行了打拍。那么 touch_key_sync
是将我们的输入信号 touch_key
同步到我们的时钟下,那么 touch_key_beat
对 touch_key_sync
进行打一拍的一个操作。那么使用 touch_key_sync
和 touch_key_beat
作为一个条件,我们就可以采集到 touch_key
它的下降沿,同时也可以采集到 touch_key
它的上升沿。
我们这里使用的是组合逻辑,使用 assign
语句来采集到了我们的下降沿 touch_key_flag
。那么同样,使用 touch_key_sync
和 touch_key_beat
作为条件也可以进行上升沿的采集;那么如果要采集上升沿,这儿就需要这样编写:assign touch_key_flag = ((touch_key_sync==1'b1) && (touch_key_beat==1'b0))
也就是说,当我们的 touch_key_sync
为高电平、我们的 touch_key_beat
为低电平,那么这个位置就会采集到一个上升沿。
因为我们使用的是组合逻辑,所以说这个位置并没有延迟一拍的一个效果;那么如果是使用时序逻辑对上升沿和下降沿进行采集,会有一个延迟一拍的一个效果。我们通过波形图来看一下。
那么首先是采集上升沿,上升沿信号用 positiveedge 来表示(posedge和Verilog关键词冲突,后文将修改)。首先赋给它一个初值就是低电平;当我们的 touch_key_sync
为高电平,我们的 touch_key_beat
为低电平,就是我们信号的上升沿;如果说我们使用的是组合逻辑,在这个时钟周期内我们的上升沿标志信号应该保持一个时钟周期的高电平,但是我们使用的是时序逻辑,所以说应该有一个延迟一拍的一个效果,那么我们的高脉冲应该保持在这个时钟周期
那么下面就是下降沿标志信号。首先它的初值也是低电平;当我们的信号 touch_key_sync
为低电平、信号 touch_key_beat
为高电平,如果说是组合逻辑,那么在这个周期内我们的下降沿标志信号应该有一个高脉冲,但是我们使用的是时序逻辑,所以说要延迟一个时钟周期;那么在这个时钟周期内,我们的下降沿标志信号应该保持一个周期的高脉冲;那么其他时刻就保持低电平
那么这个波形图它表示的就是:使用时序逻辑来采集上升沿或下降沿
那么波形图绘制完成了,代码应该怎么编写呢?我们来看一下。
那么首先是时序逻辑,我们使用 always
语句。我们先来看一下上升沿标志信号,那么当我们的复位信号有效时给它一个初值就是低电平;当我们的信号 touch_key_sync
为高电平、信号 touch_key_beat
为低电平,让它保持一个周期的高脉冲;那么这儿就应该这样编写;那么其他时刻让它保持低电平
那么这样就使用时序逻辑来实现了上升沿的一个采集。
我们这个位置 ((touch_key_sync==1'b1)&&(touch_key_beat==1'b0))
使用的是逻辑与运算来实现的,那么上升沿的采集还有另一种方式就是逻辑或运算,它的代码应该怎么编写呢?
同样的,我们使用 always 语句,那么给它初值也是低电平;那么在这个位置我们使用逻辑或运算,当我们的 touch_key_sync
为低电平或者 touch_key_beat
为高电平时,让它继续保持低电平;那么其他时刻让它保持高电平
那么这种编写方式是使用的逻辑或运算来实现的上升沿的采集。
那么接下来就是下降沿的采集,那么下降沿的采集同样的是使用 always 语句;那么初值同样是低电平;当我们的信号 touch_key_sync
为低电平 touch_key_beat
为高电平,这儿就表示输入信号的下降沿,那么我们的下降沿标志信号就保持一个周期的高脉冲;那么这儿就应该这样编写;那么其他时刻让它保持低电平,就与这些位置相对应
那么这儿就使用时序逻辑实现了下降沿的采集,那么这儿使用的是逻辑与运算;同样我们也可以使用逻辑或运算来进行下降沿的采集,那么初值同样是给它低电平;那么这个位置我们使用逻辑或运算,当我们的 touch_key_sync
为高电平或者 touch_key_beat
为低电平时,让它保持低电平;那么其他时刻让它保持高电平
那么以上部分就是边沿检测相关内容的讲解
touch_ctrl_led.v
module touch_ctrl_led
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire touch_key ,
output reg led_out
);
reg touch_key_sync;
reg touch_key_beat;
wire touch_key_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
begin
touch_key_sync <= 1'b1;
touch_key_beat <= 1'b1;
end
else
begin
touch_key_sync <= touch_key;
touch_key_beat <= touch_key_sync;
end
assign touch_key_flag = ((touch_key_sync==1'b0)
&&(touch_key_beat==1'b1))
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
led_out <= 1'b1;
else if (touch_key_flag == 1'b1)
led_out <= ~led_out;
else
led_out <= led_out;
/* //使用时序逻辑采集 touch_key 的上升沿
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
positiveedge <= 1'b0;
else if ((touch_key_sync==1'b1)
&&(touch_key_beat==1'b0))
positiveedge <= 1'b1;
else
positiveedge <= 1'b0;
//使用时序逻辑采集 touch_key 的上升沿
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
positiveedge <= 1'b0;
else if ((touch_key_sync==1'b0)
||(touch_key_beat==1'b1))
positiveedge <= 1'b0;
else
positiveedge <= 1'b1;
//使用时序逻辑采集 touch_key 的下降沿
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
negativeedge <= 1'b0;
else if ((touch_key_sync==1'b0)
&&(touch_key_beat==1'b1))
negativeedge <= 1'b1;
else
negativeedge <= 1'b0;
//使用时序逻辑采集 touch_key 的下降沿
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
negativeedge <= 1'b0;
else if ((touch_key_sync==1'b1)
||(touch_key_beat==1'b0))
negativeedge <= 1'b0;
else
negativeedge <= 1'b1;
*/
endmodule
2.3.4 代码编译
那么这样代码编写完成,下面开始代码的编译。
我们回到桌面,新建一个新的实验工程
然后加载我们的代码,进行全编译;那么编译完成,点击 OK
2.4 逻辑仿真
2.4.1 仿真代码编写
那么下面开始仿真代码的编写
那么首先是时间参数,模块开始、模块名称、端口列表,然后是模块结束;然后是变量的声明,然后引出我们的输出信号
那么下面开始赋初值,模拟的按键信号初值为高电平
那么这儿就是模拟产生了触摸按键信号,那么延迟的时间长短大家也可以自己定义。
下面生成我们的时钟信号,然后实例化我们的模块
那么这样仿真代码编写完成,保存
tb_touch_ctrl_led.v
`timescale 1ns/1ns
module tb_touch_ctrl_led();
reg sys_clk;
reg sys_rst_n;
reg touch_key;
wire led_out;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
touch_key <= 1'b1;
#20
sys_rst_n <= 1'b1;
#205
touch_key <= 1'b0;
#803
touch_key <= 1'b1;
#1308
touch_key <= 1'b0;
#1505
touch_key <= 1'b1;
end
touch_ctrl_led touch_ctrl_led_inst
(
.sys_clk (sys_clk ),
.sys_rst_n(sys_rst_n),
.touch_key(touch_key),
.led_out (led_out )
);
endmodule
2.4.2 仿真代码编译
回到我们的实验工程,添加我们的仿真代码,然后全编译检查语法错误;编译通过点击 OK
进行仿真设置,然后开始仿真
那么仿真编译完成,打开 sim 窗口,添加模块波形;回到波形界面,全选、分组、消除前缀,Restart 清除波形,这里的时间参数先设为 10us,然后运行一次、查看全局视图
观看仿真波形,发现系统时钟没有振荡,我们回到仿真代码,添加系统时钟模拟生成部分代码,保存,重新仿真编译
2.4.5 波形对比
接下来将仿真波形与我们绘制的波形图进行对比,首先先看一下模拟产生的按键输入信号,这儿是没有问题的
然后看一下两个变量。变量 touch_key_sync
将我们的输入信号同步到系统时钟,那么 touch_key_beat
延迟它一个时钟周期,这儿是没问题的,和我们波形图是对应的;然后在输入信号的下降沿,也就是说 touch_key_sync
低电平、touch_key_beat
高电平时,我们的 touch_key_flag
信号有效,这儿也是没有问题的,和我们的波形图是一致的,我们的输出信号也是一致的
这样仿真波形与我们的绘制波形是一致的,仿真验证通过。
2.5 上板验证
2.5.1 管脚绑定
回到我们的实验工程,绑定管脚。led_out 与 N3 端口绑定,也就是我们的 D9 LED 灯;我们的时钟信号是 E1 端口;复位信号是 M15;那么触摸按键是 K11。重新编译,编译完成点击 OK
2.5.2 结果验证
按照下图所示连接下载器与电源,下载器的另一端连接电脑,然后给开发板上电
回到实验工程,点击 Programmer 这个位置打开下载界面,然后选择我们的 SOF 文件,点击 Start 进行程序的下载,程序下载完成
我们的 LED 灯 D9 一开始是处于熄灭状态,我们点击我们的触摸按键 LED 灯点亮,再次点击触摸按键 LED 灯熄灭
那么这样,实现了我们的实验效果,上板验证成功。
在本章节当中,我们实现了触摸按键控制我们的 LED 灯。这儿引入了一个新的方法就是上升沿与下降沿的采集,大家一定要掌握这种方法,在以后的工程中会经常的用到。
参考资料: