项目名称
issp数码管显示
具体要求
对数码管驱动电路进行设计,并利用In system sources and probes editor(ISSP)输入显示的数据,在数码管上正常显示
设计说明
本设计采用的是8位8段的数码管(8位代表8个数码管),每个数码管的结构图如下,下图为共阳极数码管,共阴极与共阳极数码管的的区别在于,公共端是接到高电平还是低电平。数码管的那个段需要点亮时,只需要给所对应的段给低电平例如如果要显示数字0,只需将a、b、c、d、e、f置0,其他段置1即可。
数码管可以显示0-F, 共阳极数码管显示的译码格式如下。
显示的内容 | 段码(二进制) | 段码(16进制) | |||||||
a | b | c | d | e | f | g | h | ||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 8'hc0 |
1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 8'hf9 |
2 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 8'ha4 |
3 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 8'hb0 |
4 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 8'h99 |
5 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 8'h92 |
6 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 8'h82 |
7 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 8'hf8 |
8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 8'h80 |
9 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 8'h90 |
a | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 8'h88 |
b | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 8'h83 |
c | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 8'hc6 |
d | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 8'ha1 |
e | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 8'h86 |
f | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 8'h8e |
一般为了节约IO口的资源,数码管 的段选一般接在一起,如果要点亮第一个和第三个数码管,只需要将所对应的位选打开,根据要显示的内容打开段选,但是如果说要同时只显示第一个码管的a段和第二个数码管的b段就没办法实现,因为打开位选之后,打开a段之后,第二个数码管的a段也是打开的。所以就要利用人眼的视觉暂留效果,可以让第一个数码管显示a段,等待1ms之后再显示第二个数码管的b段,在人眼睛看到的是同时显示的。
数码管驱动模块设计架构
cnt产生1ms的标志信号,每个1ms到来改变一次数码管的位选,进行移位。 data_in为数码管输入的内容,经过位选之后,输出选择的数码管的内容,数码管的内容经过lut进行译码转化为16进制显示。
由于本次输出的seg和sel采用两块8位74HC595芯片串转并进行输出,节约IO资源。将两块芯片看成一个模块,则为16位的串转并进行输出。ds是数据端口串行数据输入,shcp为移位寄存器的时钟输入,stcp存储寄存器时钟输入。
下图为部分时序图,当shcp为时钟下降沿时,输入数据的最高位,数据最高位最终从第二个移位寄存器进行输出到段选所以输入数据的组成为{seg,sel}, ds在每个shcp的下降沿进行输入数据,上升沿时数据保持不变,由于总共要输入16位数据,所以需要16个下降沿,待16位数据全部输入后,再来一个时钟上升沿数据并行输出。
具体的设计过程是 ,所需要的shcp的时钟设置为12.5MHz,所以先生成12.5M的时钟,由于每个下降沿需要对数据进行输入,上升沿保持不变。设计一个边沿检测电路对shcp的上升沿和下降沿进行检测 ,需要检测到32个,因为从0-31计数进行数据输入,开始计数的时候是从下降沿开始计数,在计数31时刚好也是一个下降沿,下一个上升沿 stcp输出一个高电平,数据进行并行输出 。
这里要注意的是第一次计数是从下降沿开始计数,之后是来一个下降沿或者上升沿计数器加一,这里笔者采用状态机来实现,可能还有更多的方法,但是笔者习惯使用状态机,因为百分之90的逻辑都可以用状态机来实现。这里普通的计数器没办法实现,第一个状态先检查下降沿,检测到之后,跳转状态2计数器加1,检测到有上升沿或者下降沿并且未计满32,计数器加1, 如果没有检测到上升沿或者下降沿继续等待,如果检测到了,并且计满32返回状态1。
生成issp的ip核
这里没有用到probe,将它的位宽设置为0,将源位宽(数据输入)定义为32,创建好ip核之后,ctrl+o快捷键可以快速打开文件。
代码设计
顶层模块设计
module sel_seg_top(
input clk,
input rst_n,
output shcp,
output stcp,
output ds
);
wire [31:0] data_in;
issp issp(
.probe(),
.source(data_in)
);
wire [7:0] sel;
wire [7:0] seg;
sel_seg sel_seg(
.clk(clk),
.rst_n(rst_n),
.data_in(data_in),
.sel(sel),
.seg(seg)
);
hc595 hc595(
.clk(clk),
.rst_n(rst_n),
.data({seg,sel}),
.shcp(shcp),
.ds(ds),
.stcp(stcp)
);
endmodule
数码管显示模块
module sel_seg(
input clk,
input rst_n,
input [31:0]data_in,
output [7:0] sel,
output reg[7:0] seg
);
//每1ms改变一下位选
reg [19:0] cnt;
localparam cnt_top=20'd50000;
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=0;
else if(cnt<cnt_top-1)
cnt<=cnt+1;
else
cnt<=0;
wire delay_1ms_done=(cnt==cnt_top-1)?1:0;
//数码管位选移位,每隔1ms改变一个数码管显示
reg [7:0] sel_r;
always@(posedge clk or negedge rst_n)
if(!rst_n)
sel_r<=8'b0000_0001;
else if(delay_1ms_done)begin
if(sel_r==8'b1000_0000)
sel_r<=8'b0000_0001;
else
sel_r<=sel_r<<1;
end
else
sel_r<=sel_r;
选择要显示的数码管
reg [3:0] disp;//显示的数码管
always@(*)
begin
case(sel_r)
8'b0000_0001:disp<=data_in[3:0];
8'b0000_0010:disp<=data_in[7:4];
8'b0000_0100:disp<=data_in[11:8];
8'b0000_1000:disp<=data_in[15:12];
8'b0001_0000:disp<=data_in[19:16];
8'b0010_0000:disp<=data_in[23:20];
8'b0100_0000:disp<=data_in[27:24];
8'b1000_0000:disp<=data_in[31:28];
default:disp<=0;
endcase
end
//选择的数码管显示的内容
always@(*)
if(!rst_n)
seg<=0;
else begin
case(disp)
4'h0:seg<=8'hc0;
4'h1:seg<=8'hf9;
4'h2:seg<=8'ha4;
4'h3:seg<=8'hb0;
4'h4:seg<=8'h99;
4'h5:seg<=8'h92;
4'h6:seg<=8'h82;
4'h7:seg<=8'hf8;
4'h8:seg<=8'h80;
4'h9:seg<=8'h90;
4'ha:seg<=8'h88;
4'hb:seg<=8'h83;
4'hc:seg<=8'hc6;
4'hd:seg<=8'ha1;
4'he:seg<=8'h86;
4'hf:seg<=8'h8e;
default:seg<=0;
endcase
end
assign sel=sel_r;
endmodule
位选和段选数据串转并进行输出
module hc595(
input clk,
input rst_n,
input [15:0]data,
output shcp,
output reg ds,
output reg stcp
);
//生成12.5M的时钟
reg [3:0] cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=0;
else if(cnt<3)
cnt<=cnt+1;
else
cnt<=0;
wire shcp_clk=(cnt<2)?1:0;
assign shcp=shcp_clk;
//边沿检测
reg shcp_clk_c1;
reg shcp_clk_c2;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
shcp_clk_c1<=0;
shcp_clk_c2<=0;
end
else begin
shcp_clk_c1<=shcp_clk;
shcp_clk_c2<=shcp_clk_c1;
end
wire nedge=shcp_clk_c2 && (~shcp_clk_c1);
wire pedge=shcp_clk_c1 && (~shcp_clk_c2);
//对shcp时钟上升沿和下降沿进行计数,采用状态机进行实现
reg [5:0] shcp_cnt;
reg state;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
shcp_cnt<=0;
state<=0;
end
else begin
case(state)
0: begin
if(nedge)begin
shcp_cnt<=shcp_cnt+1;
state<=1;
end
else
state<=0;
end
1: begin
if(nedge || pedge)begin
if(shcp_cnt<32)
shcp_cnt<=shcp_cnt+1'b1;
else
shcp_cnt<=0;
end
else
state<=state;
end
default:;
endcase
end
//---------------------------------------------//
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
stcp<=0;
ds<=0;
end
else
begin
case(shcp_cnt)
6'd0 : begin ds<=data[15];stcp<=0; end
6'd1 : ;
6'd2 : ds<=data[14];
6'd3 : ;
6'd4 : ds<=data[13];
6'd5 : ;
6'd6 : ds<=data[12];
6'd7 : ;
6'd8 : ds<=data[11];
6'd9 : ;
6'd10 : ds<=data[10];
6'd11 : ;
6'd12 : ds<=data[9];
6'd13 : ;
6'd14 : ds<=data[8];
6'd15 : ;
6'd16 : ds<=data[7];
6'd17 : ;
6'd18 : ds<=data[6];
6'd19 : ;
6'd20 : ds<=data[5];
6'd21 : ;
6'd22 : ds<=data[4];
6'd23 : ;
6'd24 : ds<=data[3];
6'd25 : ;
6'd26 : ds<=data[2];
6'd27 : ;
6'd28 : ds<=data[1];
6'd29 : ;
6'd30 : ds<=data[0];
6'd31 : ;
6'd32 : stcp=1;
default:begin ds<=0;stcp<=0; end
endcase
end
endmodule
利用issp进行显示
全编译并配置好代码之后,打开tool->issp,在name中将数据格式改为16进制,随便输入要显示的数据,可以正常显示。