文章目录
第一节 项目背景
随着照明领域需求的不断扩大,LED 技术也在迅速发展,其控制方式也越来越多样化,可以产生多样的视觉效果。相较于只具备“开”“关”功能的传统 LED 照明,能够实现从 0 到 100%灯光亮度调节的 LED 灯在家装灯饰、舞美灯光等领域的需求更为突出。这种灯的灯光亮度可以通过调节控制由高到低的逐渐变化,像是人在呼吸一般,因而被称作呼吸灯。呼吸分为两个过程,一个是“呼”,一个是“吸”。而所谓的“呼吸灯”就是将人的呼吸频率通过光的强弱表现出来,其被广泛应用于手机上,并成为各大品牌手机的卖点之一。当手机里有未处理的通知,比如未接来电或未查收的短信时,呼吸灯就会像呼吸一样有节奏的由暗到亮不断变化,从而起到通知提醒的作用。
呼吸灯的设计方法有很多,例如采用 555 定时器的设计方案,电路利用电容充放电的原理较为简单,也可以用单片机产生脉冲宽度调制来驱动 LED。本案例中采用 PWM 驱动 LED 灯的方法来进行设计。
脉冲宽度调制技术(Pulse Width Modelation,PWM)是利用微处理器/FPGA 的数字输出对模拟电路进行控制的一种有效技术,其广泛应用于测量、通信、功率控制与变换等众多领域。PWM 数字信号从处理器到被控系统都采用数字形式,无需进行数模转换。航模中的控制信号大多是 PWM 信号,比如 FUTABA、JR 等舵机的控制都采用 PWM 方式,发射机给接收机输送脉冲后接收机会控制舵机进行旋转。举个例子,假定基础脉宽是 100ms,当发射机的脉宽增大(如增加到 150ms)时接收机控制舵机进行正向旋转;反之发射机的脉宽减小(如减小到 50ms)时,接收机控制舵机进行逆向旋转。
PWM 是一种对模拟信号电平进行数字编码的方法。通过使用高分辨率计数器对方波的占空比进行调制,从而对一个具体模拟信号的电平进行编码。由于在给定的任何时刻,满幅值的直流供电只存在有(ON)和无(OFF)两种状态,因此 PWM 信号仍然是数字信号。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。直流供电被加到负载上的时候为“通”,负载供电被断开的时候为“断”。只要有足够的带宽,任何模拟值都可以使用 PWM 进行编码。
通俗来说,PWM 是连续的、具有一定比例占空比的脉冲信号,可以通过控制占空比来对其进行改变。简单来说,可以认为 PWM 就是一种方波,如图 3.3-1 所示。
PWM原理的补充讲解:
以单片机为例,我们知道,单片机的IO口输出的是数字信号,IO口只能输出高电平和低电平
假设高电平为5V 低电平则为0V 那么我们要输出不同的模拟电压,就要用到PWM,通过改变IO口输出的方波的占空比从而获得使用数字信号模拟成的模拟电压信号
我们知道,电压是以一种连接1或断开0的重复脉冲序列被夹到模拟负载上去的(例如LED灯,直流电机等),连接即是直流供电输出,断开即是直流供电断开。通过对连接和断开时间的控制,理论上来讲,可以输出任意不大于最大电压值(即0~5V之间任意大小)的模拟电压
比方说 占空比为50% 那就是高电平时间一半,低电平时间一半,在一定的频率下,就可以得到模拟的2.5V输出电压 那么75%的占空比 得到的电压就是3.75V
pwm的调节作用来源于对“占周期”的宽度控制,“占周期”变宽,输出的能量就会提高,通过阻容变换电路所得到的平均电压值也会上升,“占周期”变窄,输出的电压信号的电压平均值就会降低,通过阻容变换电路所得到的平均电压值也会下降
也就是,在一定的频率下,通过不同的占空比 即可得到不同的输出模拟电压
pwm就是通过这种原理实现D/A转换的。
总结:
PWM就是在合适的信号频率下,通过一个周期里改变占空比的方式来改变输出的有效电压
PWM频率越大,相应越快,了解 PWM 之后。
下面来学习一下 PWM 实现呼吸灯的原理:
信号的高低电平变化可以控制 LED 灯的明灭状态。当输出信号为低电平时 LED 灯亮,反之,当输出信号为高电平时 LED 灯灭。当输出电平持续为低,则灯一直保持亮的状态;而当输出电平持续为高,则灯一直保持灭的状态。如果输出信号 50%为低电平,50%为高电平,在 PWM 适宜的周期下LED 灯光会变暗,这也说明占空比对 LED 灯的明暗程度会产生影响。
另一个影响 LED 亮度的因素则是 PWM 波形的周期。试想一下,如果 PWM 的周期是 2 秒,占空比为 50%,此时观察到 LED 的状态是暗 1 秒、亮 1 秒,而不是半亮的状态。只有缩短 PWM 的期,才能看到 LED 半亮的状态。根据经验可知,如果要达到改变亮度的效果PWM 的周期以 10 毫秒为宜。
综上所述,通过控制高低电平的时间,即占空比(高电平占周期的百分比),以及控制 PWM 的周期,就可以改变灯的明暗程度。
如何实现PWM信号输出?
那么如果要实现PWM信号输出如何输出呢?
1)可以直接通过芯片内部模块输出PWM信号,前提是这个I/O口要有集成模块,只需要简单几步操作即可,这种自带有PWM输出的功能模块在程序设计更简便,同时数据更精确。如下图,一般的IC口都会标明这个是否是PWM口;
2)但是如果IC内部没有PWM功能模块,或者要求不是很高的话可以利用I/O口设置一些参数来输出PWM信号,因为PWM 信号其实就是一高一低的一系列电平组合在一起。具体方法是给I/O加一个定时器,对于你要求输出的PWM信号频率与你的定时器一致,用定时器中断来计数,但是这种方法一般不采用,除非对于精度、频率等要求不是很高可以这样实现。
第二节 设计目标
了解 PWM 呼吸灯的运行原理后来明确本次设计的功能目标。
确定功能目标是本书以及至简设计法的特别之处,只有对设计的功能目标有一定的理解和预期,才能更好地进一步讨论如何逐步进行代码设计和实现。后续设计中的每一个步骤都是围绕着设计目标的实现来针对性的展开,如果对于设计目标一知半解,那么在后续的设计思路中可能难以形成体系化的思考模式,只能碎片式的接收知识。设计目标就像大楼的设计图,如果连最终想要实现的目标都无法确定,那么后续的讨论就没有任何意义。因此在学习完整设计案例的过程中,静下心来从明确设计目标开始,一步步细细咀嚼,逐步掌握,才可以起到事半功倍的效果。
本工程旨在实现控制 LED 灯亮度的功能,具体要求为:上电后,LED 灯显示接近于灭,随后每隔 2 秒亮度发生一次变化,在前 10 秒时间内,每隔 2 秒逐渐变亮。在下一个 10 秒时间内,每隔 2秒,亮度逐渐变暗。简单来说即是以 20 秒为一次循环,每隔 2 秒变化一次,前 10 秒亮度逐渐增大,后 10 秒亮度逐渐减小。
选用开发板的硬件原理图如下图所示。
本设计的上板效果如图 3.3-3 所示
如果想要观看上板后的演示视频,可以登录至简设计法官方网站进行观看。
第三节 设计实现
3.1顶层设计
新建目录:D:\mdy_book\mdyBookPwmled
,并在此目录中新建一个名为 mdyBookPwmled.v
的文件。
用 GVIM 打开该文件后开始编写代码。在这里再次强调,初学者按照本书提供的路径名和文
件名创建文件,不要对路径名或文件名进行任何更改。因为对于初学者来说,如果贸然按照自己的意
愿更名,后续操作中可能会出现中文路径、空格路径等非法路径的问题,或者有些文件名更改后会出
现报错的现象。因此建议先按照要求更名保存,在多次进行工程练习熟悉了各个步骤后再进行自主更
名操作。在操作中不要想当然,细心操作以避免不必要的错误。
首先对板子上的 LED 灯进行分析,每个 LED 灯都与一个信号相连。这个信号一端连接 LED,另一端连接 FPGA。FPGA 通过控制这一信号的输出就可以控制 LED 灯的亮、灭以及亮度。当 FPGA 输出信号为 0 时,LED 灯点亮,反之,当 FPGA 输出信号为 1 时,LED 灯熄灭。通过输出 PWM波形并控制其占空比,FPGA 可以实现对 LED 灯的亮度进行控制。硬件电路图的连接关系如下表所示。本工程中 LED1 灯连接的 FPGA 管脚为 AA4,对应 FPGA工程信号为 led;工程的时钟管脚为 G1,对应 FPGA 工程信号为 clk;复位管脚为 G1,对应 FPGA工程信号为 rst_n。
综上所述,本工程一共需要三个信号,时钟 clk,复位 rst_n 和输出信号 led。将 module 的名称
定义为 mdyBookPwmled
,在顶层信号代码中需要将与外部相连接的输入/输出信号列出,从而实现
信号与管脚的连接,其具体代码如下:
随后对信号的输入输出属性进行声明,指出对于 FPGA 来说这一信号属于输入还是输出,若为输入声明则为 input,若为输出声明则为 output。在本设计中,由于 clk 是外部晶振输送给 FPGA 的,因此在 FPGA 中 clk 为输入信号 input;同样地,rst_n 是外部按键给 FPGA 的,在 FPGA 中 rst_n 也为输入信号 input;同时可知 led 是 FPGA 输出控制 LED1 显示状态的,因此 led 为输出信号 output。
根据三个信号的属性将输入输出端口定义补充完整,其代码如下:
3.2 信号设计
在进行信号设计之前先按照至简设计法的思路来进行架构设计。回顾一下设计需求:通电后,LED 灯显示接近于灭,在之后的 10 秒内,每隔 2 秒钟亮度变化一次,逐渐变亮;在下一个 10 秒内,依旧是每隔 2 秒亮度变化一次,但是会逐渐变暗。可以将其总结为:本设计以 20 秒为一次循环,每隔 2 秒变化一次,前 10 秒亮度逐渐增大,后 10 秒亮度逐渐减小。
前文中详细的介绍过 PWM 的原理,可以得知通过控制 PWM 的占空比可以实现 LED 灯的亮度控制。PWM 占空比越大(高电平时间越长,低电平时间越低),灯的亮度越暗。可以这样理解:FPGA 控制 led 信号的输出,可以输出为 PWM 波形并通过调整占比来达到改变 LED 灯亮度的效果。
根据设计目标可以得出设计方案:每 20 秒一次循环,每隔 2 秒改变一次 led 的占空比,前 10 秒占空比逐渐变小,LED 灯逐渐变亮,后 10 秒占空比逐渐变大,LED 灯也随之逐渐变暗。由于设计目标只对亮度改变进行要求,并没有说明具体的占空比是多少,因此本书自行设定占空比。可以在上板时根据观察到的视觉效果,调整占空比的大小。本书设定占空比如下:
第 1 个 2 秒内,占空比为 95%;
第 2 个 2 秒内,占空比为 85%;
第 3 个 2 秒内,占空比为 70%;
第 4 个 2 秒内,占空比为 50%;
第 5 个 2 秒内,占空比为 20%;
第 6 个 2 秒内,占空比为 20%;
第 7 个 2 秒内,占空比为 50%;
第 8 个 2 秒内,占空比为70%;
第 9 个 2 秒内,占空比为 85%;
第 10 个 2 秒内,占空比为 95%。
之后以此为规律循环往复。
经过多次的实验和测试后发现:PWM 每 10 毫秒刷新一次,LED 灯显示的亮度效果是最好的。因此根据经验值,本书将 PWM 波周期设为 10 毫秒。建议初学者按照书中提供经验值来进行操作,在完全掌握了设计原理可以独立完成设计后,可以再进行不同波型以及不同占空比的尝试。
根据前文分析,得到 led 信号的变化波形图如下图所示。
第 1 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(9.5 毫秒时变低);
第 2 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(8.5 毫秒时变低);
第 3 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(7.0 毫秒时变低);
第 4 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(5.0 毫秒时变低);
第 5 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(2.0 毫秒时变低);
第 6 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(2.0 毫秒时变低);
第 7 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(5.0 毫秒时变低);
第 8 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(7.0 毫秒时变低);
第 9 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(8.5 毫秒时变低);
第 10 次持续时间 2 秒,每 10 毫秒输出一个 PWM 波(9.5 毫秒时变低);
此处PWM 波变低的时间是根据 10 毫秒 PWM 波的占空比算出来的,例如占空比为 95%时可以得出 10ms×95%=9.5ms,以此类推,可以得出所有 PWM 波的变化值。
根据至简设计法原理,总结需求可知,这次设计需要以下三个计数器:
计算输出 10 毫秒 PWM波的计数器;
计算每一个持续 2 秒时间的计数器;
计算次数(1-10 次)的计数器。
来思考一下:既然设计目标为 2 秒改变一次状态,那么为什么除了计算 2 秒的计数器之外还要增加次数的计数器,这样岂不是更麻烦吗?实际上增加计数器的操作正是采用了至简设计法的设计思路,让信号代码更加有条理并便于设计师确定位置。
举个生活中常见的例子,如下图所示,可以把每一次 20 秒的循环看做楼层,把每 2 秒一次的状
态改变看做门牌号,循环中的 2 秒、4 秒、6 秒时间即在对应门牌 1 号 2 号 3 号,以此类推。如果只
用一个计数器的话,那么一楼门牌号为 1、2、3、4、5、6、7、8,二楼门牌号为 9、10、11、12、
13、14、15、16、17、18,三楼以此类推。随着楼层的变高,这种计数方式的弊端就会显露出来。
比如在这种情况下想要寻找 76 号,就可能需要很久才能找到。
如果在一个计数单位的基础上再加一个计数单位,即采用两种技术模式,一个记楼层,一个记门牌号,如下图所示。在同样的门牌号计数中,可以记为一楼的 1、2、3、4、5、6、7、8 号,二楼的
1、2、3、4、5、6、7、8、9、10 号,以此类推,每一层都有对应的房间号。在这种计数模式下,
如果想要找到七层的 6 号房间,不需要多做思考就可以一下定位到正确位置。
此外,如果想要定位到每一层固定位置的房间,同样可以使用两种计数单位复合的模式:用 cnt
0 来表示房间号,其范围是 0-9,用 cnt1 来表示楼层号,其范围是 0-1,通过 cnt0 和 cnt1 两个计数
器可以找到任何一个房间。如果想找同一个位置的房间,也可以直接用 cnt0 来表示。例如 cnt04
可以统一表示每层楼的四号房间。但如果只有房间号这一计数模式而没有楼层的话,想表示每层楼的
四号房间,则表示方式为“cnt04”、“cnt0=12”,更高楼层以此类推。两种表现形式的难易程度
显而易见。
通过案例分析可以发现复合计数并不是多此一举,反而是最简单的计数方式。因此,除计算输出10 毫秒 PWM 波的计数器外,本设计会使用一个计数 2 秒的计数器和一个表示次数的计数器。这是最适合本设计的计数器方案,在后续遇到问题时可以快速的定位到相应位置,从而避免很多麻烦。不论是简单设计还是复杂设计,至简设计法都会全面的考虑设计需求,在每一个环节采用最适合的设计方案,尽量为后续的步骤减少不必要的麻烦。
确定了三个计数器后来讨论一下每个计数器的实现。至简设计法的设计规则中有讲过,计数器的设计只考虑两个因素:加 1 条件和计数数量。只要确定好相应逻辑,就能完成计数器代码设计。首先来讨论计数 10 毫秒 PWM 波的计数器 cnt0,由于 cnt0 始终不停地进行计数,因此可以认为其加 1 条件是一直有效的,可写成:assign add_cnt0==1。
可能会有同学提出疑问:加 1 条件的概念是什么?这里以停车位来进行比喻,一般情况下对每个停车位置会进行对应编号,但是如果某个位置上放置了一块石头无法作为停车位时,该位置就不能获得对应的编号。反之则可以认为停车位编号的加 1 条件就是:对应位置上没有石头,其可以继续的进行编号,即 assign add_cnt0 = “没有石头”。因此如果在设计中计数器一直没有阻碍地进行计数工作,则可以认为加 1 条件是一直有效的。
接着讨论计数器 cnt0 的计数数量:本工程的工作时钟是 50MHz,即周期为 20ns,当计数器计数到第 10_000_000/20=500_000 个时,就代表 10 毫秒时间到了,因此 cnt0 的计数数量为 500_000。
确定好 cnt0 的加 1 条件和计数数量后开始进行代码编写,以往都是一行行的输入相应代码。但是至简设计法有一个小技巧,可以节省代码编写时间的同时在一定程度上降低了代码的出错率。至简设计法将日常代码中常用到的固定部分制作成模板,进行代码编程时可以调用相应模板后根据逻辑输入对应设计的变量将代码补充完整。这处就可以用模板编写计数器代码,感受一下这个炫酷的功能。打开 GVIM 工具,在命令模式下输入“:Mdyjsq”后点击回车就调出了对应模板,如下图所示。之后再将本案例中的变量填到模板里面,就可以得到完整正确的计数器代码。
按照上文方法,得出计数器 cnt0 的代码设计如下:
下面来设计记录 2 秒时间的计数器 cnt1。本工程的工作时钟是 50MHz,即周期为20ns,因此当计数器计数到第 2_000_000_000/20=100_000_000 个时,就代表 2 秒时间计时结束。这是第一种设计思路,至简设计法在这里也提供另外一种设计思路:与 cnt0 复合使用,以 10 毫秒为小周期,通过数 2_000_000_000/10_000_000=200 个 10 毫秒时间,就能确定 2 秒时间计时结束。在这种思路下可以得出计数器 cnt1 的加 1 条件是 end_cnt0,计数数量为 200。此时继续调用至简设计法模板,在 GVIM 命令模式下输入“:Mdyjsq”,点击回车后调出对应模板,将“add_cnt1”和“end_cnt1”补充完整,得到计数器 cnt1 的代码如下:
最后是次数计数器 cnt2 的设计。根据设计目标可知,每隔 2 秒为 1 次改变,计数器的值应加 1,即 cnt2 的加 1 条件为 end_cnt1;每个周期该计数器应计数 10 次,即 cnt2 的计数数量为 10。继续调用至简设计法模板,在命令模式下输入“:Mdyjsq”,点击回车,调出对应模板后将“add_cnt1”和
“end_cnt1”补充完整,得到计数器 cnt2 的代码如下:
确定好三个计数器的代码后来思考一下输出信号 led 的变化。回想设计目标可知 led 有两个变化点:变 0 和变 1。当 10 毫秒计数器计数到一定个数时 led 信号值变为 0,但由于占空比不断进行变化,该计数值也会发生变化。可以假设该值为 x,也就是当计数器数到第 x 个时,led 的值变 0。led 值变为 1 则是由于 10 毫秒计数时间到了,也就是当 end_cnt0 时,led 的值变 1 。依旧调用至简设计法模板,在编辑模式下输入“Shixu2”回车,调出模板并将代码补充完整,得出 led 信号的代码如下:
为什么Led的变零条件是add_cnt0&&cnt0==x-1
?
在这个地方add_cnt0始终等于1 。写不写是无所谓的,但是写了绝对不会错。
看前面内容:
计数器规则 3:只有在加 1 条件有效时,才能表示计数器的计数值。
假定加 1 条件为 add_cnt,计数器当前值为 cnt,则
add_cnt&&cntx-1 表示计数器计数到 x 个。
cntx-1 不能表示计数器计数到 x 个。
计数器是从 0 开始计数的,因此计数器的默认值即初始值是 0。那么当计数器的值为 0 时要如何区分这是开始计数的第 1 个值,还是并未计数的默认值呢?
这种情况下可以通过加 1 条件来进行区分。当加 1 条件无效时,计数器值为 0 表示未开始计数,此时的 0 为默认值;当加 1 条件有效时,计数器值为 0 表示计的第 1 个数。同理,当 cntx-1,不能表示计数到 x;只有当 cntx-1 且加 1 条件有效时,才表示计数到 x。
因此,当 add_cnt&&cnt5-1 时,表示计数到 5 个。而当 add_cnt0 &&cnt==5-1 时,不能表示计数到 5 个
最后再来思考一下变量 x。x 代表 led 信号值变为 0 的条件,即 PWM 波变低的时刻。由于不同次数中 PWM 波的占空比是不断变化的,其对应的 x 值会有所变化。也就是说 x 的值与闪烁次数有关,即与计数器 cnt2 有关。在第 1 次闪烁(cnt2=0)时,led 信号在 9.5 毫秒时刻变为 0,即在第一次闪烁中,当 cnt0 数到第9_500_000/20=475_000 个时 led 信号变为 0。因此当 cnt2=0 时,x 的值为 4
75_000。同样的,第 2 次闪烁(cnt2=1)时,led 信号在 8.5 毫秒时刻变为 0。8_500_000/20=425_000,因此 cnt2=1 时,x=425_000。同理可得第三次 x=350_000,第四次 x=250_000,第五次 x=100_000,第六次 x=100_000,第七次 x=250_000,第八次 x=350_000,第九次 x=425_000,第十次 x=475_000。
综上所述,可得 x 的代码如下:
至此,主体程序已经完成。回顾一下思考过程会发现设计的每一步都要按照设计目标逐步展开,看似在讨论一个个小问题,但实际上每个问题都是围绕设计目标来进行讨论的,这也是本书最开始强调制定和理解设计目标的重要性的原因。
3.3 信号定义
下来将 module 补充完整,首先来定义信号类型。对类型 reg 和 wire 的判断总会有多余的联想,比如认为 reg 是寄存器,wire 是线;或者认为 reg 类型会综合成寄存器,wire 类型不会综合成寄存器。
但是这些其实和 reg 型还是 wire 型都并无关系。实际上对信号类型的判断不需要做任何的联想,只
要记住一个规则“用 always 实现的是 reg 型,其他都是 wire 型”就可以了。
cnt0 是用 always 产生的信号,因此类型为 reg。cnt0 计数的最大值为 500_000,需要用 19 根线表示,即位宽是 19 位。关于信号位宽的获取,至简设计法在此分享一个非常实用的技巧:打开计算器,点击“查看”,选择“程序员”模式,在“十进制”下将信号值输入,就会获得对应的信号位宽。如下图所示,将 cnt0 的值 500_000 输入,可以看出其位宽为 19。
综上所述,cnt0 的信号定义代码如下:
cnt1 也是用 always 产生的信号,因此类型为 reg。cnt1 计数的最大值为 200,需要用 8 根线表
示,即位宽是 8 位。编辑模式下输入“Reg8”调用模板,得到代码表示如下:
同理,cnt2 信号也由 always 产生,其类型为 reg。cnt2 计数的最大值为 9,需要用 4 根线表示,
251
即位宽是 4 位。编辑模式下输入“Reg4”调用模板,得到代码表示如下:
add_cnt0 和 end_cnt0 都是用 assign 方式设计的,因此类型为 wire。其值是 0 或者 1,用 1 根
线表示即可,位宽为 1。编辑模式下输入“Wire1”调用模板,得到代码表示如下:
add_cnt1 和 end_cnt1 也是用 assign 方式设计的,因此类型为 wire。其值是 0 或者 1,用 1 根
线表示即可,位宽为 1。编辑模式下输入“Wire1”调用模板,得到代码表示如下:
同样,add_cnt2 和 end_cnt2 是用 assign 方式设计,类型为 wire。其值是 0 或者 1,用 1 根线
表示即可,位宽为 1。编辑模式下输入“Wire1”调用模板,得到代码表示如下:
led 信号是用 always 方式设计的,因此类型为 reg。其值是 0 或者 1,用 1 根线表示即可,位宽
为 1。编辑模式下输入“Reg1”调用模板,得到代码表示如下:
x 是用 always 方式设计的,因此类型为 reg。其值是最大是 475_000,需要 19 根线表示,即位
宽为 19,其代码表示如下:
完整代码如下
module pwmled(
clk,
rst_n,
led
);
input clk;
input rst_n;
output led;
reg [ 18:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ 7:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg [ 3 :0] cnt2 ;
wire add_cnt2 ;
wire end_cnt2 ;
reg led;
reg[18:0] x;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt0 <= 0;
end
else if(add_cnt0) begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0+1 ;
end
end
assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0 == 500_000-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt1 <= 0;
end
else if(add_cnt1) begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1+1 ;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 200-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt2 <= 0;
end
else if(add_cnt2) begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2+1 ;
end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && cnt2 == 10-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led <= 1;
end
else if(add_cnt0 && cnt0==x-1)begin
led <= 0;
end
else if(end_cnt0)begin
led <= 1;
end
end
always @(*)begin
if(cnt2==0)begin
x=475_000;
end
else if(cnt2==1)begin
x=425_000;
end
else if(cnt2==2)begin
x=350_000;
end
else if(cnt2==3)begin
x=250_000;
end
else if(cnt2==4)begin
x=100_000;
end
else if(cnt2==5)begin
x=100_000;
end
else if(cnt2==6)begin
x=250_000;
end
else if(cnt2==7)begin
x=350_000;
end
else if(cnt2==8)begin
x=425_000;
end
else begin
x=475_000;
end
end
endmodule
第四节 综合和上板
4.1 新建工程
打开软件“Quartus Ⅱ
”,点击 File
下拉列表中的 New Project Wzard...
新建工程选项,如下
图所示。
随后会出现
Quartus
新建工程介绍,如下图所示,直接点击“Next
”。
此时会出现工程文件夹、工程名、顶层模块名设置界面,如图 3.3- 11 所示。设置目录为:
D:/mdy_book/mdyBookPwmled
,工程名和顶层名为 mdyBookPwmled
。这里再次进行强调,为了避免初学者使用过程中出现报错情况,强烈建议按照本书的工程名和文件名进行设置,设置完成后点击“Next”
。
新建工程类型设置选择“Empty project
”,如下图所示,然后点击“Next
”。
文件添加界面如图 3.3- 13 所示,点击右侧的“Add”按钮,添加之前写好的“mdyBookPwmled.v
”文件,可以看到界面下方会显示出文件,随后点击“Next
”。
芯片型号选择界面如图 3.3- 14 所示,“
Device Family
”选择“Cyclone ⅣE
”,在芯片型号选择处选择“EP4CE15F23C8
”,之后点击“Next
”。
图 3.3- 15 为 QUARTUS
设置工具界面,不必做任何修改,直接点击“Next”
即可。
新建工程的汇总情况如下图所示,点击“Finish
”,完成新建工程。
4.2 综合
新建工程步骤完成后,会出现如下所示的 QUARTUS
界面。
点击编译按钮可以对整个工程进行编译。编译成功的界面如图 3.3- 18 所示
4.3 配置管脚
下面需要对相应管脚进行配置。如下图所示,在菜单栏中选中“Assignments”
,然后选择“Pin Planner”
,随后会弹出配置管脚的窗口。
在配置窗口最下方中的“
location”
一列,参考错误!未找到引用源。,按照表 3.3-1 中最右两列
配置好 FPGA 管脚,最终配置结果如图 3.3- 20。配置完成后,关闭“Pin Planner”
,软件自动会保
存管脚配置信息。
4.4 再次综合
再次打开“QUARTUS
”软件,在菜单栏中选中“Processing
”,然后选择“Start Compilation
”,
再次对整个工程进行编译和综合,如下图所示。
当出现图 3.1-70
QUARTUS
编译成功标志时,说明已经成功完成编译综合。
4.5 连接开发板
完成编译后开始进行上板调试操作,按照下图的方式,将下载器接入电脑 USB 接口,接上开发
板电源后按下开发板下方蓝色开关,硬件连接完毕。
4.6 上板
打开 QUARTUS
界面,双击“Tasks”
一栏中的”Program Device
‘。
出现下载程序界面后点击“add file
”添加“.sof
”文件,点击“Start
”,会在上方的“Progress
”处显示进度,当进度条到 100%时提示成功,此时即可在开发板上观察相应现象。
如果操作步骤正确,此时可以在板子上看到 LED 灯在十秒内逐渐变亮,接下来的十秒又逐渐变暗,随后继续变亮,不断循环。如果观察到这一现象,可以判断此次设计成功。反之,如果 LED 灯没有正常显示或者按照设计目标的规律变暗变亮,就需要从头开始进行错误排查。如果无法自己完成错误排查的话,可以重新按照步骤操作一遍,相信一定可以达到想要的效果。