前言
本系列文章来源于FPGA学习开源网站fpga4fun,网站由浅入深的介绍了26个FPGA工程,通过工程的学习,可以了解什么是FPGA以及它是如何工作的。本人仅用于学习和记录,如有侵权,速删。
一、工程介绍
本工程使用FPGA驱动LED数码管。
1.1 LED显示原理
LED是一个发光二极管,当加正向电压,LED点亮,加反向电压,LED熄灭,在实际连接电路上,FPGA引脚通过一个电阻连接LED,电阻值一般在100Ω-1KΩ之间,实际连接电路如下所示:
1.2 控制LED的发光强度
首先,看一下如何通过verilog代码实现LED的开关,代码类似用verilog代码实现蜂鸣器,具体代码如下所示:
module LEDBlink(
input clk_i, //时钟一般在10MHz-50MHz
output LED_o
);
reg [31:0] cnt;
always@(posedge clk_i)begin
cnt <= cnt + 1;
end
assign LED_o = cnt[22]; //这里使用的是计数器23bit控制开关的频率,使用不同的计数器位数实现不同的开关频率
endmodule
其次,看一下如何控制LED实现半亮这种状态,一般由两种解决方案:一是将与FPGA、LED串联的电阻的阻值提高一倍;二是让FPGA输出不停的翻转,使得LED点亮和熄灭的时间一样,这样可以看到半亮的状态,具体代码如下所示:
module LEDhalflit(
input clk_i,
output LED_o
);
reg toggle;
always@(posedge clk_i)begin
toggle <= ~toggle; //在时钟的一半进行翻转
end
assign LED = toggle;
endmodule
然后,看一下如何控制LED的强度,一般可以使用pwm的思想。下面是一个使用4bit输入控制LED在16个亮度级别之间进行选择的例子,具体代码如下所示:
module LED_PWM(
input clk_i,
input [3:0] pwm_i, //16个亮度级别
output LED_o
);
reg [4:0] pwm;
always@(posedge clk_i)begin
pwm <= pwm[3:0] + pwm_i;
end
assign LED = pwm[4];
endmodule
最后,看一下如何使LED看起来会发光,一般是使用计数器结合pwm的思想,具体代码如下所示:
module LEDglow(
input clk_i,
output LED_o
);
reg [23:0] cnt;
reg [ 4:0] pwm;
wire [3:0] intensity = cnt[23] ? cnt[22:19] : ~cnt[22:19] //亮度在亮暗切换
always@(posedge clk_i)begin
cnt <= cnt + 1;
end
always@(posedge clk_i)begin
pwm <= pwm[3:0] + intensity;
end
assign LED = PWM[4];
endmodule
1.3 7段发光二极管显示
数码管由8个发光二极管组成,8个二极管命名为‘A’到‘G’,还有一个DP(点)。这8个二极管并不是独立的,一般会将阴极或者阳极接到一起,实际物理器件最少有九个引脚,如下图所示:
FPGA驱动数码管,最简单的方案是使用8个IO引脚,如下图所示,以显示数字2为例,具体代码如下:
module LED_7seg(
output segA_o,
output segB_o,
output segC_o,
output segD_o,
output segE_o,
output segF_o,
output segG_o,
output segDP_o
);
assign {segA_o,segB_o,segC_o,segD_o,segE_o,segF_o,segG_o,segDP_o} = 8'b11011010;
endmodule
下面使用FPGA驱动数码管实现一个10进制计数器,具体代码如下所示:
module LED_7seg(
input clk_i,
output segA_o,
output segB_o,
output segC_o,
output segD_o,
output segE_o,
output segF_o,
output segG_o,
output segDP_o
);
reg [23:0] cnt; //使用24bit计数器
always@(posedge clk_i)begin
cnt <= cnt + 24'h1;
end
wire cntovf = &cnt; //归约与,&cnt=cnt[0]&cnt[1]&....&cnt[23],每计满24位,cntovf值为1,如果时钟设置好,可以实现1s置1一次
reg [3:0] BCD;
always@(posedge clk_i)begin
if(cntovf)beign //每一秒,计数器加1
BCD <= (BCD==4'h9 ? 4'h0 : BCD+4'h1); //from 0 to 9
end
end
reg [7:0] SevenSeg;
always@(*)begin
case(BCD)
4'h0: SevenSeg = 8'b1111_1100;
4'h1: SevenSeg = 8'b0110_0000;
4'h2: SevenSeg = 8'b1101_1010;
4'h3: SevenSeg = 8'b1111_0010;
4'h4: SevenSeg = 8'b0110_0110;
4'h5: SevenSeg = 8'b1011_0110;
4'h6: SevenSeg = 8'b1011_1110;
4'h7: SevenSeg = 8'b1110_0000;
4'h8: SevenSeg = 8'b1111_1110;
4'h9: SevenSeg = 8'b1111_0110;
default: SevenSeg = 8'b0000_0000;
endcase
end
assign {segA_o,segB_o,segC_o,segD_o,segE_o,segF_o,segG_o,segDP_o} = SevenSeg;
endmodule
下面使用FPGA驱动数码管实现一个10进制平滑计数器,效果实现淡入淡出,具体代码如下所示:
module LED_7seg(
input clk_i,
output segA_o,
output segB_o,
output segC_o,
output segD_o,
output segE_o,
output segF_o,
output segG_o,
output segDP_o
);
reg [23:0] cnt; //使用24bit计数器
always@(posedge clk_i)begin
cnt <= cnt + 24'h1;
end
wire cntovf = &cnt; //归约与,&cnt=cnt[0]&cnt[1]&....&cnt[23],每计满24位,cntovf值为1,如果时钟设置好,可以实现1s置1一次
reg [3:0] BCD_new;
reg [3:0] BCD_old;
always@(posedge clk_i)begin
if(cntovf)beign //每一秒,计数器加1
BCD_new <= (BCD_new==4'h9 ? 4'h0 : BCD_new+4'h1); //from 0 to 0
end
end
always@(posedge clk_i)begin
if(cntovf)beign
BCD_old <= BCD_new; //将计数器打一拍
end
end
reg [4:0] pwm;
wire [3:0] pwm_input = cnt[22:19];
always@(posedge clk_i)begin
pwm <= pwm[3:0] + pwm_input; //通过pwm控制实现淡入淡出效果
end
wire [3:0] BCD = (cnt[23] | pwm[4]) ? BCD_new : BCD_old;
reg [7:0] SevenSeg;
always@(*)begin
case(BCD)
4'h0: SevenSeg = 8'b1111_1100;
4'h1: SevenSeg = 8'b0110_0000;
4'h2: SevenSeg = 8'b1101_1010;
4'h3: SevenSeg = 8'b1111_0010;
4'h4: SevenSeg = 8'b0110_0110;
4'h5: SevenSeg = 8'b1011_0110;
4'h6: SevenSeg = 8'b1011_1110;
4'h7: SevenSeg = 8'b1110_0000;
4'h8: SevenSeg = 8'b1111_1110;
4'h9: SevenSeg = 8'b1111_0110;
default: SevenSeg = 8'b0000_0000;
endcase
end
assign {segA_o,segB_o,segC_o,segD_o,segE_o,segF_o,segG_o,segDP_o} = SevenSeg;
endmodule