FPGA基础设计(七):数码管驱动

前言

数码管显示分为静态显示和动态显示;
静态显示:一个数码管共8段,每个数码管段选接入8位的数据线来显示字符,送入一个字符永久保持,直到送入一个新的字符;每个数码管驱动需要独立的数据线,硬件电路比较复杂,成本高,一般不用。

动态显示:每个数码管段选并联到8位数据线上,利用位选选择哪一个数码管显示,轮流的进行位选送入相应的字符。循环扫描所有数码管,利用人视觉暂留作用,使人感觉数码管都在同时显示。

十六线制驱动数码管

  总体实现:设置扫描时钟频率,比如使用1ms的刷新时间让8个数码管轮流显示,即第1ms选择数码管1,让其显示相应的段码;第2ms选择数码管2,让其显示相应的段码;以此类推依次显示8个数码管,最后循环以上操作。
根据数码管动态显示原理,需要8位数据线进行位选、8位数据线进行段选共16位数据线进行驱动,而且还要实现的是位选与段选的配合:当位选段选择某个数码管时段选端要显示其相应的字符【都是组合逻辑实现,段选位选同时输出】。

模块设计

再说一遍,总体要实现的是同时输出8位位选信号以及对应的8位段码
1、分频模块
分频计数产生1KHz的扫描时钟信号Clk_1k,即对应数码管的刷新频率;
2、循环移位模块
1KHz时钟作为驱动信号,驱动8位循环移位模块进行位选,即输出作为8选1多路器的选择端;
3、8选1多路器
根据位选信号,选择输出对应要显示的字符;
这里给所有数码管要显示字符都已经给定,即4位要显示的数据;
4、段选输出–4输出查找表
对8选1多路器输出的字符进行译码,段选输出;
5、位选输出
2选1多路器,使能数码管时输出循环移位模块的位选信号。
逻辑电路图

代码

1、分频模块

	 // 分频计数模块
		 always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				div_cnt <= 15'b0;
		   else if(!En)
				div_cnt <= 15'b0;  // 数码管没有使能,分频也就没有意义
			else if(div_cnt == 249999)
				div_cnt <= 15'b0; 
			else 
				div_cnt <= div_cnt + 1'b1; 
		
		// 分频计数产生1K扫描时钟信号
		always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)	
				Clk_1k <= 1'b0;
			else if(div_cnt == 249999)
				Clk_1k <= ~Clk_1k;
			else 
				Clk_1k <= Clk_1k;

2、循环移位模块
1KHz时钟信号驱动8位循环移位寄存器,其输出作为8选1多路器的选择端;

		always@(posedge Clk_1k or negedge Rst_n)
			if(!Rst_n)
				sel_r <= 8'b0000_0001;
			else if(sel_r == 8'b1000_0000) // 说明已经循环移位一遍了,重新开始扫描
				sel_r <= 8'b0000_0001;
			else
				sel_r <= sel_r << 1;

3、8选1多路器
8个数码管要显示的字符全部寄存在Data_disp,每个数码管占4位,当选择哪一个数码管将对应的字符输出到译码电路。

always@(*)
	case(sel_r)
		8'b0000_0001:data_tmp = Data_disp[3:0];
		8'b0000_0010:data_tmp = Data_disp[7:4];
		8'b0000_0100:data_tmp = Data_disp[11:8];
		8'b0000_1000:data_tmp = Data_disp[15:12];
		8'b0001_0000:data_tmp = Data_disp[19:16];
		8'b0010_0000:data_tmp = Data_disp[23:20];
		8'b0100_0000:data_tmp = Data_disp[27:24];
		8'b1000_0000:data_tmp = Data_disp[31:28];
		default:data_tmp = 4'b0000;
	endcase

4、段选输出,译码

always@(*)
	case(data_tmp)
		4'h0:Seg = 7'b1000000;
		4'h1:Seg = 7'b1111001;
		4'h2:Seg = 7'b0100100;
		4'h3:Seg = 7'b0110000;
		4'h4:Seg = 7'b0011001;
		4'h5:Seg = 7'b0010010;
		4'h6:Seg = 7'b0000010;
		4'h7:Seg = 7'b1111000;
		4'h8:Seg = 7'b0000000;
		4'h9:Seg = 7'b0010000;
		4'ha:Seg = 7'b0001000;
		4'hb:Seg = 7'b0000011;
		4'hc:Seg = 7'b1000110;
		4'hd:Seg = 7'b0100001;
		4'he:Seg = 7'b0000110;
		4'hf:Seg = 7'b0001110;
	endcase

5、位选输出

assign Sel = (En) ? sel_r : 8'b0000_0000;

波形分析

1、开始时,sel=8’b000_0001,选择数码管1,要显示数据data_disp[0]为8,对应段码为7’b000_0000;
clk_1K上升沿来临,sel=8’b0000_0010,选择数码管2,要显示数据data_disp[1]为0,对应段码为7’b1000_0000,验证成功;

2、clk_1K上升沿来临,sel=8’b0000_0100,选择数码管3,要显示数据data_disp[2]为5,对应段码为7’b001_0010,验证成功。

三线制数码管驱动

上述实现使用FPGA16个IO,占用FPGA资源。
实际电路设计中,FPGA驱动数码管还要级联两片8位74HC595移位寄存器构成16位的寄存器,并将级联后的输出连接到数码管的位选和段选。

原理:

将FPGA输出的8位位选、8位段选数据通过串行端口DIO输入到两片74HC595中,然后并行输出到数码管的位选和段选段;也就是说通过3个IO控制8个8段数码管
所以,最主要的是74HC595的驱动。

74HC595

简介

1、芯片引脚

2、引脚功能简介
DS:串行数据输入
SHCP:移位寄存器时钟输入,工作时钟12.5MHz
STCP:存储寄存器时钟输入
Q0~Q7:串行数据输出
OE/:输出使能输入【输出存储寄存器中的数据】
Q7S:串行数据输出,与下一片8位的74HC595串行输入端相连;这样可以构成16位并行数据输出。

3、移位寄存器原理
每来临一个SHCP时钟上升沿,将DS端串行数据移入到移位寄存器;
当下一个上升沿来临,上一个移入的数据会往下移动一位;
当一次输入的串行数据超过8位,后面的数据会通过Q7S端输出。

4、串转并原理

  • 先把要传输的串行数据送到DS端口;
  • 产生SHCP时钟上升沿,将DS端口数据移入到移位寄存器;
  • 产生STCP时钟上升沿,将移位寄存器里的数据送入到存储寄存器;
  • 将OE/引脚置为低电平,存储寄存器的数据会在Q0~Q7并行输出。

时序图

那么如何驱动74HC595将串行数据转为并行数据?
首先根据时序图模拟出SHCP、STCP、DS三个信号的时序,这三个信号再在相应时刻进行操作【时序图模拟出来就实现了数据的传输】。
在这里插入图片描述

具体实现:

这里使用一种思想:
为了方便控制整个时序的实现,使得SHCP、DS、STCP信号同步,使用计数替代计时,计数到某个值产生相应的时钟电平。

1、计数替换计时

 always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		div_cnt <= 0;
	else if(div_cnt == CNT_MAX - 1'b1)
		div_cnt <= 0;
	else
		div_cnt <= div_cnt + 1'b1;
	
 wire sck_plus;  // 计数标志信号
 // sck_plus时钟周期是系统时钟周期的两倍
 assign sck_plus = (div_cnt == CNT_MAX - 1'b1); // 每当计数到1是sck_plus为1,其他情况sck_plus为0,
 
 reg [5:0]SHCP_EDGE_CNT;
		 
 // 序列机计数器
 always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		SHCP_EDGE_CNT <= 0;
	else if(sck_plus)begin
		if(SHCP_EDGE_CNT == 32) // 传16位数据,需要SHCP16个时钟周期共32个时钟边沿
			SHCP_EDGE_CNT <= 0;
		else
			SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;  	 
	end														
	else														
		SHCP_EDGE_CNT <= SHCP_EDGE_CNT;

2、模拟时序图
  以SHCP为基准,根据SHCP的变化来确定其他信号的变化,执行相应的功能。
一共输送16位数据,SHCP需经历16次上升沿,32次翻转,计数到32,在每个计数值执行相应的操作。

 always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)begin
			ST_CP <= 1'b0;
			DS    <= 1'b0;
			SH_CP <= 1'b0;
		end
		else begin
			case(SHCP_EDGE_CNT)  // 根据边沿计数器的值确定做什么事情,使用计数替代计时
				0: begin 
						SH_CP <= 0; // 确定第0时刻,传输最高位数据
						ST_CP <= 1'b0; 
						DS <= r_data[15];  // /第一个下降沿,先将串行数据最高位送到DS端	
					end  
				1: begin 
						SH_CP <= 1'b1;  // 第1时刻,SHCP产生上升沿,这个时候会将第0时刻送到DS端的数据移入到移位寄存器,所以SHCP为下降沿时将数据送到DS端
						ST_CP <= 1'b0; // 保持不变,为低电平
					end 
				2: begin 
						SH_CP <= 1'b0;  // 第2时刻,SHCP产生下降沿
						ST_CP <= 1'b0;  // 仍保持不变,16位数据未传输完成
						DS    <= r_data[14]; // SHCP下降沿时又将下一位数据送到DS端口
				   end
				3: begin SH_CP <= 1'b1; end  // 产生上升沿,先将第1时刻移入到移位寄存器的数据往下移位,再将第2时刻送到DS端数据移入到移位寄存器
				4: begin SH_CP <= 1'b0; DS <= r_data[13]; end
				5: begin SH_CP <= 1'b1; end
				6: begin SH_CP <= 1'b0; DS <= r_data[12]; end
				7: begin SH_CP <= 1'b1; end
				8: begin SH_CP <= 1'b0; DS <= r_data[11]; end
				9: begin SH_CP <= 1'b1; end
				10: begin SH_CP <= 1'b0; DS <= r_data[10]; end
				11: begin SH_CP <= 1'b1; end
				12: begin SH_CP <= 1'b0; DS <= r_data[9]; end
				13: begin SH_CP <= 1'b1; end
				14: begin SH_CP <= 1'b0; DS <= r_data[8]; end
				15: begin SH_CP <= 1'b1; end
				16: begin SH_CP <= 1'b0; DS <= r_data[7]; end
				17: begin SH_CP <= 1'b1; end
				18: begin SH_CP <= 1'b0; DS <= r_data[6]; end
				19: begin SH_CP <= 1'b1; end
				20: begin SH_CP <= 1'b0; DS <= r_data[5]; end
				21: begin SH_CP <= 1'b1; end
				22: begin SH_CP <= 1'b0; DS <= r_data[4]; end
				23: begin SH_CP <= 1'b1; end
				24: begin SH_CP <= 1'b0; DS <= r_data[3]; end
				25: begin SH_CP <= 1'b1; end
				26: begin SH_CP <= 1'b0; DS <= r_data[2]; end
				27: begin SH_CP <= 1'b1; end
				28: begin SH_CP <= 1'b0; DS <= r_data[1]; end
				29: begin SH_CP <= 1'b1; end
				30: begin SH_CP <= 1'b0; DS <= r_data[0]; end  // 将最后一位数据送到DS端
				31: begin SH_CP <= 1'b1; end  // 将最后一位数据移入到移位寄存器【使用仿真看看是不是这样的】
				32: begin ST_CP <= 1'b1; end  // 此时串行数据都移入到移位寄存器,ST_CP产生上升沿,移位寄存器的数据进入存储寄存器,OE/使能,输出数据到数码管
				default:
					begin
						SH_CP <= 1'b0;
						DS    <= 1'b0;
						ST_CP <= 1'b0;
					end
			endcase
		end

每两个系统时钟周期计数一次,用此计数值模拟出时序图:
1、从0开始计数,SHCP_EDGE_CNT为0,模拟出时序SH_CP <= 0; ST_CP <= 1’b0; DS <= r_data[15];
2、计数到1,SH_CP <= 1’b1; ST_CP <= 1’b0;
3、计数到2,SH_CP <= 1’b0; ST_CP <= 1’b0;DS <= r_data[14];

31、计数到30、begin SH_CP <= 1’b0; DS <= r_data[0]; end
32、计数到31、 begin SH_CP <= 1’b1; end
33、计数到32、 begin ST_CP <= 1’b1; end

可以看到

  • SH_CP每计数一次翻转一次,即每两个系统时钟周期翻转一次,实现了SH_CP时钟频率为12.5MHz;
  • 当SH_CP下降沿,数据提前送到DS端,等到下一SH_CP上升沿将此数据移入到移位寄存器;
  • 计数到31,产生SH_CP上升沿,将最后一位数据移到移位寄存器;
  • 计数到32,此时串行数据都移入到移位寄存器,产生ST_CP上升沿,移位寄存器数据进入存储寄存器,使能OE/,16位并行数据送到数码管。

整体模块设计:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值