zzuxzt

基于FPGA的74HC595驱动数码管动态显示--Verilog实现

0
阅读(18784)

一.数码管简要介绍

数码管分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳极(COM)需接+5V才能使其工作。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码,共阴极(COM)需接GND才能使其工作。如下图:

                                

下面是数码管的编码:

(1)共阳极数码管:

位选为高电平(即1)选中数码管; 

各段选为低电平(即0接地时)选中各数码段;

由0到f编码为:

 parameter SEG_NUM0  = 8'hc0,

SEG_NUM1  = 8'hf9,

SEG_NUM2  = 8'ha4,

SEG_NUM3  = 8'hb0,

SEG_NUM4  = 8'h99,

SEG_NUM5  = 8'h92,

SEG_NUM6  = 8'h82,

SEG_NUM7  = 8'hF8,

SEG_NUM8  = 8'h80,

SEG_NUM9  = 8'h90,

SEG_NUMA  = 8'h88,

SEG_NUMB  = 8'h83,

SEG_NUMC  = 8'hc6,

SEG_NUMD  = 8'ha1,

SEG_NUME  = 8'h86,

SEG_NUMF  = 8'h8e;

(2)共阴极数码管:

位选为低电平(即0)选中数码管;

各段选为高电平(即1接+5V时)选中各数码段;

由0到f的编码为: 

parameter SEG_NUM0  = 8'h3f,

SEG_NUM1  = 8'h06,

SEG_NUM2  = 8'h5b,

SEG_NUM3  = 8'h4f,

SEG_NUM4  = 8'h66,

SEG_NUM5  = 8'h6d,

SEG_NUM6  = 8'h7d,

SEG_NUM7  = 8'h07,

SEG_NUM8  = 8'h7f,

SEG_NUM9  = 8'h6f,

SEG_NUMA  = 8'h77,

SEG_NUMB  = 8'h7c,

SEG_NUMC  = 8'h39,

SEG_NUMD  = 8'h5e,

SEG_NUME  = 8'h79,

SEG_NUMF  = 8'h71;

二.74HC595简要介绍

74HC595是8位串行输入/8位串行或并行输出的存储状态寄存器,内部具有有8位移位寄存器和一个存储器,具有三态输出功能,可由SPI接口直接驱动。其引脚图如下:

                             

74HC595控制线主要有SHCP:移位寄存器时钟(上升沿有效);STCP:存储器时钟(上升沿有效);DS:串行移位输入;Q7:串行输出;Q0-Q7:并行输出;OE:使能端(低电平有效);MR:异步复位端(低电平有效)。

内部逻辑示意图:

                      

    74HC595控制时序图:

        

      

三.74HC595驱动数码管电路图

    

四.FPGA控制74HC595驱动数码管思路

由FPGA控制74HC595驱动数码管其实主要是抓住74HC595的控制时序,进而输出所需控制显示的内容,由同步状态机实现。

1.首先,要想74HC595工作起来,得有时钟输入。由于74HC595的数据输入是串行输入,所以我们需要产生第一个时钟---串行移位时钟:shcp。根据芯片手册上所写的在VCC=3.0V时的最大时钟频率为15M,因而,在这里我选取shcp = 10M,用简单的计数器对50M全局时钟进行5分频得到。

2.下面到了关键时候了,已经产生shcp串行移位时钟驱动74HC595移位寄存器工作了,但什么时候由FPGA传送数据呢?由于74HC595是在shcp时钟上升沿读取数据,所以为了确保FPGA传送的数据被准确地接收下来,在FPGA里面,我选取shcp时钟的下降沿发送数据。这里可以理解成SPI协议传输,FPGA为主机,74HC595为从机,主机在下降沿发送数据,从机在上升沿读取数据。在代码里,我采用边沿检测技术来捕获shcp时钟的下降沿,这个技术读者需要好好体会把握,很有用处。

3.到了这步,数据已经能正常移位进入74HC595串行移位寄存器了。这里我们要开始思考数码管的问题了,根据上面电路图可以知道,要完全将8位数码管的段选数据和位选数据传给数码管,需要24个状态,即:完成一次数码管的显示驱动需要产生24个shcp时钟。在移位数据完全进入移位寄存器时,产生一个stcp存储器时钟上升沿,把有效数据传入数码管。具体实现方式请参考代码理解。

4.一次数码管的显示驱动已经在前面几步完成了,如果要进行动态显示的话,就需要数码管循环扫描了,每一次扫描,执行位选数据变换,段选数据变换即可。

五.Verilog代码

本代码主要实现了8位数码管集体同步从0-F循环计数,动态显示。

/***************************************************************
         **************name: seg_595************
              *********author: zzuxzt*********
         **************time: 2014.5.21***************
***************************************************************/
module seg_595(input clk,
					input rst_n,
					output shcp,   //shift clk
					output stcp,   //latch clk
					output seg_data); 

/********************Common code****************/
//------------duan xuan------------------------
parameter 	SEG_NUM0 	= 8'h3f,//c0,
				SEG_NUM1 	= 8'h06,//f9,
				SEG_NUM2 	= 8'h5b,//a4,
				SEG_NUM3 	= 8'h4f,//b0,
				SEG_NUM4 	= 8'h66,//99,
				SEG_NUM5 	= 8'h6d,//92,
				SEG_NUM6 	= 8'h7d,//82,
				SEG_NUM7 	= 8'h07,//F8,
				SEG_NUM8 	= 8'h7f,//80,
				SEG_NUM9 	= 8'h6f,//90,
				SEG_NUMA 	= 8'h77,//88,
				SEG_NUMB 	= 8'h7c,//83,
				SEG_NUMC 	= 8'h39,//c6,
				SEG_NUMD 	= 8'h5e,//a1,
				SEG_NUME 	= 8'h79,//86,
				SEG_NUMF 	= 8'h71;//8e;
			 
//--------------wei xuan----------------------
parameter wei_all = 8'b0000_0000;
	
/****************************seg display data**********************************/
//---------------------data conventer----------------------------
reg [7:0] seg_num;
wire [7:0] wei_data;
assign wei_data = wei_all; 
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			seg_num <= 1'b0;
		end
	else 
		begin
			case(data)
				8'h0: seg_num <= SEG_NUM0;
				8'h1: seg_num <= SEG_NUM1;
				8'h2: seg_num <= SEG_NUM2;
				8'h3: seg_num <= SEG_NUM3;
				8'h4: seg_num <= SEG_NUM4;
				8'h5: seg_num <= SEG_NUM5;
				8'h6: seg_num <= SEG_NUM6;
				8'h7: seg_num <= SEG_NUM7;
				8'h8: seg_num <= SEG_NUM8;
				8'h9: seg_num <= SEG_NUM9;
				8'ha: seg_num <= SEG_NUMA;
				8'hb: seg_num <= SEG_NUMB;
				8'hc: seg_num <= SEG_NUMC;
				8'hd: seg_num <= SEG_NUMD;
				8'he: seg_num <= SEG_NUME;
				8'hf: seg_num <= SEG_NUMF;
				default: seg_num <= SEG_NUM0;
			endcase
		end
end

//------------------1s counter-----------------------
reg [31:0] cnt;
reg [7:0] data;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			cnt <= 1'b0;
			data <= 1'b0;
		end
	else if(cnt==32'd50000000)
		begin
			cnt <= 1'b0;
			if(data==8'hf)
				data <= 1'b0;
			else
				data <= data + 1'b1;
		end
	else
		cnt <= cnt + 1'b1;
end	

/*****************************seg driver*************************************/
//---------------shift clk----------------------
reg [2:0] cnt_st;
reg shcp_r;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			cnt_st <= 1'b0;
			shcp_r <= 1'b1;
		end
	else if(cnt_st==3'd4)
		begin
			cnt_st <= 3'd0;
			shcp_r <= ~shcp_r;      //10M
		end
	else
		cnt_st <= cnt_st + 1'b1;
end

//--------------------capture the shift clk trailing edge----------------------
reg shcp_r0,shcp_r1;
wire shcp_flag;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			shcp_r0 <= 1'b1;
			shcp_r1 <= 1'b1;
		end
	else
		begin
			shcp_r0 <= shcp_r;
			shcp_r1 <= shcp_r0;
		end
end 

assign shcp_flag = (~shcp_r0 && shcp_r1) ? 1'b1:1'b0;  //shcp_clk negedge
			
//-----------------------------seg display state----------------------------
reg [4:0] state;
reg stcp_r;
reg seg_data_r;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			state <= 1'b0;
			stcp_r <= 1'b1;
			seg_data_r <= 1'b0;
		end
	else if(shcp_flag)
		begin
			case(state)
				//--------------------duan xuan--------------------------
				5'd0:	begin
							seg_data_r <= seg_num[7];
							stcp_r <= 1'b1;							
							state <= state + 1'b1;
						end
				5'd1: begin
							seg_data_r <= seg_num[6];
							state <= state + 1'b1;
						end
				5'd2: begin
							seg_data_r <= seg_num[5];
							state <= state + 1'b1;
						end
				5'd3: begin
							seg_data_r <= seg_num[4];
							state <= state + 1'b1;
						end
				5'd4: begin
							seg_data_r <= seg_num[3];
							state <= state + 1'b1;
						end
				5'd5: begin
							seg_data_r <= seg_num[2];
							state <= state + 1'b1;
						end
				5'd6: begin
							seg_data_r <= seg_num[1];
							state <= state + 1'b1;
						end
				5'd7: begin
							seg_data_r <= seg_num[0];
							state <= state + 1'b1;
						end
				5'd8:	begin
							seg_data_r <= seg_num[7];
							stcp_r <= 1'b1;							
							state <= state + 1'b1;
						end
				5'd9: begin
							seg_data_r <= seg_num[6];
							state <= state + 1'b1;
						end
				5'd10: begin
							seg_data_r <= seg_num[5];
							state <= state + 1'b1;
						end
				5'd11: begin
							seg_data_r <= seg_num[4];
							state <= state + 1'b1;
						end
				5'd12: begin
							seg_data_r <= seg_num[3];
							state <= state + 1'b1;
						end
				5'd13: begin
							seg_data_r <= seg_num[2];
							state <= state + 1'b1;
						end
				5'd14: begin
							seg_data_r <= seg_num[1];
							state <= state + 1'b1;
						end
				5'd15: begin
							seg_data_r <= seg_num[0];
							state <= state + 1'b1;
						 end
				//-----------------------wei xuan------------------------------
				5'd16: begin
							seg_data_r <= wei_data[0];
							state <= state + 1'b1;
						end
				5'd17: begin
							seg_data_r <= wei_data[1];
							state <= state + 1'b1;
						end
				5'd18: begin
							seg_data_r <= wei_data[2];
							state <= state + 1'b1;
						 end
				5'd19: begin
							seg_data_r <= wei_data[3];
							state <= state + 1'b1;
						 end
				5'd20: begin
							seg_data_r <= wei_data[4];
							state <= state + 1'b1;
						 end
				5'd21: begin
							seg_data_r <= wei_data[5];
							state <= state + 1'b1;
						 end
				5'd22: begin
							seg_data_r <= wei_data[6];
							state <= state + 1'b1;
						 end
				5'd23: begin
							seg_data_r <= wei_data[7];
							stcp_r <= 1'b0;    //latch the all data
							state <= 1'b0;
						 end
				default: state <= 5'bxxxxx;
			endcase	
		end
end

assign shcp = shcp_r1;  //output to drive 74HC595 shift reg
assign stcp = stcp_r;  //output to drive 74HC595 latch reg
assign seg_data = seg_data_r;  
			
endmodule

六.Modelsim仿真图