萌新的FPGA学习之水 一水到底
重读实验给我印象最深的2点是我们面对的设计需要使得时序自动切换
那么我们将切换时序的时钟装入另一个每隔0.5s变换一次的参数上
下附上代码
module key_led(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位,低电平有效
input [1:0] key , //按键
output reg [1:0] led //LED 灯
);
//parameter define
parameter CNT_MAX = 25'd2500_0000; //LED 灯闪烁频率
//reg define
reg [24:0] cnt; //计数器
reg led_flag; //LED 控制信号
//*****************************************************
//** main code
//*****************************************************
//计数器计时 0.5s
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 25'd0;
else if(cnt < (CNT_MAX - 25'd1))
cnt <= cnt + 25'd1;
else
cnt <= 25'd0;
end
//每隔 500ms 就更改 LED 的闪烁状态
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led_flag <= 1'b0;
else if(cnt == (CNT_MAX - 25'd1))
led_flag <= ~led_flag;
end
//根据按键的状态以及 LED 的闪烁状态来赋值 LED
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led <= 2'b00;
else case(key)
2'b10 : //如果按键 0 被按下,则两个 LED 交替闪烁
if(led_flag == 1'b0)
led <= 2'b01;
else
led <= 2'b10;
2'b01 : //如果按键 1 被按下,则两个 LED 同时亮灭交替
if(led_flag == 1'b0)
led <= 2'b11;
else
led <= 2'b00;
2'b11 : //如果两个按键都未被按下,则两个 LED 都保持常灭
led <= 2'b00;
default: ;
endcase
end
endmodule
下面讲述程序固化的方法
双击打开“ZYNQ 纯 PL 端固化脚本”文件夹
其中 bootbin.bif 是引导镜像文件,用
于指定使用哪些文件来生成 BOOT.bin(固化的镜像文件);
create_bootbin.bat 是一个批量处理文件,用于
生成 BOOT.bin,并将其烧写到 ZYNQ 开发板的 flash 芯片中;
zynq_fsbl.elf 是固化的初始化文件
看完了脚本文件夹中必要的基础文件后,我们来讲解下该如何使用这些文件完成 PL 端的程序固化。首先,我们使用文本编辑器(例如 Notepad++)打开 create_bootbin.bat 文件,如下图所示:
create_bootbin.bat 会调用 Vitis 的生成镜像(bootgen)功能和下载(program_flash)功能,所以我们需要给出这两个功能所在的安装路径,即找到 Vitis 安装路径下的 bin 文件夹,复制其路径,并替换图 19.6.3中红框内的路径,替换完成后保存即可。
接着我们找到工程的比特流文件,在没有人为干扰 Vivado 工程内部文件分布的情况下,其所在路径如下:
找到工程中的比特流文件后,我们将其复制到“ZYNQ 纯 PL 端固化脚本”文件夹中,如下图所示:
然后我们使用文本编辑器打开 bootbin.bif 文件,如下图所示:
bootbin.bif 文件中的第 4 行代码用于指定生成镜像文件时所需的比特流文件的来源,因为我们已经将所需的所有文件都存放在同一个文件夹下了,所以这里我们无需指定文件的绝对路径,直接输入比特流文件的完整文件名(包含尾缀)即可。修改完成后我们点击保存。接着我们连接开发板的电源和下载器,将领航者开发板上的启动模式开关 BOOT_CFG 拨到 JTAG 模式(即 1 和 2 都拨到 ON),如下图所示:
最后打开电源开关并双击运行 create_bootbin.bat
当运行界面显示“Bootimage generated successfully”时,说明 BOOT.bin 文件生成成功,此时我们可以
在“ZYNQ 纯 PL 端固化脚本”文件夹中看到生成的 BOOT.bin 文件,如图 19.6.9 所示。需要注意的是,
此时 create_bootbin.bat 还在进行程序的固化,所以不要关闭运行界面,如果在运行界面显示“Flash
Operation Successful”前在某一处卡顿的时间过长(一两分钟了都没有更新进度)的话,大家可以按一下
键盘上的回车键来手动让界面继续运行下去。
打开电源开关后,底板上的两个 PL LED 处于常灭状态。当我们按下 PL_KEY0 时,可以看到两个 PL
LED 交替闪烁;按下 PL_KEY1 时,可以看到两个 PL 的 LED 同时闪烁。此时就说明我们的程序成功固化
在 flash 器件中了。
小水之 按键蜂鸣器实验
key 值为高电平则按键被释放,key 值为低电平则按键被按下。所以我们消抖的过程就是滤除按键值保持时间小于 20ms 的值,那么我们需要做的就是在按键被按下或者被释放导致按键值产生变化时,从 20ms 开始倒计时,如果 20ms 的倒计时还没有完成按键值就再次产生变化,此时需要从头开始 20ms 倒计时,前一次导致按键值变化的操作视为无效操作,将该次变化视为按键抖动消除;如果计完 20ms 按键状态一直没有改变,说明此次按键被按下或者被释放是有效操作,将此次按键第二次的打拍的值赋值给按键消抖后的信号 key_filter。
记得一个转化 1s = 1000 ms
小水之 触摸按键控制LED 实验
本节的实验任务是使用触摸按键控制 LED 灯的亮灭,开发板上电后 LED 为点亮状态,手指触摸后LED 熄灭;当再次触摸时,LED 点亮。
小水之 呼吸灯实验
方便大家理解呼吸灯的实现过程,我们将呼吸灯两个组成部分开说,这里我们先详细说明呼吸灯由暗到亮的渐变过程(由亮到暗的过程与由亮到暗的过程刚好相反)。本实验中我们将由暗到亮的渐变过程定义为 2s,接下来我们将这 2s 分成 1000 份,就是由暗到亮的渐变过程中有 1000 个亮度过渡,每一份过渡时间为 2ms;接下开我们再将每一份过渡时间 2ms 均分为 1000份,每份时间为 2us,我们以 2us 为单位递减 2ms 中的高电平的占空比,随着高电平占空比的不断减少,led灯的亮度不断变暗。接下来我们定义一个计数器 cnt_2us,用来产生 2us 的时间;再定义一个计数 cnt_2ms,在 2us 的计数基础上进行 2ms 计数;最后定义一个计数器 cnt_2s,在 2ms 的计数基础上进行 2s 的计数。由上面的功能分析可知,呼吸灯实验中共有 5 个模块,分别是 2us 计数模块、2ms 计数模块、2s 计数模块、产生“呼吸”切换信号模块以及输出 led 灯信号模块。
接下来我们定义一个计数器 cnt_2us,用来产生 2us 的时间;再定义一个计数 cnt_2ms,在 2us 的计数基
础上进行 2ms 计数;最后定义一个计数器 cnt_2s,在 2ms 的计数基础上进行 2s 的计数。
20ns的100倍是 2us 我们以2us 作为最小间隔 1000倍就是2ms 再 1000倍就是 2s 2s是整个呼吸灯的时间
我们会惊奇的通过设计控制了 cnt_2ms 和 cnt_2s 都是1000个 我们这样就可以在后续的比较中这么设定
既然 1ms=1000us 那么 cnt_2us 计数从0到999然后把ms+1 诸如此类 接下来 0到999个ms 就计数1s
我们把cnt_2s分段成1000个阶段 既然cnt_ms也是从0转到999才使得cnt_2s +1 那么我们能不能这么设计 假定0是渐亮
那么一开始cnt_2s=0 cnt_2ms从0转到999 1转的时间是2us 那么这一轮 一次都不亮
接下来cnt_2s=1 接下来cnt_2ms 从0转到999 就会有1下的时间小于cnt_2s 那么就会亮2us
以此类推 当cnt_2s=999 cnt_2ms 从0到999 都会小于cnt_2s 就会一直常亮
module breath_led(
input sys_clk , //系统时钟 50MHz
input sys_rst_n , //系统复位,低电平有效
output reg led //LED 灯
);
//parameter define
parameter CNT_2US_MAX = 7'd100;
parameter CNT_2MS_MAX = 10'd1000;
parameter CNT_2S_MAX = 10'd1000;
//reg define
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms;
reg [9:0] cnt_2s;
reg inc_dec_flag; //亮度递增/递减 0:递增 1:递减
//*****************************************************
//** main code
//*****************************************************
//cnt_2us:计数 2us
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2us <= 7'b0;
else if(cnt_2us == (CNT_2US_MAX - 7'b1 ))
cnt_2us <= 7'b0;
else
cnt_2us <= cnt_2us + 7'b1;
end
//cnt_2ms:计数 2ms
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2ms <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2ms <= 10'b0;
else if(cnt_2us == CNT_2US_MAX - 7'b1)
cnt_2ms <= cnt_2ms + 10'b1;
else
cnt_2ms <= cnt_2ms;
end
//cnt_2s:计数 2s
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2s <= 10'b0;
else if(cnt_2s == (CNT_2S_MAX - 10'b1)
&& cnt_2ms == (CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= cnt_2s + 10'b1;
else
cnt_2s <= cnt_2s;
end
//inc_dec_flag 为低电平,led 灯由暗变亮,inc_dec_flag 为高电平,led 灯由亮变暗
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
inc_dec_flag <= 1'b0;
else if(cnt_2s == (CNT_2S_MAX - 10'b1)
&& cnt_2ms ==( CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
inc_dec_flag <= ~inc_dec_flag;
else
inc_dec_flag <= inc_dec_flag;
end
//led:输出信号连接到外部的 led 灯
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led <= 1'b0;
else if((inc_dec_flag == 1'b1 && cnt_2ms >= cnt_2s)
|| (inc_dec_flag == 1'b0 && cnt_2ms <= cnt_2s))
led <= 1'b1;
else
led <= 1'b0;
end
endmodule