Augus

你好,FPGA之Uart-数码管显示

0
阅读(4906)

功能实现: 

                1.按键控制串口数据接收

                2.数码管显示串口接收的数据

工具: in - system sources and probes editor 的使用

代码:

module uart_tx_top(clk,rst_n,rs232_tx,key_in0,sel,seg,led);
	input clk;
	input rst_n;
	input key_in0;
	wire [32:0]disp_data;
	
	output rs232_tx;
	output led;
	output [7:0] sel;//数码管位选(选择当前要显示的数码管)
	output [6:0] seg;//数码管段选(当前要显示的内容)
	
	wire send_en;
	wire [7:0]data_byte;
	wire key_flag0;
	wire key_state0;
	
	assign send_en = key_flag0 & !key_state0;
	
	uart_tx uart_tx(
		.clk(clk),
		.rst_n(rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(4'd4),
		
		.rs232_tx(rs232_tx),
		.tx_done(),
		.uart_state(led)
	);
	
	key_filter key_filter0(
		.clk(clk),
		.rst_n(rst_n),
		.key_in(key_in0),
		.key_flag(key_flag0),
		.key_state(key_state0)
	);
	
	HEX8 HEX8(
		.clk(clk),
		.rst_n(rst_n),
		.En(1'b1),
		.disp_data(data_byte),
		.sel(sel),
		.seg(seg)
	);
	
    issp issp (
        .source (data_byte)  // sources.source
    );


endmodule

module uart_tx(
					clk,
					rst_n,
					data_byte,
					send_en,
					baud_set,
					rs232_tx,
					tx_done,
					uart_state
					);
	
	input clk,rst_n;
	input [7:0]data_byte; //发送字节
	input send_en;//发送控制
	input [3:0]baud_set;//波特率选择
	
	output reg rs232_tx;//数据发送
	output reg tx_done;//发送完成
	output reg uart_state;//是否处于空闲状态
	
	//查找表--比特率选择
	reg [15:0]bps_max;//分频计数最大值
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_max <= 16'd5207;
	else begin
		case (baud_set)
			0:	bps_max <= 16'd5207;//9600
			1:	bps_max <= 16'd2603;//19200
			2:	bps_max <= 16'd1301;//38400
			3:	bps_max <= 16'd867;//57600
			4:	bps_max <= 16'd433;//115200
			default:bps_max <= 16'd5207;	
		endcase
	end
	
	reg [15:0]div_cnt;
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		div_cnt <= 16'd0;
	else if(uart_state)begin
		if(div_cnt == bps_max)
			div_cnt <= 16'd0;
		else
			div_cnt <= div_cnt +1'b1;
	end
	else
		div_cnt <= 16'd0;
	
	reg bps_clk;//波特率时钟
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_clk <= 1'b0;
	else if(div_cnt == 16'd1)
		bps_clk <= 1'b1;
	else
		bps_clk <= 1'b0;
		
	reg [3:0]bps_cnt;  //计数到11,波特率时钟计数器
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_cnt <= 4'd0;
	else if(bps_clk)
		bps_cnt <= bps_cnt + 1'b1;
	else if(bps_cnt == 4'd11)  //原tx_done 错
		bps_cnt <= 4'd0;
	else
		bps_cnt <= bps_cnt;
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		tx_done <= 1'b0;
	else if(bps_cnt == 4'd11)
		tx_done <= 1'b1;
	else
		tx_done <= 1'b0;
		
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		uart_state <= 1'b0;
	else if(send_en)
		uart_state <= 1'b1;
	else if(bps_cnt == 4'd11)//原tx_done
		uart_state <= 1'b0;
	else
		uart_state <= uart_state;
	
	reg [7:0]r_data_byte;
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		r_data_byte <= 8'd0;
	else if(send_en)
		r_data_byte <= data_byte;
	else
		r_data_byte <= r_data_byte;
	
	localparam START_BIT = 1'b0;
	localparam STOP_BIT = 1'b1;
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		rs232_tx <= 1'b1;
	else begin
		case(bps_cnt)
			0	:	rs232_tx <= 1'b1;
			1	:	rs232_tx <= START_BIT;
			2	:	rs232_tx <= r_data_byte[0];
			3	:	rs232_tx <= r_data_byte[1];
			4	:	rs232_tx <= r_data_byte[2];
			5	:	rs232_tx <= r_data_byte[3];
			6	:	rs232_tx <= r_data_byte[4];
			7	:	rs232_tx <= r_data_byte[5];
			8	:	rs232_tx <= r_data_byte[6];
			9	:	rs232_tx <= r_data_byte[7];
			10	:	rs232_tx <= STOP_BIT;
			default:rs232_tx <= 1'b1;
		endcase
	end	
	
endmodule

module key_filter(clk,rst_n,key_in,key_flag,key_state);

	input clk;
	input rst_n;
	input key_in;
	
	output reg key_flag;
	output reg key_state;
	
	localparam
		IDEL		= 4'b0001,
		FILTER0	= 4'b0010,
		DOWN		= 4'b0100,
		FILTER1 	= 4'b1000;
		
	reg [3:0]state;
	reg [19:0]cnt;
	reg en_cnt;	//使能计数寄存器
	
//对外部输入的异步信号进行同步处理
	reg key_in_sa,key_in_sb;
	always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_in_sa <= 1'b0;
		key_in_sb <= 1'b0;
	end
	else begin
		key_in_sa <= key_in;
		key_in_sb <= key_in_sa;	
	end
	
	reg key_tmpa,key_tmpb;
	wire pedge,nedge;
	reg cnt_full;//计数满标志信号
	
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态
	always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_tmpa <= 1'b0;
		key_tmpb <= 1'b0;
	end
	else begin
		key_tmpa <= key_in_sb;
		key_tmpb <= key_tmpa;	
	end

//产生跳变沿信号	
	assign nedge = !key_tmpa & key_tmpb;
	assign pedge = key_tmpa & (!key_tmpb);
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		en_cnt <= 1'b0;
		state <= IDEL;
		key_flag <= 1'b0;
		key_state <= 1'b1;
	end
	else begin
		case(state)
			IDEL :
				begin
					key_flag <= 1'b0;
					if(nedge)begin
						state <= FILTER0;
						en_cnt <= 1'b1;
					end
					else
						state <= IDEL;
				end
					
			FILTER0:
				if(cnt_full)begin
					key_flag <= 1'b1;
					key_state <= 1'b0;
					en_cnt <= 1'b0;
					state <= DOWN;
				end
				else if(pedge)begin
					state <= IDEL;
					en_cnt <= 1'b0;
				end
				else
					state <= FILTER0;
					
			DOWN:
				begin
					key_flag <= 1'b0;
					if(pedge)begin
						state <= FILTER1;
						en_cnt <= 1'b1;
					end
					else
						state <= DOWN;
				end
			
			FILTER1:
				if(cnt_full)begin
					key_flag <= 1'b1;
					key_state <= 1'b1;
					state <= IDEL;
				end
				else if(nedge)begin
					en_cnt <= 1'b0;
					state <= DOWN;
				end
				else
					state <= FILTER1;
			
			default:
				begin 
					state <= IDEL; 
					en_cnt <= 1'b0;		
					key_flag <= 1'b0;
					key_state <= 1'b1;
				end
				
		endcase	
	end
	

	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		cnt <= 20'd0;
	else if(en_cnt)
		cnt <= cnt + 1'b1;
	else
		cnt <= 20'd0;
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		cnt_full <= 1'b0;
	else if(cnt == 999_999)
		cnt_full <= 1'b1;
	else
		cnt_full <= 1'b0;	

endmodule

module HEX8(clk,rst_n,En,disp_data,sel,seg);

	input clk;	//50M
	input rst_n;
	input En;	//数码管显示使能,1使能,0关闭
	
	input [31:0]disp_data;
	
	output [7:0] sel;//数码管位选(选择当前要显示的数码管)
	output reg [6:0] seg;//数码管段选(当前要显示的内容)
	
	reg [14:0]divider_cnt;//25000-1
	
	reg clk_1K;
	reg [7:0]sel_r;
	
	reg [3:0]data_tmp;//数据缓存

//	分频计数器计数模块
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		divider_cnt <= 15'd0;
	else if(!En)
		divider_cnt <= 15'd0;
	else if(divider_cnt == 24999)
		divider_cnt <= 15'd0;
	else
		divider_cnt <= divider_cnt + 1'b1;

//1K扫描时钟生成模块		
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		clk_1K <= 1'b0;
	else if(divider_cnt == 24999)
		clk_1K <= ~clk_1K;
	else
		clk_1K <= clk_1K;
		
//8位循环移位寄存器
	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;
		
	always@(*)
		case(sel_r)
			8'b0000_0001:data_tmp = disp_data[3:0];
			8'b0000_0010:data_tmp = disp_data[7:4];
			8'b0000_0100:data_tmp = disp_data[11:8];
			8'b0000_1000:data_tmp = disp_data[15:12];
			8'b0001_0000:data_tmp = disp_data[19:16];
			8'b0010_0000:data_tmp = disp_data[23:20];
			8'b0100_0000:data_tmp = disp_data[27:24];
			8'b1000_0000:data_tmp = disp_data[31:28];
			default:data_tmp = 4'b0000;
		endcase
		
	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
		
	assign sel = (En)?sel_r:8'b0000_0000;

endmodule

`timescale 1ns/1ns
`define clk_period 20

module uart_tx_tb;

	reg clk;
	reg rst_n;
	reg send_en;
	reg [3:0]baud_set;
	reg [7:0]data_byte;
	
	wire rs232_tx;
	wire tx_done;
	wire uart_state;
	
	uart_tx uart_tx(
		.clk(clk),
		.rst_n(rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(baud_set),
		
		.rs232_tx(rs232_tx),
		.tx_done(tx_done),
		.uart_state(uart_state)
	);
	
	initial clk = 1;
	always#(`clk_period/2)clk = ~clk;
	
	initial begin
		rst_n = 1'b0;
		//key_in0 = 1'b1;
		data_byte = 8'd0;
		send_en = 1'd0;
		baud_set = 3'd4;
		#(`clk_period*20 + 1 )
		rst_n = 1'b1;
		//key_in0 = 1'b1;
		#(`clk_period*50);
		data_byte = 8'haa;
		send_en = 1'd1;
		#`clk_period;
		send_en = 1'd0;
		
		@(posedge tx_done)
		
		#(`clk_period*5000);
		data_byte = 8'h55;
		send_en = 1'd1;
		#`clk_period;
		send_en = 1'd0;
		
		@(posedge tx_done)
		#(`clk_period*5000);
		$stop;	
	end

endmodule

仿真:

效果:

2016-04-17_000122.jpg

更多资料请参考:

            发烧友小梅哥专版