【原创】FPGA应用(二)——74HC595驱动
0赞一、设计需求
设计一个驱动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顶层模块:
/**********************************************版权申明************************************************* ** 电子技术应用网站, 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
//*********************************************文件结束*****************************************************仿真结果如下:
很明显,从仿真结果来看,本设计已实现了功能。接着,就可以对设计进行综合、布局布线、生成bit流文件和下载到板子上。在开发板上可以看到8盏led灯在做流水运动。
![clip_image002[6] clip_image002[6]](http://files.chinaaet.com/images/blog/2015/07/12/5245812404501.jpg)
![clip_image004[5] clip_image004[5]](http://files.chinaaet.com/images/blog/2015/07/12/5245984152650.jpg)
![clip_image006[9] clip_image006[9]](http://files.chinaaet.com/images/blog/2015/07/12/5246163406534.jpg)
![clip_image008[5] clip_image008[5]](http://files.chinaaet.com/images/blog/2015/07/12/5246233132944.jpg)
![clip_image010[5] clip_image010[5]](http://files.chinaaet.com/images/blog/2015/07/12/5246399746457.jpg)
![clip_image012[5] clip_image012[5]](http://files.chinaaet.com/images/blog/2015/07/12/5250196962745.jpg)
![clip_image014[5] clip_image014[5]](http://files.chinaaet.com/images/blog/2015/07/12/5250274346602.jpg)
![clip_image016[6] clip_image016[6]](http://files.chinaaet.com/images/blog/2015/07/12/5250313491818.jpg)
![clip_image018[5] clip_image018[5]](http://files.chinaaet.com/images/blog/2015/07/12/5250362947955.jpg)
