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