74HC595驱动(并转串,fpga与时钟匹配,fpga与外部芯片的连接注意事项)
上一次设计的动态扫描数码管显示电路模型如上,这是一个32位并行数据[31:0]disp_num选通输出并行数据[7:0]select和[7:0]段选的电路。因此需要输出16个信号
而在开发板上的电路与这个的接口不同,如下:
这个开发板设计的fpga只有有三个输出,接到2片级联的74HC595芯片上,并行输出段选和位选数据。
这三个输出分别为:DIO(串行数据),SCLK(与74HC595匹配的时钟信号),RCLK(锁存器使能信号)
因此,想要在这片开发板上控制数码管显示,就得修改代码。
在上一次代码的基础上,把输出的16位并行数据转化为串行数据从DIO端口输出。(并行数据转串行数据:逐个输出即可)。
由于74HC595时钟信号频率与fpga芯片的时钟信号频率不相等,fpga芯片的时钟频率为50MHz,而74HC595时钟频率在不同的驱动电压下不同,在3.3VS可以达到6~30MHz,为了方便,我们可以根据fpga50MHz的时钟产生一个12.5MHz的信号作为74HC595的SCLK信号输入 。
为了数据传输的稳定,在传输串行数据时,我们可以在SCLK的低电平时刻从fpga中输出一个数据,在SCLK上升沿到来时输入到74HC595,这样子防止亚稳态的产生。
每16个数据传输完成后,再让锁存器使能信号有效,这样子一次性把16个数据从两片移位寄存器中传到锁存器中,输出给数码管。
对于74HC595芯片,其设计就是一个移位寄存器(实现串行数据转换为并行数据),如图所示:
16个输出Q并行输出。
module drive_digital_tube(
clk,
reset,
data,
s_en,
DIO,
sclk,
rclk,
s_done
);
input clk;//50MHz
input reset;
input [15:0]data;
input s_en;
output reg DIO;
output reg sclk;
output reg rclk;
output reg s_done;
reg [1:0]div_cnt;// 分频计数器,频率为25MHz
always@( posedge clk or negedge reset)
begin
if (!reset)
div_cnt <= 2'd0 ;
else if(div_cnt == 1)
div_cnt <= 2'd0 ;
else
div_cnt <= div_cnt + 1'd1 ;
end
wire pulse25M; //频率为25MHz的取样脉冲
assign pulse25M = ( div_cnt == 2'd1 ) ? 1 : 0 ;
reg [5:0]cnt;//计数33个状态
always@(posedge clk or negedge reset)
begin
if(!reset)
cnt <= 6'd0 ;
else if (s_en)
begin
if (pulse25M)
begin
if(cnt == 6'd33)
cnt <= 6'd0 ;
else
cnt <= cnt + 1 ;
end
end
else
cnt <= 6'd0 ;
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
DIO <= 0 ;
sclk <= 0 ;
rclk <= 0 ;
s_done = 0 ;
end
else if(s_en)
begin
if (pulse25M)
case(cnt)//sclk为12.5MHz信号,rclk为输出使能(所有数据取完后),在sclk的低电平时取值,可以稳定,在sclk上升沿到来时可稳定输出
0:begin DIO <= 0 ;sclk <= 0 ;rclk <= 0 ;s_done = 0 ;end
1: sclk <= 1 ;
2:begin DIO <=data[15] ;sclk <= 0 ; end
3:sclk <= 1 ;
4:begin DIO <=data[14] ;sclk <= 0 ; end
5:sclk <= 1 ;
6:begin DIO <=data[13] ;sclk <= 0 ; end
7:sclk <= 1 ;
8:begin DIO <=data[12] ;sclk <= 0 ; end
9:sclk <= 1 ;
10:begin DIO <=data[11] ;sclk <= 0 ; end
11:sclk <= 1 ;
12:begin DIO <=data[10] ;sclk <= 0 ; end
13:sclk <= 1 ;
14:begin DIO <=data[9] ;sclk <= 0 ; end
15:sclk <= 1 ;
16:begin DIO <=data[8] ;sclk <= 0 ; end
17:sclk <= 1 ;
18:begin DIO <=data[7] ;sclk <= 0 ; end
19:sclk <= 1 ;
20:begin DIO <=data[6] ;sclk <= 0 ; end
21:sclk <= 1 ;
22:begin DIO <=data[5] ;sclk <= 0 ; end
23:sclk <= 1 ;
24:begin DIO <=data[4] ;sclk <= 0 ; end
25:sclk <= 1 ;
26:begin DIO <=data[3] ;sclk <= 0 ; end
27:sclk <= 1 ;
28:begin DIO <=data[2] ;sclk <= 0 ; end
29:sclk <= 1 ;
30:begin DIO <=data[1] ;sclk <= 0 ; end
31:sclk <= 1 ;
32:begin DIO <=data[0] ;sclk <= 0 ; rclk <= 1 ;end
33:begin sclk <= 1 ; rclk <= 0 ;s_done = 1 ;end
default ;
endcase
end
else
begin
DIO <= 0 ;
sclk <= 0 ;
rclk <= 0 ;
s_done = 0 ;
end
end
end
module
`timescale 1ns / 1ns
module drive_digital_tube_tb(
);
reg clk ;
reg reset;
reg s_en;
reg [15:0]data;
wire DIO;
wire sclk;
wire rclk;
wire s_done ;
drive_digital_tube drive_digital_tube_sim(
clk,
reset,
data,
s_en,
DIO,
sclk,
rclk,
s_done
);
initial clk = 1;
always #10 clk = ! clk ;
initial
begin
reset = 0 ;
data = 0 ;
s_en = 0 ;
#201;
reset = 1 ;
s_en = 1 ;
data = 16'ha55a;
@(negedge s_done);
s_en = 0;
#200;
s_en = 1;
data = 16'h7597;
@(negedge s_done);
s_en = 0;
#200;
s_en = 1;
data = 16'h6425;
@(negedge s_done);
$stop ;
end
endmodule
注意:
①有的内部变量由于没有真正用上,在仿真时波形为X,会使结果错误。检查alyays里面有没有 posedge reset,没有的话可能会出现这种情况。
②对于输入的数据,最好拿一个寄存器把当下的值存下来(添加一个控制信号),再慢慢输出,不然可能会造成输入数据不稳定,传输结果错误。
③fpga与外部芯片连接的场景:大部分情况下需要fpga产生一个时钟信号来驱动外部芯片,因此,对于fpga既要提高数据又要提高时钟的情况,我们可以在自己提供的时钟的低电平时刻输出数据,这样子可以保证外部芯片输入数据的稳定,在时钟上升沿到来时已经稳定,不会产生亚稳态。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)