EE_FPGA基础教程系列 --NO.3-- 玩转LED
[复制链接]
Table of Contents
1. 剧情回放 4
2. 玩转LED 4
2.1 硬件连接 4
2.2 程序编写 4
2.3 程序简介 5
2.4 换个玩法 7
2.5 点灯进阶 7
2.6 答疑解惑 9
3. 总结 10
详细内容:
1. 剧情回放
话说上次在建立的第一个EE_FPGA工程的时候,我们使用了一个点亮LED的范例,这里我们继续点亮LED这个话题。
2. 玩转LED
2.1 硬件连接
首先,我们得打开EE_FPGA的硬件手册,找到LED部分的原理图。如下图所示,我们看到,LED的右端是连着上拉电阻的VDD3.3V高电平,左端则连着FPGA的管脚pin_31-35。那我们想,假若我们的FPGA管脚输出的是高电平即3.3V,那么左右两端的电压相当,就不会有电流流过LED,也就不会发光了。如果FPGA管脚输出的是低电平,那就有电流从LED流过,LED就点亮了。原理就是这么简单。
2.2 程序编写
一般FPGA的核心电平是1.2V,管脚电平是3.3V,所以,要想点亮哪个LED,只要给连接的那个管脚赋0,不点亮赋1就可以了。
上次点亮四个LED的程序我们是这样写的:module led (
clk,rst_n,
led
);
input clk;
input rst_n;
output[3:0] led;
reg[3:0] led_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
led_r <= 4'b1111;
else
led_r <= 4'b0; // led是一端接上拉电阻,输入低电平点亮
end
assign led = led_r;
endmodule 复制代码
2.3 程序简介
这里我就讲点Verilog的经验之谈,有不妥的地方还请大家多指正。关于Verilog语法,还请大家找本书系统地学习下,这里无法一点一点细讲,要细讲的话书上是最合适的。推荐两本书,一本是夏宇闻老师的《Verilog 数字系统设计教程》 ,另外一本《设计与验证Verilog HDL》也很不错。
Verilog的一个程序模块,是以module 和 endmodule 开头和结尾的。跟在module后面的是这个模块的模块名。模块名后面的括号里列出了这个模块的输入输出管脚列表。再接下来我们要对这些管脚是输入input 还是输出 output 以及是多少位进行描述。再接下来差不多就是程序的正文了。
我们知道,数字电路就分为两种:组合逻辑电路和时序逻辑电路。其实我们写的FPGA程序它不叫程序,它一般叫做硬件描述语言,(这里我也是图方便混着叫了,嘿嘿)。说这个呢,想表达这么个意思:我们写了FPGA程序,然后Quartus帮我们综合到FPGA芯片中,在FPGA内部就是生成两种数字电路,组合逻辑电路和时序逻辑电路。
所以,我们写程序,也就两种逻辑电路的描述。凡是clock信号有关系的就是时序逻辑,跟clock没什么关系的就是组合逻辑(这是我妄自断言,还没有出处,有问题的话请拍砖)。这就引出了wire数据型和reg数据型的问题,还有阻塞赋值和非阻塞赋值。这些概念大家一定要在书里仔细阅读。
学习Verilog并不难,花个一天时间看下书,弄个程序写一下基本就会了。对于以前一直从事c语言,c++软件开发的同学,首先得理解这样的问题。我们所写的Verilog程序,并非软件程序,它是用来描述硬件电路的!module led (
clk,rst_n,
led
);
input clk;
input rst_n;
output[3:0] led;
reg[3:0] led_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
led_r <= 4'b1111;
else
led_r <= 4'b0; // led是一端接上拉电阻,输入低电平点亮
end
wire[3:0] led;
assign led = led_r;
endmodule 复制代码 2.4 换个玩法
四个LED亮着是不是没什么好玩的呢?我们可不可以这样的,让四个灯轮流亮起来,就是第一个灯亮了之后熄灭,然后第二个亮再熄灭,接着第三个,第四个。这个流水灯,或者叫跑马灯,大家在做单片机实验的时候估计都做过吧。这里我们只要做一个移位寄存器就可以了。我们修改下面这段程序reg[3:0] led_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
led_r <= 4'b0111;
else
led_r <= {led_r[0],led_r[3:1]};
end 复制代码 {}是Verilog里的拼接运算符,这句{led_r[0],led_r[3:1]}的意思就是把上次led_r的第0位移到最高位,然后高3位往右移。也就是说,最初,我们给四个led的赋值是0111,那只有最高位的那个led灯是点亮的。第一次移位后变成1011,那就只有第二个是亮的;接着1101,第三个;1110,第四个;然后又变成第一个0111。
2.5 点灯进阶
按原理分析,我们应该是实现了流水灯这个功能了。下载到板子上看看效果吧。结果估计另大家都失望了,怎么回事?四个LED灯都还亮着。聪明的同学估计找到原因了,但我还想买个关子,我们先做下面的实验。module led (
clk,rst_n,
led
);
input clk;
input rst_n;
output[3:0] led;
reg[19:0] cnt;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 20'b0;
else
cnt <= cnt + 1'b1;
end
reg enable_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
enable_r <= 1'b0;
else if (cnt == 20'hfffff)
enable_r <= 1'b1;
else
enable_r <= 1'b0;
end
wire enable;
assign enable = enable_r;
reg[3:0] led_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
led_r <= 4'b1;
else if(enable)
led_r <= {led_r[0],led_r[3:1]};
else ;
end
wire[3:0] led;
assign led = led_r;
endmodule 复制代码 我又写了上面这段程序,程序中我增加了一个计数器cnt。开始,计数器是20位的,每个系统时钟的上升沿 posedge clk 执行一次计数器 +1 操作 cnt <= cnt + 1'b1 。 在计数器计数到满的时候,我定义了一个enable信号 else if (cnt == 20'hfffff) enable_r <= 1'b1;而其他时候,我都让这个 enable 信号维持低电平。(这里加一句,计数器到满的时候 cnt = 20'hfffff再 + 1 它就溢出了,回到20’h0)。reg[3:0] led_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
led_r <= 4'b1;
else if(enable)
led_r <= {led_r[0],led_r[3:1]};
else ;
end 复制代码 然后我们再看这段程序,我让它只有在 enable == 1’b1 的时候进行移位。大家把这段程序下载到板子上,有什么现象?四个led在闪动。
2.6 答疑解惑
好了,这个关子就卖到这里了。问题出在我们的眼睛骗了我们。百度一下,知道一个数据:人眼分辨事物的最高频率为24HZ,即反应一次要0.042s 。我们最初的移位是用的系统时钟clk,它是50MHZ,也就是说灯的每盏灯的亮灭是按50M的速度在移动,眼睛当然是完全看不出来啦,所以效果跟四个灯全亮是一样的。
后面我们用了一个计数器,使移位变成了2的20次方个CLK时间,即频率变成了50M/1048576 ,约等于50 HZ,这时我们的眼睛还是分辨不了,所以我们看到四个灯都在闪烁。
接下来,我们再把计数增大一点,把移位的频率降下来,就能看到流水灯的现象了。
这是个有趣的现象,相同原理的实验我在本科的时候做过,那时候也没太在意。最近发现安装在山地车轮胎上的“风火轮”也是用的这个原理哦!
3. 总结
这一篇我们讲了LED点亮的原理,以及计数器的设计,还有流水灯的设计。好玩吧!有兴趣的欢迎加入一起学习!
附:pdf文档:
EE_FPGA基础教程系列 -- 玩转LED.pdf
(750.21 KB, 下载次数: 2193)
[ 本帖最后由 xieqiang 于 2011-5-12 11:27 编辑 ]