1. 串行移位寄存器原理(以四个移位寄存器为例)
- 通过移位寄存器实现串转并:一个数据输入端口可得到四位并行数据。
通过给data输送0101数据,那么在经过四个时钟周期后,与data相连的四个寄存器的输出端口得到了0101这样的数据,然后我们将latch信号拉高,在下一个时钟周期,D0, D1, D2, D3同时分别获得了这四个数据1010。(其中DFF指D触发器,LATCH信号也可连接锁存器来控制输出)
- 级联
级联:数据输出端口作为另外的移位寄存器数据端的输入。
2.使用74HC595驱动数码管
- ACX720上不是直接用fpga的管脚驱动数码管的,而是用74HC595这样的一种串转并的串行移位寄存器来驱动的
2.1 74HC595端口图:
2.2 74HC595时序图
2.3 74HC595时钟工作频率
取工作频率为12.5MHz。
3. 使用74HC595芯片驱动数码管的verilog代码实现
1.要完成的模块为hc595_driver,实现将16位并行数据转为串行数据发送至74HC595中,实现三线制控制数码管。因此我们的输入需遵循74HC595时序图。
3.1 设计代码
我们要输出shcp,stcp和ds,并满足它们的时序波形(照葫芦画瓢)。shcp的为最小的时间单元,我们可以根据它来作为其他信号波形的参照,但是切记不可将其作为门控时钟,我们只需要把这些要输出的信号当成普通信号,按照时序图上的时序输出即可。
-
shcp的时钟频率为12.5MHz,即一个周期为80ns,我们取半个周期为最小时间单元进行变化,即40ns。
-
根据最小时间单元产生对应的节点,需要一个计数器记录这些节点(节点执行完开始下一次节点执行),产生节点后再在每个节点做相应的事情即可。
3. hc595_driver里的寄存器是从小到大排列的(0到15),即第0位数据最后进,放在最外面,第15位数据放在最里面。先进15,最后进0。
cpp
module hex8_2(
clk,
rstn,
disp_data,
sel,
led
);
parameter times = 50000; // 1ms
input clk;
input rstn;
input [31:0]disp_data;
output reg [7:0] sel;
output reg [7:0] led;
reg [15:0]div_cnt;
always@(posedge clk or negedge rstn)
if(!rstn)
div_cnt <= 0;
else if(div_cnt >= times - 1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'd1;
//使能时钟
reg clk_lk;
always@(posedge clk or negedge rstn)
if(!rstn)
clk_lk <= 0;
else if(div_cnt == times - 1)
clk_lk <= 1'd1;
else
clk_lk <= 0;
//cnt累加器
reg [2:0] num_cnt;
always@(posedge clk_lk or negedge rstn)
if(!rstn)
num_cnt <= 0;
else if(clk_lk == 1)
num_cnt <= num_cnt + 1'd1;
//三八译码器
always@(posedge clk or negedge rstn)
if(!rstn)
sel <= 0;
else case(num_cnt)
0:sel = 8'b0000_0001;
1:sel = 8'b0000_0010;
2:sel = 8'b0000_0100;
3:sel = 8'b0000_1000;
4:sel = 8'b0001_0000;
5:sel = 8'b0010_0000;
6:sel = 8'b0100_0000;
7:sel = 8'b1000_0000;
endcase
//八选一多路器
reg [3:0]disp_tmp;
always@(posedge clk)
case(num_cnt)
0:disp_tmp = disp_data[3:0];
1:disp_tmp = disp_data[7:4];
2:disp_tmp = disp_data[11:8];
3:disp_tmp = disp_data[15:12];
4:disp_tmp = disp_data[19:16];
5:disp_tmp = disp_data[23:20];
6:disp_tmp = disp_data[27:24];
7:disp_tmp = disp_data[31:28];
endcase
//四十六译码器
always@(posedge clk)
case(disp_tmp)
0:led = 8'hc0;
1:led = 8'hf9;
2:led = 8'ha4;
3:led = 8'hb0;
4:led = 8'h99;
5:led = 8'h92;
6:led = 8'h82;
7:led = 8'hf8;
8:led = 8'h80;
9:led = 8'h90;
4'ha:led = 8'h88;
4'hb:led = 8'h83;
4'hc:led = 8'hc6;
4'hd:led = 8'ha1;
4'he:led = 8'h86;
4'hf:led = 8'h8e;
default:led = 8'hc0;
endcase
endmodule
cpp
module hc595_driver(
clk,
rstn,
data,
s_en,
shcp,
stcp,
ds
);
parameter times = 2; //40ns为最小刻度
input clk;
input rstn;
input [15:0] data;
input s_en;
output reg shcp;
output reg stcp;
output reg ds;
reg [15:0] r_data;
always@(posedge clk)
if(s_en)
r_data <= data;
//1.产生74hc595时序所需的最小时间单元
reg [7:0] div_cnt;
//脉冲信号
wire sck_plus;
assign sck_plus = div_cnt >= times - 1;
always@(posedge clk or negedge rstn)
if(!rstn)
div_cnt <= 0;
else if(div_cnt >= times - 1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'd1;
//2.依据sck_plus做相对应的事情
//先记录sck_plus的节点数
reg [5:0]shcp_edge_counter;
always@(posedge clk or negedge rstn)
if(!rstn)
shcp_edge_counter <= 0;
else if(sck_plus) //sck_plus不是寄存器的输出
if(shcp_edge_counter == 32)
shcp_edge_counter <= 0;
else
shcp_edge_counter <= shcp_edge_counter + 1'd1;
always@(posedge clk or negedge rstn)
if(!rstn) begin
shcp <= 0;
stcp <= 0;
ds <= 0;
end
else case(shcp_edge_counter)
0: begin shcp <= 0; ds <= r_data[15]; stcp <= 0; end
1: shcp <= 1'd1;
2: begin shcp <= 0; ds <= r_data[14]; end
3: shcp <= 1'd1;
4: begin shcp <= 0; ds <= r_data[13]; end
5: shcp <= 1'd1;
6: begin shcp <= 0; ds <= r_data[12]; end
7: shcp <= 1'd1;
8: begin shcp <= 0; ds <= r_data[11]; end
9: shcp <= 1'd1;
10: begin shcp <= 0; ds <= r_data[10]; end
11: shcp <= 1'd1;
12: begin shcp <= 0; ds <= r_data[9]; end
13: shcp <= 1'd1;
14: begin shcp <= 0; ds <= r_data[8]; end
15: shcp <= 1'd1;
16: begin shcp <= 0; ds <= r_data[7]; end
17: shcp <= 1'd1;
18: begin shcp <= 0; ds <= r_data[6]; end
19: shcp <= 1'd1;
20: begin shcp <= 0; ds <= r_data[5]; end
21: shcp <= 1'd1;
22: begin shcp <= 0; ds <= r_data[4]; end
23: shcp <= 1'd1;
24: begin shcp <= 0; ds <= r_data[3]; end
25: shcp <= 1'd1;
26: begin shcp <= 0; ds <= r_data[2]; end
27: shcp <= 1'd1;
28: begin shcp <= 0; ds <= r_data[1]; end
29: shcp <= 1'd1;
30: begin shcp <= 0; ds <= r_data[0]; end
31: shcp <= 1'd1;
32: begin shcp <= 0; stcp <= 1; ds <= 0; end
default : begin
shcp <= 0;
stcp <= 0;
ds <= 0;
end
endcase
endmodule
3.2 针对hc595_driver的仿真代码
cpp
`timescale 1ns / 1ps
module hc595_driver_tb();
reg clk;
reg rstn;
reg [15:0] data;
reg s_en;
wire shcp;
wire stcp;
wire ds;
hc595_driver hc595_driver_inst(
clk,
rstn,
data,
s_en,
shcp,
stcp,
ds
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rstn = 0;
#201;
rstn = 1;
#200;
s_en = 1;
data = 16'h1357;
#4000;
s_en = 0;
#200;
s_en = 1;
data = 16'h2468;
#4000;
s_en = 0;
$stop;
end
endmodule