最近一段时间咱们学校在改革,eda实训也开始全面学习fpga开发版,作者就引领着大家对fpga的初步学习。由于作者能力有限,部分内容可能会有错误,欢迎各位大佬在评论区补充内容。
作者的实习一共有三个内容,分别是点亮小灯、进行不同模式的流水灯和自由开发项目。接下来作者将会一一介绍每个实训的内容,本文侧重讲解代码,引脚等配置仔细听上课老师的讲解。
点亮小灯
第一是点亮小灯,代码如下,需要注意的内容见注释:
//描述:LED 闪烁,新建立的.v文件的名称需要和module后的字母(led_flash)相同,不然会报错
module led_flash(CLK_50M,RST_N,LED);
//外部端口声明
input CLK_50M; //时钟的端口,开发板用的 50M 晶振
input RST_N; //复位的端口低电平复位
output LED; //对应开发板上的 LED
//内部端口声明
reg [26:0] time_cnt; //用来控制 LED 闪烁频率的定时计数器
reg [26:0] time_cnt_n; // time_cnt 的下一个状态
reg led_reg; //用来控制 LED 亮灭的显示寄存器
reg led_reg_n; // led_reg 的下一个状态
//设置定时器的时间为 1s,计算方法为 1s/(1/(50*106Hz))=50*106 50MHz 为开发板晶振//
parameter SET_TIME_1S =27'd50_000_000; //27'd 为 27 位十进制数
//逻辑功能实现 //时序电路,用来给 time_cnt 寄存器赋值
//<=是非阻塞式赋值,=是阻塞是赋值,作者分不太清有什么用,但是对于同一变量,只能使用一种赋值方式,要么全部使用阻塞式赋值,要么全部使用非阻塞式赋值。
always @ (posedge CLK_50M or negedge RST_N)
begin
if (!RST_N) //判断复位
time_cnt <= 27'h0; //初始化 time_cnt 值
else
time_cnt <= time_cnt_n; //用来给 time_cnt 赋值
end
//组合电路,实现 1s 的定时计数器
always @ (*)
begin
if(time_cnt ==SET_TIME_1S) //判断 1s 时间,因为开发板使用的晶振是50MHZ,在定义SET_TIME_1S时赋值了50M,所以每到达一次就是1s
time_cnt_n = 27'h0; //如果到达 1s 定时计数器将会被清零
else
time_cnt_n = time_cnt+27'h1; //如果未到 1s,定时计数器将会继 续累加
end
always @ (posedge CLK_50M or negedge RST_N)
begin
if (!RST_N) //判断复位
led_reg <= 1'b0; //初始化 led_reg 值
else
led_reg <= led_reg_n; //用来 给 led_reg 赋值
end
//组合电路,判断时间,控制 LED 的亮或灭
always @ (*)
begin
if(time_cnt ==SET_TIME_1S) //判断 1s 时间
led_reg_n =~led_reg; //如果到达1s,显示寄存器将会改变LED的 状态
else
led_reg_n =led_reg;//如果未到 1s,显示寄存器将会将保持 LED 的原状态
end
assign LED=led_reg; //最后,将显示寄存器的值赋值给端口 LED
endmodule
流水灯
实验要求:
流水灯分阶段显示,第一阶段从右到左依次点亮;第二阶段LED流水灯从左到右依次熄灭;第三阶段LED流水从中间向两边点依次点亮;第四时间段LED流水灯从中间向两边点依次熄灭,此为一个循环。
方法1 查表法
//描述:LED 闪烁
// 'define SYS_CLK_VAL (27'd50_000_000)
module led_flash
(
input sys_clk, //系统时钟端口,开发板用的 50M 晶振
input sys_rst_n, //复位的端口低电平复位
output reg [7:0] led //LED输出信号
);
reg [27:0] cnt; //系统时钟计数
integer led_control;
//用于计数的计数器
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) //复位
cnt<=27'd0;
else if(cnt<27'd10_000_000) //没到计数值,cnt就加1,等到到达10_000_000也就是10_000_000/50M =0.2秒时,cnt就置0
cnt<=cnt+1;
else //溢出归零
cnt<=27'd0;
end
//用于led灯状态的选择
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
led_control <= 0;
else if(cnt == 27'd0_000_000) begin
led_control = led_control + 1;
if(led_control>23)
led_control=0;
//下面过程是查表法,将所有的可能用case写出,当然也可以用if写,不过那样会很麻烦
case (led_control)
0 : led<=8'b00000001;
1 : led<=8'b00000011;
2 : led<=8'b00000111;
3 : led<=8'b00001111;
4 : led<=8'b00011111;
5 : led<=8'b00111111;
6 : led<=8'b01111111;
7 : led<=8'b11111111;
8 : led<=8'b01111111;
9 : led<=8'b00111111;
10 : led<=8'b00011111;
11 : led<=8'b00001111;
12 : led<=8'b00000111;
13 : led<=8'b00000011;
14 : led<=8'b00000001;
15 : led<=8'b00000000;
16 : led<=8'b00011000;
17 : led<=8'b00111100;
18 : led<=8'b01111110;
19 : led<=8'b11111111;
20 : led<=8'b11100111;
21 : led<=8'b11000011;
22 : led<=8'b10000001;
23 : led<=8'b00000000;
endcase
end
end
endmodule
方法2 逻辑法
这一篇是使用逻辑法,就是在上面那种查表法的基础上,将case语句使用左右移、与或语句来进行运算,相对于查表法较为复杂。
//描述:LED 流水
// 'define SYS_CLK_VAL (27'd50_000_000)
module led_flowing_lights
(
input sys_clk, //系统时钟端口,开发板用的 50M 晶振
input sys_rst_n, //复位的端口低电平复位
output reg [7:0] led //LED输出信号
);
reg [27-1:0] cnt; //系统时钟计数
integer led_cnt;
integer led_mode;
//初始化
initial begin
led_mode <=0;
led <= 8'b00000001;
led_cnt <=0;
end
//用于计数的计数器
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) //复位
cnt<=27'd0;
else if(cnt<27'd10_000_000) //没到计数值
cnt<=cnt+1;
else //溢出归零
cnt<=27'd0;
end
//用于led灯状态的选择
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin//初始化
led_mode <=0;
led = 8'b00000000;
led_cnt <=0;
end
else if(cnt == 27'd0) begin
led_cnt <= led_cnt + 1;
if((led_cnt>7 &&(led_mode==0||led_mode==1)) || (led_cnt>3 &&(led_mode==2||led_mode==3))) begin
led_cnt <= 0;
led_mode = led_mode +1 ;
if (led_mode>=4)
led_mode = 0;
end
if(led_mode==0)
led = (led << 1)+1;
else if(led_mode==1)
led = (led >>1);
else if(led_mode==2 && led_cnt==0)
led = 8'b00011000;
else if(led_mode==2 && led_cnt!=0)
led = (led<<1) | (led>>1);
else if(led_mode==3 && led_cnt==0)
led = 8'b11100111;
else if(led_mode==3 && led_cnt!=0)
led = ((led<<1)|(8'b00000001)) & ((led>>>1) | (8'b10000000));
end
end
endmodule
在流水灯这一章的学习中,建议大家试着用其他思路来完成这一部分的学习,以上代码仅供参考。
自由设计
这个嘛,就要看实力了,由于咱们学校用的板子在网上没找到资料,但作者在实习时找到了正点原子板子的例程,大部分例程改引脚后就可以用,但是有些不行,比如数码管,因为板载数码管驱动方式不同。大家还可以在其他地方找找其他的例程,本文就不赘述了。
正点原子fpga例程
公众号:湛岚宁的小屋