一、設(shè)計需求
設(shè)計一個驅(qū)動74HC595芯片工作的功能模塊,并在CB哥的開發(fā)板上8盞led燈上實現(xiàn)流水燈的效果。
二、設(shè)計思路
1、74HC595介紹及分析
圖1所示為74HC595芯片的封裝及引腳分布。74HC595是由8位移位寄存器和8位三態(tài)并行輸出的D型鎖存器組成,如圖2所示。
圖1 74HC595封裝及引腳分布
圖2 74HC595邏輯圖
74HC595具有以下特征:
(1) 移位寄存器接收串行數(shù)據(jù)、提供串行或8位并行數(shù)據(jù)輸出;
(2) 移位寄存器和鎖存器擁有獨立的時鐘輸入;
(3) 移位寄存器擁有異步復位信號
圖3所示為74HC595的功能表,于是可總結(jié)為:
當復位信號RESET為低電平時,
(1) 移位寄存器輸出為低電平(包括并行和串行輸出);
(2) 當輸出使能信號為低電平時,若鎖存器時鐘上升沿到來,則最后輸出為低電平(移位寄存器輸出),否則保持不變;當輸出使能信號為高電平時,最后輸出為高阻態(tài)。
當復位信號RESET為高電平時,
(1) 當移位時鐘上升沿到來時,移位寄存器輸出與輸入內(nèi)容一樣,否則保持不變;
(2) 當輸出使能信號為低電平時,若鎖存器時鐘上升沿到來,則最后輸出與輸入內(nèi)容(移位寄存器輸出),否則保持不變;當輸出使能信號為高電平時,最后輸出為高阻態(tài)。
圖3 74HC595功能表
圖4所示為74HC595的時序圖。由時序圖可以看出,串行輸入數(shù)據(jù)在移位時鐘的上升沿被讀進移位寄存器中;當復位信號有效時,移位寄存器被清零;在輸出使能為低電平且鎖存時鐘的上升沿時,移位寄存器的值被鎖存輸出;當輸出使能為高電平時,輸出高阻態(tài)。而串行輸出接口則是輸出移位寄存器的最高位,且不受輸出使能的控制。
圖4 74HC595時序圖
本次的led流水燈設(shè)計不需要高阻態(tài),故將輸出使能始終置為低電平,同時也不需要復位信號,將其置為高電平,永不復位。此外也不需要串行輸出。在不考慮這三個信號的情況下,重新給出簡化后的74HC595時序圖,如圖5所示。這里考慮了輸入數(shù)據(jù)對移位時鐘上升沿的建立時間和保持時間,如圖6所示,由于芯片供電電壓為3.3V且當前的室溫是在25攝氏度到85攝氏度之間,故最小建立時間和保持時間分別為50ns和5ns。而移位時鐘上升沿對鎖存時鐘上升沿的最小建立時間為70ns。
圖5 簡化后的74HC595時序圖
圖6 建立時間和保持時間要求
此外,移位時鐘的最大頻率可以是10MHz,如圖7所示。但為了滿足時序要求,移位時鐘采用5MHz即200ns,輸入數(shù)據(jù)只能在移位時鐘的下降沿改變,這里建立時間和保持時間裕量分別為50ns和95ns。鎖存時鐘也在移位時鐘的下降沿產(chǎn)生,于是建立時間裕量還有30ns。
圖7 移位時鐘最大頻率與溫度、電壓的關(guān)系
2、設(shè)計分析
如圖8所示,本設(shè)計由三個模塊組合,分別為子模塊led_ctrl、driver_74595和頂層模塊led_water,它們的作用分別為:
led_ctrl模塊 :負責產(chǎn)生流水燈顯示的數(shù)據(jù);
driver_74595模塊 :負責驅(qū)動74HC595芯片工作并發(fā)送led數(shù)據(jù);
led_water頂層模塊 :例化led_ctrl和driver_74595模塊,完成流水燈設(shè)計。
圖8 設(shè)計組織框架
至于led_ctrl模塊設(shè)計思路可參考上上篇博文“FPGA應(yīng)用(一)——流水燈”,這里主要分析driver_74595模塊的設(shè)計。該模塊采用狀態(tài)機的方案來實現(xiàn),如圖9所示。狀態(tài)機中有四個狀態(tài),分別為IDLE、CLK_L、CLK_H和FINISH。在IDLE中主要完成led數(shù)據(jù)的加載和發(fā)送;在CLK_L中產(chǎn)生移位時鐘的低電平;在CLK_H中產(chǎn)生移位時鐘的高電平和led數(shù)據(jù)的發(fā)送;在FINISH中產(chǎn)生鎖存時鐘。
圖9 74HC595驅(qū)動狀態(tài)機
三、設(shè)計實現(xiàn)
led_water頂層模塊:
/**********************************************版權(quán)申明*************************************************
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: led_water.v
** 創(chuàng)建者: CrazyBird
** 創(chuàng)建日期: 2015-7-11
** 版本號: v1.0
** 功能描述: 該模塊完成74HC595的驅(qū)動并實現(xiàn)流水燈的功能
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module led_water(
rst_n,
clk,
shift_clock,
lacth_clock,
led_dout
);
//******************************************************************************
// 參數(shù)定義
//******************************************************************************
// 修改以下參數(shù)以滿足需求
parameter CLK_CYCLE = 20; // 時鐘周期,單位ns
parameter LED_WIDTH = 8; // led數(shù)據(jù)位寬
// 修改以上參數(shù)以滿足需求
//******************************************************************************
// 輸入/輸出端口定義
//******************************************************************************
input rst_n; // 全局復位信號
input clk; // 全局時鐘信號,50MHz
output shift_clock; // 74HC595的移位時鐘信號
output lacth_clock; // 74HC595的鎖存時鐘信號
output led_dout; // 74HC595的串行數(shù)據(jù)輸入
//******************************************************************************
// 變量定義
//******************************************************************************
wire [LED_WIDTH-1:0] led_data; // led燈數(shù)據(jù)輸出
wire led_flag; // led燈數(shù)據(jù)輸出標志
//******************************************************************************
// 模塊連接
//******************************************************************************
// 例化led_ctrl模塊
led_ctrl #(
.CLK_CYCLE(CLK_CYCLE),
.LED_WIDTH(LED_WIDTH)
)
u_led_ctrl(
.rst_n ( rst_n ),
.clk ( clk ),
.led_data ( led_data ),
.led_flag ( led_flag )
);
// 例化driver_74595模塊
driver_74595 #(
.CLK_CYCLE(CLK_CYCLE),
.LED_WIDTH(LED_WIDTH)
)
u_driver_74595(
.rst_n ( rst_n ),
.clk ( clk ),
.led_data ( led_data ),
.led_flag ( led_flag ),
.shift_clock ( shift_clock ),
.latch_clock ( lacth_clock ),
.led_dout ( led_dout )
);
//******************************************************************************
endmodule
//*********************************************文件結(jié)束*****************************************************
led_ctrl模塊:
/**********************************************版權(quán)申明*************************************************
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: led_ctrl.v
** 創(chuàng)建者: CrazyBird
** 創(chuàng)建日期: 2015-7-11
** 版本號: v1.0
** 功能描述: 該模塊主要負責產(chǎn)生led燈流水顯示的數(shù)據(jù)
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module led_ctrl(
rst_n,
clk,
led_data,
led_flag
);
//******************************************************************************
// 參數(shù)定義
//******************************************************************************
// 修改以下參數(shù)以滿足需求
parameter CLK_CYCLE = 20; // 時鐘周期,單位ns
parameter T0 = 500_000_000; // 0.5s,流水燈流動速率
// parameter T0 = 5000; // 測試用
parameter LED_WIDTH = 8; // led數(shù)據(jù)位寬
parameter DELAY0 = 25; // 計數(shù)器位寬
// 修改以上參數(shù)以滿足需求
// 以下參數(shù)不要修改
parameter T0_VAL = (T0/CLK_CYCLE)-1; // 0.5s,流水燈流動速率
// 以上參數(shù)不要修改
//******************************************************************************
// 輸入/輸出端口定義
//******************************************************************************
input rst_n; // 全局復位信號
input clk; // 全局時鐘信號,50MHz
output reg [LED_WIDTH-1:0] led_data; // led燈數(shù)據(jù)輸出
output reg led_flag; // led燈數(shù)據(jù)輸出標志
//******************************************************************************
// 計數(shù)器
//******************************************************************************
reg [DELAY0-1:0] cnt;
wire cnt_done; // 0.5計數(shù)完成標志位
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
cnt <= {(LED_WIDTH){1'b0}};
else if(cnt_done==1'b1)
cnt <= {(LED_WIDTH){1'b0}};
else
cnt <= cnt + 1'b1;
end
assign cnt_done = (cnt==T0_VAL);
//******************************************************************************
// 流水燈數(shù)據(jù)的產(chǎn)生
//******************************************************************************
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
led_data <= {{(LED_WIDTH-1){1'b0}},1'b1};
led_flag <= 1'b0;
end
else if(cnt_done==1'b1)
begin
led_data <= {led_data[LED_WIDTH-2:0],led_data[LED_WIDTH-1]};
led_flag <= 1'b1;
end
else
begin
led_data <= led_data;
led_flag <= 1'b0;
end
end
//******************************************************************************
endmodule
//*********************************************文件結(jié)束*****************************************************
driver_74595模塊:
/**********************************************版權(quán)申明*************************************************
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: driver_74595.v
** 創(chuàng)建者: CrazyBird
** 創(chuàng)建日期: 2015-7-11
** 版本號: v1.0
** 功能描述: 該模塊完成對74HC595的驅(qū)動
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module driver_74595(
rst_n,
clk,
led_data,
led_flag,
shift_clock,
latch_clock,
led_dout
);
//******************************************************************************
// 參數(shù)定義
//******************************************************************************
// 修改以下參數(shù)以滿足需求
parameter CLK_CYCLE = 20; // 時鐘周期,單位ns
parameter T0 = 100; // 100ns,移位時鐘高低電平時間長度
parameter T1 = 200; // 200ns,鎖存時鐘周期
parameter LED_WIDTH = 8; // led數(shù)據(jù)位寬
parameter W0 = 3; // 100ns計數(shù)器位寬
parameter W1 = 4; // 200ns計數(shù)器位寬
parameter W2 = 4; // 計算移位時鐘周期數(shù)
// 修改以上參數(shù)以滿足需求
// 以下參數(shù)不要修改
parameter T0_VAL = (T0/CLK_CYCLE)-1; // 100ns,移位時鐘高低電平時間長度
parameter T1_VAL = (T1/CLK_CYCLE)-1; // 200ns,鎖存時鐘周期
parameter IDLE = 2'b00,
CLK_L = 2'b01,
CLK_H = 2'b10,
FINISH = 2'b11;
// 以上參數(shù)不要修改
//******************************************************************************
// 輸入/輸出端口定義
//******************************************************************************
input rst_n; // 全局復位信號
input clk; // 全局時鐘信號,50MHz
input [LED_WIDTH-1:0] led_data; // led燈數(shù)據(jù)輸出
input led_flag; // led燈數(shù)據(jù)輸出標志
output reg shift_clock; // 74HC595的移位時鐘信號
output reg latch_clock; // 74HC595的鎖存時鐘信號
output led_dout; // 74HC595的串行數(shù)據(jù)輸入
//******************************************************************************
// 變量定義
//******************************************************************************
wire shift_clock_cnt_done; // 100ns計數(shù)完成標志位
wire latch_clock_cnt_done; // 200ns計數(shù)完成標志位
wire period_cnt_done; // 生成8個移位時鐘標志位
//******************************************************************************
// 狀態(tài)機實現(xiàn)74HC595驅(qū)動
//******************************************************************************
reg [1:0] state;
reg [LED_WIDTH-1:0] led_data_r; // led燈的加載變量
reg shift_clock_cnt_en; // 100ns計數(shù)使能
reg latch_clock_cnt_en; // 200ns計數(shù)使能
reg period_cnt_en; // 移位時鐘周期計數(shù)使能
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
state <= IDLE;
led_data_r <= {(LED_WIDTH){1'b0}};
shift_clock <= 1'b0;
latch_clock <= 1'b0;
shift_clock_cnt_en <= 1'b0;
latch_clock_cnt_en <= 1'b0;
period_cnt_en <= 1'b0;
end
else
begin
case(state)
IDLE :
begin
if(led_flag)
begin
state <= CLK_L;
led_data_r <= led_data; // 數(shù)據(jù)加載
shift_clock_cnt_en <= 1'b1;
end
else
state <= IDLE;
end
CLK_L :
begin
shift_clock <= 1'b0;
if(shift_clock_cnt_done==1'b1)
state <= CLK_H;
else
state <= CLK_L;
end
CLK_H :
begin
shift_clock <= 1'b1;
period_cnt_en <= 1'b1;
if(shift_clock_cnt_done==1'b1)
begin
period_cnt_en <= 1'b0;
if(period_cnt_done==1'b1)
begin
state <= FINISH;
shift_clock_cnt_en <= 1'b0;
latch_clock_cnt_en <= 1'b1;
shift_clock <= 1'b0;
end
else
begin
state <= CLK_L;
led_data_r <= {led_data_r[LED_WIDTH-2:0],1'b0};
end
end
else
state <= CLK_H;
end
FINISH :
begin
latch_clock <= 1'b1;
if(latch_clock_cnt_done==1'b1)
begin
state <= IDLE;
latch_clock_cnt_en <= 1'b0;
latch_clock <= 1'b0;
end
else
state <= FINISH;
end
endcase
end
end
//******************************************************************************
// 各種計數(shù)器
//******************************************************************************
// 移位時鐘計數(shù)器
reg [W0-1:0] shift_clock_cnt;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
shift_clock_cnt <= {(W0){1'b0}};
else if(shift_clock_cnt_en==1'b1)
begin
if(shift_clock_cnt_done==1'b1)
shift_clock_cnt <= {(W0){1'b0}};
else
shift_clock_cnt <= shift_clock_cnt + 1'b1;
end
else
shift_clock_cnt <= {(W0){1'b0}};
end
assign shift_clock_cnt_done = (shift_clock_cnt==T0_VAL);
// 鎖存時鐘計數(shù)器
reg [W1-1:0] latch_clock_cnt;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
latch_clock_cnt <= {(W1){1'b0}};
else if(latch_clock_cnt_en)
begin
if(latch_clock_cnt_done==1'b1)
latch_clock_cnt <= {(W1){1'b0}};
else
latch_clock_cnt <= latch_clock_cnt + 1'b1;
end
else
latch_clock_cnt <= {(W1){1'b0}};
end
assign latch_clock_cnt_done = (latch_clock_cnt==T1_VAL);
// 移位時鐘周期計數(shù)器
reg [W2-1:0] period_cnt;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
period_cnt <= {(W2){1'b0}};
else if((period_cnt_en==1'b1)&&(shift_clock_cnt_done==1'b1))
begin
if(period_cnt_done==1'b1)
period_cnt <= {(W2){1'b0}};
else
period_cnt <= period_cnt + 1'b1;
end
else
period_cnt <= period_cnt;
end
assign period_cnt_done = (period_cnt==7);
//******************************************************************************
// led數(shù)據(jù)輸出
//******************************************************************************
assign led_dout = led_data_r[LED_WIDTH-1];
//******************************************************************************
endmodule
//*********************************************文件結(jié)束*****************************************************
仿真結(jié)果如下:
很明顯,從仿真結(jié)果來看,本設(shè)計已實現(xiàn)了功能。接著,就可以對設(shè)計進行綜合、布局布線、生成bit流文件和下載到板子上。在開發(fā)板上可以看到8盞led燈在做流水運動。





