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既要提高数据又要提高时钟的情况,我们可以在自己提供的时钟的低电平时刻输出数据,这样子可以保证外部芯片输入数据的稳定,在时钟上升沿到来时已经稳定,不会产生亚稳态。
标签:reset,输出,74HC595,en,fpga,转串,reg,时钟 来源: https://www.cnblogs.com/fbur/p/16388257.html