crazybird

【原创】FPGA应用(二)——74HC595驱动

0
阅读(5655)

一、设计需求

    设计一个驱动74HC595芯片工作的功能模块,并在CB哥的开发板上8盏led灯上实现流水灯的效果。

二、设计思路

1、74HC595介绍及分析

    图1所示为74HC595芯片的封装及引脚分布。74HC595是由8位移位寄存器和8位三态并行输出的D型锁存器组成,如图2所示。

clip_image002[6]

图1 74HC595封装及引脚分布

clip_image004[5]

图2 74HC595逻辑图

    74HC595具有以下特征:

    (1) 移位寄存器接收串行数据、提供串行或8位并行数据输出;

    (2) 移位寄存器和锁存器拥有独立的时钟输入;

    (3) 移位寄存器拥有异步复位信号

    图3所示为74HC595的功能表,于是可总结为:

    当复位信号RESET为低电平时,

    (1) 移位寄存器输出为低电平(包括并行和串行输出);

    (2) 当输出使能信号为低电平时,若锁存器时钟上升沿到来,则最后输出为低电平(移位寄存器输出),否则保持不变;当输出使能信号为高电平时,最后输出为高阻态。

    当复位信号RESET为高电平时,

    (1) 当移位时钟上升沿到来时,移位寄存器输出与输入内容一样,否则保持不变;

    (2) 当输出使能信号为低电平时,若锁存器时钟上升沿到来,则最后输出与输入内容(移位寄存器输出),否则保持不变;当输出使能信号为高电平时,最后输出为高阻态。

clip_image006[9]

图3 74HC595功能表

    图4所示为74HC595的时序图。由时序图可以看出,串行输入数据在移位时钟的上升沿被读进移位寄存器中;当复位信号有效时,移位寄存器被清零;在输出使能为低电平且锁存时钟的上升沿时,移位寄存器的值被锁存输出;当输出使能为高电平时,输出高阻态。而串行输出接口则是输出移位寄存器的最高位,且不受输出使能的控制。

clip_image008[5]

图4 74HC595时序图

    本次的led流水灯设计不需要高阻态,故将输出使能始终置为低电平,同时也不需要复位信号,将其置为高电平,永不复位。此外也不需要串行输出。在不考虑这三个信号的情况下,重新给出简化后的74HC595时序图,如图5所示。这里考虑了输入数据对移位时钟上升沿的建立时间和保持时间,如图6所示,由于芯片供电电压为3.3V且当前的室温是在25摄氏度到85摄氏度之间,故最小建立时间和保持时间分别为50ns和5ns。而移位时钟上升沿对锁存时钟上升沿的最小建立时间为70ns。

clip_image010[5]

图5 简化后的74HC595时序图

clip_image012[5]

图6 建立时间和保持时间要求

    此外,移位时钟的最大频率可以是10MHz,如图7所示。但为了满足时序要求,移位时钟采用5MHz即200ns,输入数据只能在移位时钟的下降沿改变,这里建立时间和保持时间裕量分别为50ns和95ns。锁存时钟也在移位时钟的下降沿产生,于是建立时间裕量还有30ns。

clip_image014[5]

图7 移位时钟最大频率与温度、电压的关系

2、设计分析

    如图8所示,本设计由三个模块组合,分别为子模块led_ctrl、driver_74595和顶层模块led_water,它们的作用分别为:

    led_ctrl模块 :负责产生流水灯显示的数据;

    driver_74595模块 :负责驱动74HC595芯片工作并发送led数据;

    led_water顶层模块 :例化led_ctrl和driver_74595模块,完成流水灯设计。

clip_image016[6]

图8 设计组织框架

    至于led_ctrl模块设计思路可参考上上篇博文“FPGA应用(一)——流水灯”,这里主要分析driver_74595模块的设计。该模块采用状态机的方案来实现,如图9所示。状态机中有四个状态,分别为IDLE、CLK_L、CLK_H和FINISH。在IDLE中主要完成led数据的加载和发送;在CLK_L中产生移位时钟的低电平;在CLK_H中产生移位时钟的高电平和led数据的发送;在FINISH中产生锁存时钟。

clip_image018[5]

图9 74HC595驱动状态机

三、设计实现

led_water顶层模块:

/**********************************************版权申明*************************************************
**                                   电子技术应用网站, CrazyBird
**                     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名:          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模块:

/**********************************************版权申明*************************************************
**                                   电子技术应用网站, CrazyBird
**                     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名:          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模块:

/**********************************************版权申明*************************************************
**                                   电子技术应用网站, CrazyBird
**                     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名:          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
//*********************************************文件结束*****************************************************

仿真结果如下:

image

    很明显,从仿真结果来看,本设计已实现了功能。接着,就可以对设计进行综合、布局布线、生成bit流文件和下载到板子上。在开发板上可以看到8盏led灯在做流水运动。