概述
目录
- 日常·唠嗑
- 前言
- 一、认清逻辑设计
- 二、时序逻辑电路设计
- 三、扩展:呼吸灯实验
日常·唠嗑
第一次建立《零基础学FPGA》专栏,是在2021年2月2日,已经过去了一年了,目前只更新了4篇。总说要更新,却总是拖更,直到这两天有关注的朋友提起,才想起来,在这里跟各位关注我的朋友说声抱歉。
新的一年,我的时间会比较充裕,会花更多时间在这个专栏上,初心不变,还是跟大家一起共进步,嘿嘿。
最近会接触一些国产FPGA开发,如京微齐力的伏羲软件等,有兴趣的也可以一起交流哇~~
前言
在零基础学FPGA系列中,我没有写组合逻辑电路设计,而是直接进入时序逻辑电路设计。因为组合逻辑比较简单,在这里我简单提一下就好。
顺带提一下,有关非阻塞赋值、阻塞赋值、assign和always这些Verilog之类的语法,我放到下一篇再讲。
一、认清逻辑设计
在数字电路中可以根据电路功能的不同分为,组合逻辑电路与时序逻辑电路。组合逻辑电路在逻辑功能上的特点是:任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。而时序逻辑从电路特征上看来,其特点为任意时刻的输出不仅取决于该时刻的输入,而且还和电路原来的状态有关。通常是以assign语句为主。 组合逻辑电路在电路结构上,不涉及对信号跳变沿的处理,无存储电路,也没有反馈电路,通常可以通过真值表的形式表达出来。时序逻辑电路在电路结构上,不管输入如何变化,仅当时钟的沿(上升沿或下降沿)到达时,才有可能使输出发生变化。通常是以always语句为主。
对于组合逻辑,这里用一个3-8 译码器电路来介绍一下:
译码器是一种多输入多输出的组合逻辑电路,负责将二进制代码翻译为特定的对象(如逻辑电平等),功能与编码器相反。译码器一般分为通用译码器和数字显示译码器两大类。
比如:三八译码器,将 3 种输入状态翻译成 8 种输出状态,其真值表如下所示。其中 A,B,C 为数据输入,Out 为数据输出。在 MCU 应用中,如果需要保证一定的速度情况下实现此功能,一般选取外挂一片74HC38 或者 74LS38 等独立芯片,但 FPGA 提供了一个完整的想象以及实现空间,仅靠其自身即可实现设计要求。
译码器真值表
module decoder(
a,
b,
c,
out
);
input a;//输入端口A
input b;//输入端口B
input c;//输入端口C
output [7:0]out;//输出端口
reg [7:0]out;
always@(a,b,c)//always@()括号内为敏感信号列表
//always语句可以带时钟,也可以不带时钟。在always不带时钟时,逻辑功能和assign完全一致,都是只产生组合逻辑。
begin
case({a,b,c})//判断a,b,c状态值,可以用拨键开关做测试
3'b000:out = 8'b0000_0001;
3'b001:out = 8'b0000_0010;
3'b010:out = 8'b0000_0100;
3'b011:out = 8'b0000_1000;
3'b100:out = 8'b0001_0000;
3'b101:out = 8'b0010_0000;
3'b110:out = 8'b0100_0000;
3'b111:out = 8'b1000_0000;
endcase
end
endmodule
二、时序逻辑电路设计
时序逻辑电路是指电路任何时刻的稳态输出不仅取决于当前的输入,还与前一时刻输入形成的状态有关。这跟组合逻辑电路相反,组合逻辑的输出只会跟目前的输入成一种函数关系。换句话说,时序逻辑拥有储存元件来存储信息,而组合逻辑则没有。
重点:
时序逻辑电路分为很多种,在这里我讲一下最常用的计数器,并比较与组合逻辑电路的区别。此处设计一个计数器,使开发板上的 LED 状态每 500ms 翻转一次。市面上开发板的晶振大多为 50MHz,也就是说时钟周期为 20ns(1÷500_000),这样可以计算得出 500ms = 500_000_000ns/20ns = 25_000_000,即需要计数器计数 25_000_000 次,也就是需要一个至少25 位的计数器(225>25_000_000>224)。且每当计数次数达到需要清零并重新计数。
Verilog HDL 之所以被称为硬件电路描述语言,就是因为我们不是在类似 C 一样进行普通的编程,而是在编写一个实际的硬件电路。下面将设计一个计数器,通过计数器控制一个LED 闪。
module counter
(
input clk, //时钟信号50Mhz
input reset_n,//复位信号
output led
);
reg led;
parameter MCNT = 24_999_999;
reg [24:0]period_cnt ; //定义计数器寄存器
//计数器计数进程
always@(posedge clk or posedge reset_n)
if(reset_n)
period_cnt <= 25'd0;
else if(period_cnt == MCNT)
period_cnt <= 25'd0;
else
period_cnt <= period_cnt + 1'b1;
//led输出控制进程
always@(posedge clk or posedge reset_n)
if(reset_n)
led <= 1'b1;
else if(period_cnt == MCNT)
led <= ~led;
else
led <= led;
endmodule
因为计数器是从 0 开始计数而不是 1,所以在计数值计数到 25’d24_999_999 时清零,而不是计数到25’d25_000_000 时清零,这里计数器最大值使用 parameter 进行参数化定义表示,使用参数化定义的好处是通过修改顶层parameter参数,从而完成整体变量参数修改。
当计数器计数到预设的值后就让 led 取反一次,来达到亮灭翻转的目的。
在这个实验基础上,可以通过PWM做呼吸灯实验。
三、扩展:呼吸灯实验
呼吸灯采用 PWM 的方式,在固定的频率下,通过调整占空比的方式来控制 LED 灯亮度的变化。PWM,即脉冲宽度调制,在由计数器产生的固定周期的 PWM 信号下,如果其占空比为 0,则 LED 灯不亮;如果其占空比为 100%,则 LED 灯最亮。所以将占空比从 0 到 100%,再从 100%到 0 不断变化,就可以实现 LED 灯的“呼吸”效果。PWM 占空比调节示意图如下图所示:
由上图可知,LED 高电平的时间由长渐渐变短,再由短渐渐变长,如果 LED 灯是高电平点亮,则 LED灯会呈现出亮度由亮到暗,再由暗到亮的过程。
周期信号计数器用于产生驱动 LED 的脉冲信号,本次实验的周期信号频率为 1Khz,其占空比由后级逻辑在每个周期之后进行递增或递减,最后再对当前计数值和占空比计数值进行比较,以输出占空比可调的脉冲信号。
module breath_led(
input clk , //时钟信号50Mhz
input reset_n , //复位信号
output led //LED
);
//reg define
reg [15:0] period_cnt ; //周期计数器频率:1khz 周期:1ms 计数值:1ms/20ns=50000
reg [15:0] duty_cycle ; //占空比数值
reg inc_dec_flag ; //0 递增 1 递减
//根据占空比和计数值之间的大小关系来输出LED
assign led = (period_cnt >= duty_cycle) ? 1'b1 : 1'b0;
//周期计数器
always @(posedge clk or negedge reset_n) begin
if(!reset_n)
period_cnt <= 16'd0;
else if(period_cnt == 16'd50000)
period_cnt <= 16'd0;
else
period_cnt <= period_cnt + 1'b1;
end
//在周期计数器的节拍下递增或递减占空比
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
duty_cycle <= 16'd0;
inc_dec_flag <= 1'b0;
end
else begin
if(period_cnt == 16'd50000) begin //计满1ms
if(inc_dec_flag == 1'b0) begin //占空比递增状态
if(duty_cycle == 16'd50000) //如果占空比已递增至最大
inc_dec_flag <= 1'b1; //则占空比开始递减
else //否则占空比以25为单位递增
duty_cycle <= duty_cycle + 16'd25;
end
else begin //占空比递减状态
if(duty_cycle == 16'd0) //如果占空比已递减至0
inc_dec_flag <= 1'b0; //则占空比开始递增
else //否则占空比以25为单位递减
duty_cycle <= duty_cycle - 16'd25;
end
end
end
end
endmodule
第 21-28 行是 1KHz 周期信号的计数器,用于产生 1KHz 的 LED 驱动信号。第 31-52 行的 always 块为占空比设定模块,每次计数完了一个周期,就根据递增/递减标志来对占空比计数值(duty_cycle)进行递增/递减 25 个计数值,这个递增或者递减的数值大小可以用来控制呼吸灯的呼吸频率。
如果占空比计数值(duty_cycle)已经递增到了最大,则呼吸灯已经处于最亮的状态,接下来开始递减;反之,如果占空比计数至已经递减到了最小,即 0,则呼吸灯处于熄灭的状态,接下来开始递增;如此循环往复,最终实现了流水灯的效果。在代码的第 18 行通过组合逻辑把当前的周期计数值和占空比计数值进行比较,来判断 LED 的输出电平。在一个周期内,如果当前的周期计数值小于等于占空比计数值,则 LED 输出高电平,即点亮;如果当前的周期计数值大于占空比计数值,则 LED 输出低电平,即熄灭。
最后
以上就是干净溪流为你收集整理的零基础学FPGA(五):时序逻辑电路设计之计数器(附有呼吸灯实验、简单组合逻辑设计介绍)日常·唠嗑前言一、认清逻辑设计二、时序逻辑电路设计三、扩展:呼吸灯实验的全部内容,希望文章能够帮你解决零基础学FPGA(五):时序逻辑电路设计之计数器(附有呼吸灯实验、简单组合逻辑设计介绍)日常·唠嗑前言一、认清逻辑设计二、时序逻辑电路设计三、扩展:呼吸灯实验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复