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