差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
7-seg_disp_verilog [2021/10/08 17:27] gongyu |
7-seg_disp_verilog [2022/03/30 11:13] (当前版本) gongyu |
||
---|---|---|---|
行 1: | 行 1: | ||
- | ## 7段数码管显示 | + | ## 2个7段数码管显示 |
- | + | ||
- | ### 硬件平台 | + | |
- | + | ||
- | ------ | + | |
- | - [[STEP-MXO2第一代]] | + | |
- | - [[STEP-Baseboard]] | + | |
- | + | ||
- | ### 设计要求 | + | |
- | + | ||
- | ------ | + | |
- 了解数码管显示的工作原理 | - 了解数码管显示的工作原理 | ||
+ | - 加强对组合逻辑中的译码器的理解 | ||
- 掌握Verilog语言设计数码管显示驱动 | - 掌握Verilog语言设计数码管显示驱动 | ||
- | - 掌握Verilog语言设计串行转并行逻辑 | ||
- | - 掌握数码管动态显示,按秒实现0到9循环左移显示 | ||
- | |||
- | ### 工作原理 | ||
- | |||
- | ------ | ||
- | |||
- | ### 八段数码管显示原理 | ||
- | |||
- | ------ | ||
- | 数码管是工程设计中使用很广的一种显示输出器件。一个8段数码管分别由a、b、c、d、e、f、g位段和表示小数点的dp位段组成。实际是由8个LED灯组成的,控制每个LED的点亮或熄灭实现数字显示。通常数码管分为共阳极数码管和共阴极数码管,结构如下图所示: | ||
- | |||
- | {{ :数码管.jpg |共阳极、共阴极数码管}} | ||
- | <WRAP centeralign> **图1 共阳极、共阴极数码管** </WRAP> | ||
- | |||
- | 共阴8段数码管的信号端低电平有效,而共阳端接高电平有效。当共阳端接高电平时只要在各个位段上加上相应的低电平信号就可以使相应的位段发光。比如:要使a段发光,则在a段信号端加上低电平即可。共阴极的数码管则相反。 | ||
- | |||
- | 数码管有两种显示方式: | ||
- | - 一种是独立显示模式,由上图可知,控制数码管显示我们需要8根数据信号线和1根控制端选通信号线,这样如果我们需要4位数码管就需要4*9 = 36根信号线才能分别显示。独立显示模式实现简单,但是需要大量的信号线。 | ||
- | - 另一种是扫描显示模式,即所有数码管共用8根数据信号线,各自再有1根选通信号线,采用分时的方式循环选通各位数码管,这样就需要8+4 = 12根信号线进行显示。利用人眼的“视觉暂留效应”,可以使数码管显示看起来是同时显示。扫描显示模式实现复杂,但是节约信号线的使用。 | ||
- | |||
- | 在[[STEP-Baseboard]]上的4位数码管结构如下: | ||
- | |||
- | {{ :数码管结构.png |数码管结构}} | ||
- | <WRAP centeralign>**图2 数码管结构** </WRAP> | ||
- | |||
- | |||
- | |||
- | 因数码管上的4位需要显示不同的数字,故我们需要扫描的方式, 扫描模式采用分时的方式循环选通各个数码管,依次显示第1、第2、第3、第4位,利用人眼睛的视觉暂留效应,扫描的频率越高,数码管显示循环周期越小,当扫描频率足够高(例如当扫描频率等于100Hz)时,则在人眼看到的数码管显示就是连续的。 | ||
- | |||
- | 当扫描频率 = 100Hz,则4位数码管单次扫描周期为10ms,因为4位数码管分时显示,则每位数码管闪烁时间应为2.5ms。 | ||
- | |||
- | |||
- | ### 74HC595驱动原理 | ||
- | |||
- | ------ | ||
- | 从前面我们知道驱动一个4位数码管至少需要12根线,对于小脚丫FPGA的引脚资源来说还是很紧张的。因此在[[STEP-Baseboard]]实验平台上采用了74HC595来驱动数码管显示,这样可以有效的节约我们的管脚资源。 | ||
- | 74HC595是较为常用的串行转并行的芯片,包括一个8位移位寄存器和一个存储器,三态缓冲输出。在最简单的情况下我们只需要控制3根引脚输入得到8根引脚并行输出信号。 | ||
- | |||
- | {{ :74hc595引脚功能.jpg |74hc595引脚功能}} | ||
- | <WRAP centeralign> **图5 74hc595引脚功能** </WRAP> | ||
- | |||
- | {{ :74hc595逻辑图.jpg |74hc595逻辑图}} | ||
- | <WRAP centeralign> **图6 74hc595逻辑图** </WRAP> | ||
- | |||
- | {{ :74hc595时序图.jpg |74hc595时序图}} | ||
- | <WRAP centeralign> **图7 74hc595时序图** </WRAP> | ||
- | |||
- | 一个4位数码管至少需要12根信号线控制显示,因此[[STEP-Baseboard]]上采用了两片74HC595级联来实现控制。单个74HC595芯片8根并行输出信号更新一次需要8个SH_CP信号周期的时间长度,所以需要16个SH_CP信号周期才能完成一位数码管的显示; | ||
- | |||
- | 假如我们把每位数码管延时时间设为2.5ms(正好满足人眼暂留效应,看起来像是4位同时显示),对应16个SH_CP信号周期,则SH_CP信号周期应该为2.5ms/16 ≈ 156us,SH_CP信号的频率应该为1000000us/156us ≈ 6.4KHz,这样在程序设计的时候我们可以将SH_CP的控制信号(sclk_out)设置为6.4KHz左右。 | ||
- | |||
- | ### 硬件连接 | ||
- | |||
- | ------ | ||
- | 根据上面[[STEP-Baseboard]]采用的4为数码馆结构图可以看到,加上数码管中间的冒号显示,我们需要14根信号线控制。通过两片74HC595级联驱动实现了3根串行总线控制16根并行总线输出,有效减少了引脚需求。具体硬件连接如下所述: | ||
- | |||
- | FPGA的系统时钟来自于小脚丫FPGA开发板配置的25MHz时钟晶振,连接FPGA的C1引脚。 | ||
- | |||
- | 数码管模块电路原理图连接和八段共阴极数码管的结构分别如图所示: | ||
- | |||
- | {{ :74hc595电路连接.png |图3 74HC595电路连接}} | ||
- | <WRAP centeralign> **图3 74HC595电路连接** </WRAP> | ||
- | |||
- | {{ :数码管电路连接.png?500 |图4. 数码管电路连接}} | ||
- | <WRAP centeralign> **图4 数码管电路连接** </WRAP> | ||
- | |||
- | |||
- | 数码管模块、小脚丫与FPGA的引脚连接关系如下: | ||
- | |||
- | ^ 74HC595管脚 |11(SCK) | 12(RCK) |14(SER) | | ||
- | ^ 小脚丫管脚 |32 | 31 |30 | | ||
- | ^ FPGA管脚 |N2 |M1 |K1 | | ||
- | |||
- | |||
- | ### 代码设计 | ||
- | |||
- | ---- | ||
- | |||
- | {{ :数码管显示程序设计.jpg |数码管显示程序设计}} | ||
- | |||
- | 数码管是针对数字的显示器件,显示内容相对固定,我们首先把需要显示的内容定义为存储器,这样当我们需要显示时,只需要有存储器的地址就可以调用数据。 | ||
- | |||
- | {{:111.png|数码管字库}} | ||
- | |||
- | 我们的数码管是由芯片74HC595驱动的,根据上面计算,SH_CP的频率为6.4KHz,所以我们首先分频产生一个6.4KHz左右的信号clk_div,然后再基于这个信号产生74HC595的控制时序。 | ||
- | |||
- | 小脚丫开发板晶振为25MHz,我们需要对时钟分频得到6.4KHz左右的信号,这样分频系数为25MHz/6.4KHz ≈ 3900。 | ||
- | |||
- | parameter CLK_DIV_PERIOD=3900; | ||
- | |||
- | {{:222.png|产生clk_div=6.4KHz}} | ||
- | |||
- | 然后我们使用状态机将clk_div的高电平、低电平、上升沿和下降沿产生脉冲,方便我们后面结合上升沿和下降沿的状态完成74HC595的控制时序。 | ||
- | |||
- | {{:333.png|}} | ||
- | |||
- | 使用状态机我们将赋值和并行转串行分别完成,分为两个状态: | ||
- | - IDLE状态:分时控制四位数码管各位需要显示的内容 | ||
- | - WRITE状态:实现74HC595的控制时序,将数码管需要显示的内容发送出去 | ||
- | |||
- | {{ :数码管显示程序框图.jpg |数码管显示程序框图}} | ||
- | |||
- | * IDLE状态:分时显示的程序设计如下: | ||
- | |||
- | {{:444.jpg|IDLE状态}} | ||
- | |||
- | * WRITE状态:实现74HC595的控制时序,串行输出的程序设计如下: | ||
- | |||
- | {{:555.png|WRITE状态}} | ||
- | |||
- | 如上所述就实现了数码管的分时显示,四位数码管可以同时显示不同的数字。 | ||
- | |||
- | 设计要求还需要按秒实现0到9循环左移显示,所以我们需要生成一个1Hz的信号clk_div_1Hz,然后由clk_div_1Hz触发完成显示内容的切换。 | ||
- | |||
- | 分频产生1Hz的时钟与上面产生6.4KHz信号的程序原理相同,这里不做赘述,然后基于1Hz时钟循环产生1~9的数据并使用寄存器逐级赋值锁存,方法如下: | ||
- | |||
- | {{:666.png|显示内容生成}} | ||
- | |||
- | 引脚分配如下: | ||
- | |||
- | ^ 管脚名称 | clk_in | rst_n_in | rclk_out | sclk_out | sdio_out | | ||
- | ^ FPGA管脚 | C1 | B1 | M1 | N2 | K1 | | ||
- | |||
- | ### 系统运行 | ||
- | |||
- | {{:系统运行1.jpg?300 |系统运行1}} | ||
- | |||
- | {{:系统运行2.jpg?300 |系统运行2}} | ||
- | |||
- | {{:系统运行3.jpg?300|系统运行3}} | ||
- | |||
- | ### 资源报告 | ||
- | |||
- | ^ 资源 | 数量 | 比例 | 说明 | | ||
- | ^ LUTs | 134 | 10% | | | ||
- | ^ 寄存器 | 88 | 6% | | | ||
- | ^ 存储器 | 0 | 0% | | | ||
- | ^ IO管脚 | 5 | | | | ||
- | ^ 时钟频率 | 25MHz | | | | ||
- | |||
- | ### 知识点 | ||
- | |||
- | * 时钟分频 | ||
- | * 串行/并行转换 | ||
- | * 数码管动态显示 | ||
- | |||
- | |||
- | ### 参考文档 | ||
- | * {{:machxo2familydatasheet.pdf|Lattice MachXO2数据手册}} | ||
- | * {{:sn74hc595.pdf|74HC595数据手册}} | ||
- | |||
- | ### 相关文档 | ||
- | |||
- | ^ **文件名称** | **功能** | | ||
- | ^ **[[DLED_DISP]].v** | **数码管显示** | | ||
- | |||
- | ## 数码管显示 | ||
- | |||
- | 本实验将会让你熟悉小脚丫上最后一种有意思的外设七段数码管。 | ||
- | \\ | ||
- | |||
- | ### 1. 硬件说明 | ||
+ | ### 1. 知识点 | ||
+ | {{ :7segled.gif |}} | ||
数码管是工程设计中使用很广的一种显示输出器件。一个7段数码管(如果包括右下的小点可以认为是8段)分别由a、b、c、d、e、f、g位段和表示小数点的dp位段组成。实际是由8个LED灯组成的,控制每个LED的点亮或熄灭实现数字显示。通常数码管分为共阳极数码管和共阴极数码管,结构如下图所示: | 数码管是工程设计中使用很广的一种显示输出器件。一个7段数码管(如果包括右下的小点可以认为是8段)分别由a、b、c、d、e、f、g位段和表示小数点的dp位段组成。实际是由8个LED灯组成的,控制每个LED的点亮或熄灭实现数字显示。通常数码管分为共阳极数码管和共阴极数码管,结构如下图所示: | ||
行 191: | 行 21: | ||
{{ ::timg.jpg |}} | {{ ::timg.jpg |}} | ||
\\ | \\ | ||
- | 这其实是一个4-16译码器,如果我们想数码管能显示16进制可以全译码,如果只想显示数字,可以只利用其中10个译码,下面看看如果用Verilog来实现。 | + | 这其实是一个4-16译码器 |
- | ### 2. Verilog代码 | + | ### 2. CircuitJS中的电路仿真 |
+ | {{ :7seg-js.mp4 |}} <WRAP centeralign> 在CircuitJS中仿真的效果 </WRAP> | ||
+ | |||
+ | |||
+ | ### 3. 原理图 | ||
+ | 在本实验中我们使用了核心板上的4个开关SW和4个按键KEY来分别控制2个数码管的显示,通过开关和按键的组合得到不同的显示数值。 | ||
+ | |||
+ | 要注意的是开关和按键的状态正好相反: | ||
+ | * 缺省状态时开关为低电平,按下开关时该端口变为高电平 | ||
+ | * 缺省状态时按键为高电平,按下按键时该端口变为低电平 | ||
+ | |||
+ | {{drawio>7_seg_x2_block.png}} | ||
+ | |||
+ | ### 4. Verilog代码 | ||
+ | 如果我们想数码管能显示16进制可以全译码,如果只想显示数字,可以只利用其中10个译码,下面看看如果用Verilog来实现。 | ||
<code verilog> | <code verilog> | ||
行 210: | 行 54: | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
// Version: |Mod. Date: |Changes Made: | // Version: |Mod. Date: |Changes Made: | ||
- | // V1.0 |2017/03/02 |Initial ver | + | // V1.0 |2021/10/08 |Initial ver |
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
// Module Function:数码管的译码模块初始化 | // Module Function:数码管的译码模块初始化 | ||
行 216: | 行 60: | ||
module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2); | module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2); | ||
- | input [3:0] seg_data_1; //数码管需要显示0~9十个数字,所以最少需要4位输入做译码 | + | input [3:0] seg_data_1; //数码管需要显示0~9十个数字,所以最少需要4位输入做译码 |
- | input [3:0] seg_data_2; //小脚丫上第二个数码管 | + | input [3:0] seg_data_2; //小脚丫上第二个数码管 |
- | output [8:0] seg_led_1; //在小脚丫上控制一个数码管需要9个信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A | + | output [8:0] seg_led_1; //在小脚丫上控制一个数码管需要9个信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A |
- | output [8:0] seg_led_2; //在小脚丫上第二个数码管的控制信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A | + | output [8:0] seg_led_2; //在小脚丫上第二个数码管的控制信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A |
- | reg [8:0] seg [9:0]; //定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽 | + | reg [8:0] seg [9:0]; //定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽 |
| | ||
- | initial //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial | + | initial //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial |
- | //initial和always不同,其中语句只执行一次 | + | //initial和always不同,其中语句只执行一次 |
begin | begin | ||
- | seg[0] = 9'h3f; //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字 0 | + | seg[0] = 9'h3f; //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字 0 |
- | seg[1] = 9'h06; //7段显示数字 1 | + | seg[1] = 9'h06; //7段显示数字 1 |
- | seg[2] = 9'h5b; //7段显示数字 2 | + | seg[2] = 9'h5b; //7段显示数字 2 |
- | seg[3] = 9'h4f; //7段显示数字 3 | + | seg[3] = 9'h4f; //7段显示数字 3 |
- | seg[4] = 9'h66; //7段显示数字 4 | + | seg[4] = 9'h66; //7段显示数字 4 |
- | seg[5] = 9'h6d; //7段显示数字 5 | + | seg[5] = 9'h6d; //7段显示数字 5 |
- | seg[6] = 9'h7d; //7段显示数字 6 | + | seg[6] = 9'h7d; //7段显示数字 6 |
- | seg[7] = 9'h07; //7段显示数字 7 | + | seg[7] = 9'h07; //7段显示数字 7 |
- | seg[8] = 9'h7f; //7段显示数字 8 | + | seg[8] = 9'h7f; //7段显示数字 8 |
- | seg[9] = 9'h6f; //7段显示数字 9 | + | seg[9] = 9'h6f; //7段显示数字 9 |
end | end | ||
| | ||
- | assign seg_led_1 = seg[seg_data_1]; //连续赋值,这样输入不同四位数,就能输出对于译码的9位输出 | + | assign seg_led_1 = seg[~seg_data_1]; //连续赋值,这样输入不同四位数,就能输出对于译码的9位输出 |
assign seg_led_2 = seg[seg_data_2]; | assign seg_led_2 = seg[seg_data_2]; | ||
endmodule | endmodule | ||
- | |||
</code> | </code> | ||
- | \\ | + | 注意最后的两句连续赋值语句的不同,原因在于4个按键和4个开关的缺省状态正好相反,所以对于第一个数码管的4个输入(对应于4个按键),需要先反相,相当于加了4个1位的反相器。 |
- | \\ | + | |
- | ### 3. 引脚分配 | + | 在Web IDE里面的效果如下: |
+ | {{ :7_seg_verilog.png |}} <WRAP centeralign> 在Web IDE中的Verilog代码窗口 </WRAP> | ||
+ | |||
+ | |||
+ | ### 5. 管脚分配 | ||
+ | 利用小脚丫上的4路按键(key)和4路开关(sw),作为输入信号分别控制2个数码管的显示输出。在Web IDE中的管脚分配见如下图: | ||
+ | {{ :7_seg_pinout.png |}} <WRAP centeralign>在Web IDE中2个数码管的控制管脚分配 </WRAP> | ||
+ | |||
+ | |||
+ | |||
+ | ### 6. 功能验证 | ||
+ | 将生成的JED文件发送到小脚丫FPGA的STEPFPGA盘中,即可完成对其编程,编程后的效果如下: | ||
- | 小脚丫上正好有4路按键和4路开关,可以用来作为输入信号分别控制数码管的输出。按照下面表格定义输入输出信号 | ||
- | \\ | ||
- | ^信号 ^引脚 ^信号 ^引脚 ^ | ||
- | |seg_data_1(0) |M7 |seg_data_2(0) |L14 ^ | ||
- | |seg_data_1(1) |M8 |seg_data_2(1) |M13 ^ | ||
- | |seg_data_1(2) |M9 |seg_data_2(2) |M14 ^ | ||
- | |seg_data_1(3) |M10 |seg_data_2(3) |N14 ^ | ||
- | \\ | ||
- | \\ | ||
- | ^信号 ^引脚 ^信号 ^引脚 ^ | ||
- | |seg_led_1(0) |A10 |seg_led_2(0) |C12 ^ | ||
- | |seg_led_1(1) |C11 |seg_led_2(1) |B14 ^ | ||
- | |seg_led_1(2) |F2 |seg_led_2(2) |J1 ^ | ||
- | |seg_led_1(3) |E1 |seg_led_2(3) |H1 ^ | ||
- | |seg_led_1(4) |E2 |seg_led_2(4) |H2 ^ | ||
- | |seg_led_1(5) |A9 |seg_led_2(5) |B12 ^ | ||
- | |seg_led_1(6) |B9 |seg_led_2(6) |A11 ^ | ||
- | |seg_led_1(7) |F1 |seg_led_2(7) |K1 ^ | ||
- | |seg_led_1(8) |C9 |seg_led_2(8) |A12 ^ | ||
- | \\ | ||
- | 配置好以后编译下载程序。这样可以通过按键或者开关来控制相应的数码管显示数字。如果你想显示16进制的AbCDeF在数码管,可以试试修改程序。这时候一定要定义一个16*9的存储器来初始化。 | ||
- | \\ | ||
- | ### 4. 小结 | + | ### 7. 扩展使用 |
+ | 小脚丫FPGA核心板对外的扩展连接只需要36根IO,但采用了132BGA封装的芯片,在核心板上有足够的管脚用于核心板上几个器件(LED、数码管、开关、按键)的使用,因此核心板上的2个7段数码管采用了FPGA的管脚直接连接的独立显示模式。 | ||
- | 了解了小脚丫数码管的工作原理,在下个实验我们将进行到有趣的时序逻辑。首先是如何控制[[5. 时钟分频|时钟分频]]。 | + | 但大多数应用场景需要用较少的IO管脚也能够驱动多个数码管,比如4个、6个乃至10个,如何才能实现?那就需要扫描显示的模式,即所有数码管共用8根数据信号线,各自再有1根选通信号线,采用分时的方式循环选通各位数码管,比如驱动4个数码管仅需要8+4 = 12根信号线进行显示。利用人眼的“视觉暂留效应”,可以使数码管显示看起来是同时显示。这种扫描显示模式实现起来相对比较复杂,但是节约信号线的使用。这种显示方式会在后面掌握了时序逻辑的基本概念以后再进行实现。 |
+ | 在实际的产品中,我们还可以使用专用的数码管驱动器件来实现,比如74HC595串行变并行的芯片、南京沁恒公司推出的[[http://www.wch.cn/products/CH450.html|数码管显示驱动芯片CH450]]等。 |