本帖最后由 Zhao_kar 于 2023-12-9 16:11 编辑
【Tang Primer25K Dock】三——数码管基本显示和原理
备注:
- 本节主要为数码管的基本测试,单独测试数码管从0-9+led的辅助显示的测试,同时计数满一个周期,切换到下一个数码管显示;
- 前面大概说过这个数码管的原理,这个数码管只有一个位选信号,位选信号的高低电平分别选择两个数码管的显示,与常规的一个数码管由一个位选信号决定亮灭不太一样
- 下一节为数码管的动态显示电路,从00到11,从而到99,去做一个基础的动态显示测试。
一、数码管原理
- 根据从官方下载的原理图可以看出,本次测试使用的是八段数码管,八段数码管如下所示(原理图),是一个8字型的数码管,分为八段,对应abcdefg和小数点dp,每一段就是一个发光二极管,这样的八段我们称为段选,实际上,每个段选实际上就是控制着led的亮灭。
- 数码管分为共阳极和共阴极,具体原因就不仔细解释了,这个是可以根据原理图判断的,而sipeed的pmod是共阳极的,根据共阳极的表,你可以根据表来进行操作,比如我想让他显示1,那就是让数码管的右边的两个led亮就行了,如下图(显示图)
- 根据表,可以得到我们想要的实际数据,比如我想让数码管显示0,就对七个段选给信号就行了,这边以abcdefg为例,应该为7'b0000001,但是这个数码管,如果按照实际的表来,你会发现他是反过来的,为了契合我们的25k开发板,pmod朝下,所以我借鉴了官方例程的值,如下。
- 说了这么多,其实简单表述就是,你给一个七位的段选值,让数码管显示对应的值,而具体的效果你可以根据原理图操作,可以查表直接用,这个一般是固定的,没有什么特别的。
/***************parameter*************/
localparam P_0 = 7'b0000001 ;
localparam P_1 = 7'b1111001 ;
localparam P_2 = 7'b0010010 ;
localparam P_3 = 7'b0110000 ;
localparam P_4 = 7'b1101000 ;
localparam P_5 = 7'b0100100 ;
localparam P_6 = 7'b0000100 ;
localparam P_7 = 7'b1110001 ;
localparam P_8 = 7'b0000000 ;
localparam P_9 = 7'b0100000 ;
localparam P_X = 7'b1111111 ;
/***************reg*******************/
二、时序设计实现
- 前面两节都没有时序逻辑的解释,而这一节大概讲讲,首先根据需求去设计,我们要做的是一个0-9的显示,其实简单说就是一个0-9的计数器,外加led的显示和数码管的位选信号的测试。
- 既然要0-9变化,所以要给一个1s的信号,而这个1s的信号(应该说时钟信号),用他来决定计数时间,比如我在一个always里面,如果使用的系统时钟(上升沿下降沿不解释了,先知道一般时钟上升沿触发,rst下降沿触发就行了),那么这一部分的执行,会在每次时钟上升沿来临时执行一次,所以,如果实现0-9的计数器,我们需要一个中间值,在1s信号来临的时候,计数1次,以此类推,当计数到9时,再执行清零。
- 阻塞与非阻塞这个比较麻烦,我不觉得我能短篇幅内解释清楚,这里给出一个我个人的理解:先看下面两个代码和一张图,关键是看时序图,在这张图里面,阻塞赋值部分,两个时钟输出是一样的,而非阻塞的out2要慢一个时钟周期,这个就是区别,具体为,always语句过程种,阻塞赋值时,block1已经=block_in了,然后到b2=b1,此时b1已经为in的值了,所以两个值是一样的;而非阻塞赋值,b1=b_in时,值不会马上变,然后到b2=b1,此时b2等于的是还没上升的b1,所以后续体现为0,而这个变化会在always结束后体现,也就是值的变化操作在always结束后执行,而在虽然b1=b_in了,但是always还没结束,此时b1还是0,而b2等于的是结束前的b1,所以b2会比b1慢一个时钟周期变化。对于阻塞部分,因为是在进行的过程就进行了操作,也就是b2=b1时,b1已经等于b-in了。
- 前面都是基础知识,现在讲关键,分频的实现,总所周知这个是50M的系统时钟,我们要把他变成1hz的时钟,也就是1/50_000_000s * cnt =1s,由此得计数的cnt值为50_000_000,但是计数1s实际上应该给0.5s的显示,因为两个电平,所以最后cnt应该为25_000_000。
三、代码+注释
module digital_cnt
#(
parameter CNT_1HZ=28'd24_999_999//计数值
)
(
input clk,//输入时钟
input rst,//复位
output[7:0]led,//8个led,忘记改了,其实二进制显示4个就行
output reg [6:0]seg_n,//七个位选,决定数码管显示的值
output sel
);
/***************parameter*************/
localparam P_0 = 7'b0000001 ;
localparam P_1 = 7'b1111001 ;
localparam P_2 = 7'b0010010 ;
localparam P_3 = 7'b0110000 ;
localparam P_4 = 7'b1101000 ;
localparam P_5 = 7'b0100100 ;
localparam P_6 = 7'b0000100 ;
localparam P_7 = 7'b1110001 ;
localparam P_8 = 7'b0000000 ;
localparam P_9 = 7'b0100000 ;
localparam P_X = 7'b1111111 ;
/***************reg*******************/
reg [27:0] cnt_1hz;//1hz的寄存计数器
reg clk_1hz=1'b0;
//分1s时钟
always @(posedge clk or negedge rst)
if (rst == 1'b0) begin//如果按下复位按键,此时cnt为0,重置
cnt_1hz <= 28'd0;
end else if (cnt_1hz == CNT_1HZ) begin//如果计数到最大值清零
cnt_1hz <= 28'd0;
clk_1hz = ~clk_1hz;//翻转
end else begin
cnt_1hz <= cnt_1hz + 1'b1;//加1计数,一个clk加一次
end
//数码管计数0-9
reg [3:0] data_temp;
wire [3:0] data;//值
reg sel_temp;//sel的选择值,以数码管显示0-9为一个周期,变化一次
assign data=data_temp;
always@(posedge clk_1hz or negedge rst)
if(rst==1'b0)
data_temp<=4'b0000;
else if(data_temp==4'b1001)begin//如果等于9
data_temp<=4'b0000;//数码管中间值为0
sel_temp <= ~sel_temp;位选翻转
end else
data_temp<=data_temp+1'b1;//+1
//led的计数,原理跟数码管一样
reg [3:0] led_out;
assign led=~led_out;
always@(posedge clk_1hz or negedge rst)
if(rst==1'b0)
led_out<=4'b0000;
else if(led_out==4'b1001)
led_out<=4'b0000;
else
led_out<=led_out+1'b1;
//数码管数据选择,译码部分,见第二节
always @ (data)
case(data)
4'b0000: seg_n <= P_0;
4'b0001: seg_n <= P_1;
4'b0010: seg_n <= P_2;
4'b0011: seg_n <= P_3;
4'b0100: seg_n <= P_4;
4'b0101: seg_n <= P_5;
4'b0110: seg_n <= P_6;
4'b0111: seg_n <= P_7;
4'b1000: seg_n <= P_8;
4'b1001: seg_n <= P_9;
default: seg_n <= P_X;
endcase
assign sel=sel_temp;//数码管使能,默认
endmodule
四、引脚约束(其实变动的只有数码管)
1、code
//Copyright (C)2014-2023 Gowin Semiconductor Corporation.
//All rights reserved.
//File Title: Physical Constraints file
//GOWIN Version: V1.9.9 Beta-5
//Part Number: GW5A-LV25MG121NES
//Device: GW5A-25
//Device Version: A
//Created Time: Fri 12 08 18:00:14 2023
IO_LOC "sel" K5;
IO_PORT "sel" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[6]" L11;
IO_PORT "seg_n[6]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[5]" K11;
IO_PORT "seg_n[5]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[4]" L5;
IO_PORT "seg_n[4]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[3]" E10;
IO_PORT "seg_n[3]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[2]" E11;
IO_PORT "seg_n[2]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[1]" A11;
IO_PORT "seg_n[1]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg_n[0]" A10;
IO_PORT "seg_n[0]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[7]" C10;
IO_PORT "led[7]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[6]" C11;
IO_PORT "led[6]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[5]" B10;
IO_PORT "led[5]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[4]" B11;
IO_PORT "led[4]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[3]" D10;
IO_PORT "led[3]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[2]" D11;
IO_PORT "led[2]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[1]" G10;
IO_PORT "led[1]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led[0]" G11;
IO_PORT "led[0]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "rst" F5;
IO_PORT "rst" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "clk" E2;
IO_PORT "clk" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
2、foolplanner图
五、演示,详情见视频
PS:在宿舍录的,背景很吵,请把声音关掉观看。
0-9显示