74HC595大概原理
具有串行=转并功能,通过移位寄存器和输出锁存器实现。驱动开发板上的8位数码管。
FPGA需要通过74HC595这个芯片把16位的数据(SEL + SEG)变为并行端口用来驱动数码管
74HC595的波形图
如图可知:
输入数据DS,先输入的数据,在8个时间周期之后,在Q7上
数码管结构
此结构为一个SPI协议接口
第一片74HC595 作为位选信号
第二片作为段选
要求:对于74HC595芯片,该芯片在SH_CP(SCLK)的上升沿将(DIO)上的数据移入内部寄存器。
目的:因此我们需要保证DS上的数据在SH_CP上升沿前后的一段时间,保持稳定。
手段:FPGA要在SH_CP的下降沿改变DS的值。
设SH_CP时钟的频率为12.5HZ
根据波形,找到SH_CP的最小时间周期,以这个周期为基准来描述DS ST_CP 的变化,类似于此
则此信号频率应该为SH_CP的二倍
代码如下:
数码管显示模块:
module hex8_2(
Clk,
Reset_n,
Disp_Data,
SEL,
SEG
);
input Clk;
input Reset_n;
input [31:0]Disp_Data;
output reg[7:0]SEL;
output reg[7:0]SEG;//seg[0]-a,seg[1]-b...seg[7]-h
reg clk_1k;
reg [15:0]div_cnt;
always@(posedge Clk or negedge Reset_n) //1000000/20/2*2=50000
if(!Reset_n)
div_cnt <= 0;
else if(div_cnt >= 49999)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
always@(posedge Clk or negedge Reset_n) //使能时钟信号
if(!Reset_n)
clk_1k <= 0;
else if(div_cnt == 49999)
clk_1k <= 1'b1;
else
clk_1k <= 0;
reg [2:0]num_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
num_cnt <= 0;
else if(clk_1k)
num_cnt <= num_cnt + 1'b1;
always@(posedge Clk)
case(num_cnt)
0: SEL = 8'b00000001;
1: SEL = 8'b00000010;
2: SEL = 8'b00000100;
3: SEL = 8'b00001000;
4: SEL = 8'b00010000;
5: SEL = 8'b00100000;
6: SEL = 8'b01000000;
7: SEL = 8'b10000000;
endcase
reg[3:0]disp_tmp;
always@(posedge Clk)
case(num_cnt)
7: disp_tmp = Disp_Data[31:28];
6: disp_tmp = Disp_Data[27:24];
5: disp_tmp = Disp_Data[23:20];
4: disp_tmp = Disp_Data[19:16];
3: disp_tmp = Disp_Data[15:12];
2: disp_tmp = Disp_Data[11:8];
1: disp_tmp = Disp_Data[7:4];
0: disp_tmp = Disp_Data[3:0];
endcase
always@(posedge Clk)
case(disp_tmp)
0: SEG = 8'hc0;
1: SEG = 8'hf9;
2: SEG = 8'ha4;
3: SEG = 8'hb0;
4: SEG = 8'h99;
5: SEG = 8'h92;
6: SEG = 8'h82;
7: SEG = 8'hf8;
8: SEG = 8'h80;
9: 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;
endcase
endmodule
驱动模块:
`timescale 1ns / 1ps
module HC595_Driver(
Clk,
Reset_n,
Data,
S_EN,
SH_CP,
ST_CP,
DS
);
input Clk;
input Reset_n;
input [15:0]Data;
input S_EN;
output reg SH_CP;
output reg ST_CP;
output reg DS;
parameter CNT_MAX = 2;
reg [15:0]r_data;
always@(posedge Clk)
if(S_EN)
r_data <= Data;
reg [7:0]divider_cnt;//分频计数器,得到最小的那个使能时间单位
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
divider_cnt <= 0;
else if(divider_cnt == CNT_MAX - 1'd1)
divider_cnt <= 0;
else
divider_cnt <= divider_cnt + 1'b1;
wire sck_plus; //产生这个两倍时钟信号
assign sck_plus = (divider_cnt == CNT_MAX - 1'd1);
reg [5:0]SHCP_EDGE_CNT;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
SHCP_EDGE_CNT <= 0;
else if(sck_plus)begin
if(SHCP_EDGE_CNT == 6'd32) //传16位数据,需要32位计数
SHCP_EDGE_CNT <= 0;
else
SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
SH_CP <= 0;
ST_CP <= 0;
DS <= 0;
end
else begin
case(SHCP_EDGE_CNT)
0: begin SH_CP <= 0;ST_CP <= 1'd0;DS <= r_data[15];end
1: SH_CP <= 1'd1;
2: begin SH_CP <= 0;DS <= r_data[14];end
3: SH_CP <= 1'd1;
4: begin SH_CP <= 0;DS <= r_data[13];end
5: SH_CP <= 1'd1;
6: begin SH_CP <= 0;DS <= r_data[12];end
7: SH_CP <= 1'd1;
8: begin SH_CP <= 0;DS <= r_data[11];end
9: SH_CP <= 1'd1;
10: begin SH_CP <= 0;DS <= r_data[10];end
11: SH_CP <= 1'd1;
12: begin SH_CP <= 0;DS <= r_data[9];end
13: SH_CP <= 1'd1;
14: begin SH_CP <= 0;DS <= r_data[8];end
15: SH_CP <= 1'd1;
16: begin SH_CP <= 0;DS <= r_data[7];end
17: SH_CP <= 1'd1;
18: begin SH_CP <= 0;DS <= r_data[6];end
19: SH_CP <= 1'd1;
20: begin SH_CP <= 0;DS <= r_data[5];end
21: SH_CP <= 1'd1;
22: begin SH_CP <= 0;DS <= r_data[4];end
23: SH_CP <= 1'd1;
24: begin SH_CP <= 0;DS <= r_data[3];end
25: SH_CP <= 1'd1;
26: begin SH_CP <= 0;DS <= r_data[2];end
27: SH_CP <= 1'd1;
28: begin SH_CP <= 0;DS <= r_data[1];end
29: SH_CP <= 1'd1;
30: begin SH_CP <= 0;DS <= r_data[0];end
31: SH_CP <= 1'd1;
32: ST_CP <= 1'd1;
default:
begin
SH_CP <= 0;
ST_CP <= 0;
DS <= 0;
end
endcase
end
endmodule
测试模块:
module hex8_test(
Clk,
Reset_n,
SEL,
SEG,
SH_CP,
ST_CP,
DS
);
input Clk;
input Reset_n;
output [7:0]SEL;
output [7:0]SEG;//seg[0]-a,seg[1]-b...seg[7]-h
output SH_CP;
output ST_CP;
output DS;
wire [31:0]Disp_Data;
hex8_2 hex8_2(
Clk,
Reset_n,
Disp_Data,
SEL,
SEG
);
wire [15:0]Data;
assign Data = {SEG,SEL};
wire S_EN;
assign S_EN = 1;
HC595_Driver HC595_Driver(
Clk,
Reset_n,
Data,
S_EN,
SH_CP,
ST_CP,
DS
);
assign Disp_Data = 32'h13579bdf;
endmodule
TB文件:
`timescale 1ns / 1ps
module hex8_tb();
reg Clk;
reg Reset_n;
reg [31:0]Disp_Data;
wire [7:0]SEL;
wire [7:0]SEG;
hex8 hex8(
Clk,
Reset_n,
Disp_Data,
SEL,
SEG
);
initial Clk = 1;
always#10 Clk = ~Clk;
initial begin
Reset_n = 0;
Disp_Data = 32'h00000000;
#201;
Reset_n = 1;
#2000;
Disp_Data = 32'h12345678;
#10000000;
Disp_Data = 32'h9abcdef0;
#10000000;
$stop;
end
endmodule