【FPGA——Cyclone Ⅳ学习笔记】一.LED流水灯(EP4CE6F17C8)

一.原理图

此黑金开发板的LED灯为共阴级连接,即I/O口输出高电平为亮。
在这里插入图片描述
在这里插入图片描述

二.Verilog HDL代码及讲解

Verilog的语法和C语言有些相似,如果有C的基础则更容易理解

`timescale 1ns / 1ps
module led_test
(
	input           clk,           // 系统时钟50MHz
	input           rst_n,         // 复位,低电平有效
	output reg[3:0] led            // LED位控制
);

//定义定时器的计数器
reg [31:0]      counter;

//循环计数器:0~4s
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		counter <= 32'd0;                     //有复位低电平信号时,计数器清零
	else if (counter == 32'd199_999_999)      //4s的计数器 (50M*4-1=199999999)
		counter <= 32'd0;                     //到达4s后清零,重新计数
	else
		counter <= counter + 32'd1;             //计数器每1/50ns自加1
end

//LED控制
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		led <= 4'b0001;                     //又复位低电平信号时,led复位
	else if (counter == 32'd49_999_999)       //计数器计到1s时,LED1亮
		led <= 4'b0001;
	else if (counter == 32'd99_999_999)       //计数器计到2s时,LED2亮
		led <= 4'b0010;
	else if (counter == 32'd149_999_999)      //计数器计到3s时,LED3亮
		led <= 4'b0100;
	else if (counter == 32'd199_999_999)      //计数器计到4s时,LED4亮
		led <= 4'b1000;
end
endmodule

语法解释:

1.timescale 1ns / 1ps:此命令属于编译预处理,是用来说明跟在该命令后面的模块延时的时间单位和时间精度,此处可不用过多关注。

2.module + name (······):代码中的led_test即为模块名称,括号中的内容是端口定义和I/O说明
也可以把端口定义和I/O说明分开编写:

module led_test(clk,rst_n,led);
	input           clk,           
	input           rst_n,         
	output reg[3:0] led            

3.reg [31:0] timer:reg说明counter是寄存器类型,可用于保存数值,常用于时序逻辑电路;[31:0]说明counter是32位的位宽,此处的timer为定时器的计数器,因此要保存计数值。此外,verilog语法规定,对于没有声明类型的输入输出端口默认为wire类型,该类型相当于物理连线,常用于组合逻辑电路。

4.always @(posedge clk or negedge rst_n) begin······end:always过程块,有点类似单片机编程中的while(1){},用于执行具体的功能。括号中的叫做敏感列表,是执行always后面代码的条件。clk是时钟信号,rst_n是复位信号,后续会与硬件层连接,posedge和negedge分别代表上升沿和下降沿。整条语句意思便是,当时钟上升沿到来或者复位键被按下(产生了下降沿)时,将执行此always后紧跟的语句块

此外,会发现代码下半段还有一个always @(posedge clk or negedge rst_n) begin······end,这里便要理解FPGA与单片机的不同之处,即并行处理。因为此处两个always后的执行条件相同,因此满足此条件时,两个always后的语句块会同时执行,而不是像单片机那样先执行上面的程序。

1.如果没有posedge或negedge代表上下边沿都会使其执行。
2.always后面的语句块中赋值表达式的左边必须为reg类型,例如代码中的counter和led都事先声明是reg类型。
3.多个always语句块中不能出现对同一个变量赋值,否则并行处理时就产生了冲突。
4.always既可描述组合逻辑也可以描述时序逻辑,此代码中即为时序逻辑。

5.if条件语句基本和c相似,此处不做过多说明。

6.数据类型:代码中有1'b0意思为1位的二进制数0,32'd0意思是32位的十进制数0。同理16'o10'h即分别代表16位的八进制数和10位的十六进制数。需要注意的是,此处所说的位数是将数值转换为二进制后的位数,用于限定后面数值的范围,类似于单片机编程中所说的char类型为8位,但此处的位数可任意设定。例如:5‘d10 -> 0 10106’h10 -> 01 0000。如果后面的数值大小超过限定范围则会截取低位,例如3’d13 -> 1 101 -> 101 -> 5
32'd199_999_999:下划线只是为了分隔,方便读数。

7.代码中出现的 ”<=“叫做非阻塞赋值,还有一种“=”为阻塞赋值,在此处看不出其特别之处。
例如:

非阻塞赋值				阻塞赋值
 a <= 1;				 a = 1;
 b <= a;				 b = a;

左边非阻塞赋值实际执行时,上下两条语句同时执行(并行)。结果为a=1,b=a前一次的值,而非刚得到的1。
右边阻塞赋值实际执行时,就和c语言一样,从上而下顺序执行。结果为a=1,b=1。
实际上,“<=”常用于时序逻辑电路,“=”常用于组合逻辑电路。

逻辑解释

此芯片的时钟频率为50MHz!
每个时钟上升沿同时执行两段always语句。即在第一个always中计数寄存器counter每隔1/50ns自加1(有点类似单片机中的定时器功能),在第二个always中counter每增加50 000 000(即1s),换成下一个led灯亮。同时,如果复位按键被按下,两个always语句也会执行,并满足了if的条件,从而进行counter和led的清零复位。

此外还有一种方法

always @(posedge clk or negedge rst_n) 
begin
    if (!rst_n)
        counter <= 32'd0;
    else if (counter < 32'd50_000_000)
        counter <= counter + 1'b1;
    else
        counter <= 32'd0;
end

always @(posedge clk or negedge rst_n) 
begin
    if (!rst_n)
        led <= 4'b0001;
    else if(counter == 32'd50_000_000) 
        led[3:0] <= {led[2:0],led[3]};
    else
        led <= led;
end

此代码中出现了一个{ ,}的语法,意思是组合。例如此代码中便是把led拆成高1位和低3位后,重新组合,把最高位调到最低位。
这种方法更类似于单片机通过定时器和位移的方式实现流水灯。

三.软件端口与硬件I/O对应

Reset复位按键与N13口相连
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默默无闻小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值