• 【FPGA零基础学习之旅#13】串口发送模块设计与验证


    🎉欢迎来到FPGA专栏~串口发送模块


    • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
    • 博客主页:小夏与酒的博客
    • 🎈该系列文章专栏:FPGA学习之旅
    • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
    • 📜 欢迎大家关注! ❤️
      FPGQ2

    CSDN

    遇见未来

    一、效果演示

    1.1 演示

    🥝发送测试:
    发送测试

    🥝issp调试测试:
    数据调试:
    更改数据
    调试数据发送:
    issp测试

    1.2 串口发送模块完整代码(可直接使用)

    模块

    🥝模块端口介绍:

    信号名称功能描述
    Clk系统时钟50MHz
    Rst_n系统复位信号
    data_byte待传输的8bit数据
    send_en发送使能信号
    baud_set波特率设置信号
    uart_tx串口信号输出
    Tx_Done发送结束信号,输出一个时钟周期高电平
    uart_state发送状态,处于发送状态时为1

    🥝baud_set值与波特率对应关系:

    baud_set波特率
    0009600
    00119200
    01038400
    01157600
    100115200

    uart_byte_tx.v:

    //
    //模块名称:串口发送模块
    //
    module uart_byte_tx(
    	input 		Clk,
    	input 		Rst_n,
    	input [7:0]	data_byte,
    	input 		send_en,
    	input [2:0]	baud_set,
    	
    	output reg uart_tx,
    	output reg Tx_Done,
    	output reg uart_state
    );
    
    	reg bps_clk;//波特率时钟
    	
    	reg [15:0]div_cnt;//分频计数器
    		
    	reg [15:0]bps_DR;//分频计数最大值
    	
    	reg [3:0]bps_cnt;//波特率计数时钟
    		
    	//定义数据的起始位和停止位
    	localparam START_BIT = 1'b0;
    	localparam STOP_BIT  = 1'b1;
    	
    	reg [7:0]r_data_byte;//数据寄存器
    	
    //----------------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			uart_state <= 1'b0;
    		else if(send_en)
    			uart_state <= 1'b1;
    		else if(bps_cnt == 4'd11)//bps_cnt计数达到11次,即发送结束
    			uart_state <= 1'b0;
    		else
    			uart_state <= uart_state;
    	end
    
    //--------<使能分频计数模块>-------	
    	assign en_cnt = uart_state;
    	
    //--------<寄存待发送的数据,使数据保持稳定>--------
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			r_data_byte <= 8'd0;
    		else if(send_en)
    			r_data_byte <= data_byte;
    		else
    			r_data_byte <= r_data_byte;
    	end
    	
    //--------<波特率查找表>--------		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			bps_DR <= 16'd5207;
    		else begin
    			case(baud_set)
    				0:bps_DR <= 16'd5207;
    				1:bps_DR <= 16'd2603;
    				2:bps_DR <= 16'd1301;
    				3:bps_DR <= 16'd867;
    				4:bps_DR <= 16'd433;
    				default:bps_DR <= 16'd5207;
    			endcase
    		end	
    	end
    	
    //----------------	
    //得到不同计数周期的计数器
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			div_cnt <= 16'd0;
    		else if(en_cnt)begin
    			if(div_cnt == bps_DR)
    				div_cnt <= 16'd0;
    			else
    				div_cnt <= div_cnt + 1'b1;
    		end
    		else
    			div_cnt <= 16'd0;
    	end
    //----------------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			bps_clk <= 1'b0;
    		else if(div_cnt == 16'd1)
    			bps_clk <= 1'b1;
    		else
    			bps_clk <= 1'b0;
    	end
    	
    //----------------		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			bps_cnt <= 4'd0;
    		else if(bps_cnt == 4'd11)//clr信号
    			bps_cnt <= 4'd0;
    		else if(bps_clk)
    			bps_cnt <= bps_cnt + 1'b1;
    		else
    			bps_cnt <= bps_cnt;
    	end
    
    //----------------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			Tx_Done <= 1'b0;
    		else if(bps_cnt == 4'd11)
    			Tx_Done <= 1'b1;
    		else
    			Tx_Done <= 1'b0;
    	end
    	
    //--------<数据位输出模块-10选1多路器>--------	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			uart_tx <= 1'b1;
    		else begin
    			case(bps_cnt)
    				0:uart_tx <= 1'b1;
    				1:uart_tx <= START_BIT;
    				2:uart_tx <= r_data_byte[0];
    				3:uart_tx <= r_data_byte[1];
    				4:uart_tx <= r_data_byte[2];
    				5:uart_tx <= r_data_byte[3];
    				6:uart_tx <= r_data_byte[4];
    				7:uart_tx <= r_data_byte[5];
    				8:uart_tx <= r_data_byte[6];
    				9:uart_tx <= r_data_byte[7];
    				10:uart_tx <= STOP_BIT;
    				default:uart_tx <= 1'b1;
    			endcase
    		end
    	end
    	
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    二、串口发送时序

    串口发送一个字节数据的时序图参考如下:
    时序图

    根据上图,一个字节想要成功发送完成,需要计数11次波特率时钟的高电平;而且一个完整的字节发送需要10位数据位,包括1个起始位8个数据位1个停止位

    需要注意,在串口未发送数据时(波特率时钟未使能时),串口输出高电平;在第一个波特率时钟高电平到来时,开始发送起始位。

    🥝波特率计算:

    baud_set波特率bps周期分频计数值50M系统时钟计数值
    09600104167ns104167/System_clk_period5208-1
    11920052083ns52083/System_clk_period2604-1
    23840026041ns26041/System_clk_period1302-1
    35760017361ns17361/System_clk_period868-1
    41152008680ns8680/System_clk_period434-1

    三、模块设计与代码详解

    根据小梅哥FPGA设计的电路图进行代码编写:
    电路设计
    代码理解主要参考前文中代码的模块注释串口发送时序图

    在此展示简单的测试激励文件:

    uart_byte_tx_tb.v:

    `timescale 1ns/1ns
    `define clock_period 20
    
    module uart_byte_tx_tb;
    
    	reg Clk;
    	reg Rst_n;
    	reg [7:0]data_byte;
    	reg send_en;
    	reg [2:0]baud_set;
    	
    	wire uart_tx;
    	wire Tx_Done;
    	wire uart_state;
    	
    	uart_byte_tx Uuart_byte_tx(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.data_byte(data_byte),
    		.send_en(send_en),
    		.baud_set(baud_set),	
    		.uart_tx(uart_tx),
    		.Tx_Done(Tx_Done),
    		.uart_state(uart_state)
    	);
    	
    	initial Clk = 1;
    	always#(`clock_period / 2) Clk = ~Clk;
    	
    	initial begin
    		Rst_n = 1'b0;
    		data_byte = 8'd0;
    		send_en = 1'b0;
    		baud_set = 3'd4;
    		#(`clock_period * 20 + 1);
    		Rst_n = 1'b1;
    		#(`clock_period * 50);
    		data_byte = 8'haa;
    		send_en = 1'b1;
    		#(`clock_period * 1);
    		send_en = 1'b0;
    		
    		@(posedge Tx_Done)
    		
    		#(`clock_period * 5000);
    		data_byte = 8'hff;
    		send_en = 1'b1;
    		#(`clock_period * 1);
    		send_en = 1'b0;
    		
    		@(posedge Tx_Done)
    		#(`clock_period * 5000);
    		
    		$stop;
    	end
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    仿真结果:

    仿真结果

    Tx_Done信号的正确输出:

    txdone信号

    四、按键控制串口发送数据

    先看整体的RTL视图来理解设计思路:
    RTL
    按键信号经过KeyFilter按键消抖模块之后的有效信号用来控制串口发送模块的发送使能;HEX8模块74HC595模块用来驱动三线制数码管显示数据;issp模块用来从电脑端调试数据;uart_byte_tx模块用来将调试数据通过串口发送给电脑。

    🔸按键消抖模块的详细讲解:【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)

    🔸HEX8模块的详细讲解:【FPGA零基础学习之旅#11】数码管动态扫描

    🔸74HC595模块的详细讲解:【FPGA零基础学习之旅#12】三线制数码管驱动(74HC595)串行移位寄存器驱动

    🔸issp ip核的创建和使用包含在文章【FPGA零基础学习之旅#11】数码管动态扫描中。

    在此给出顶层模块,其余模块见文末:

    uart_byte_tx_top.v:

    module uart_byte_tx_top(
    	input 			Clk,		//50M
    	input 			Rst_n,
    	input 			key_in,
    	output 			SH_CP,		//shift clock
    	output 			ST_CP,		//latch data clock
    	output 			DS,			//shift serial data
    	output 			uart_tx,
    	output			led
    );
    
    	wire [7:0] sel;//数码管位选(选择当前要显示的数码管)
    	wire [7:0] seg;//数码管段选(当前要显示的内容)	
    
    	wire  [7:0] data_byte;
    	
    	wire key_flag;
    	wire key_state;
    	
    	issp issp(
    		.probe(),
    		.source(data_byte)
    	);
    	
    	KeyFilter KeyFilter(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.key_in(key_in),
    		.key_flag(key_flag),
    		.key_state(key_state)
    	);
    	
    	//通过按键状态使能串口发送模块,按键按下时,串口发送模块使能
    	assign send_en = key_flag & (!key_state);
    	
    	uart_byte_tx uart_byte_tx(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.data_byte(data_byte),
    		.send_en(send_en),
    		.baud_set(3'd0),
    		.uart_tx(uart_tx),
    		.Tx_Done(),
    		.uart_state(led)
    	);
    
    	//由于串口目前只发送一个字节数据,只占用2个数码管,所以其余数码管显示0
    	HEX8 HEX8(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.En(1'b1),
    		.disp_data({24'h0,data_byte}),
    		.sel(sel),
    		.seg(seg)
    	);
    
    	m74HC595_Driver m74HC595_Driver(
    		.Clk(Clk),
    		.Rst_n(Rst_n),
    		.Data({seg,sel}),
    		.S_EN(1'b1),
    		.SH_CP(SH_CP),
    		.ST_CP(ST_CP),
    		.DS(DS)
    	);
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    程序配置完成之后的数码管显示和led的点亮:
    程序配置完成

    使用issp更改数据,数码管显示数据并通过按键发送:
    🥝修改为11:
    11
    发送

    🥝修改为AF:
    af
    afff

    本案例的其余模块在此部分列出:

    KeyFilter.v:

    //
    //模块:按键消抖模块
    //key_state:输出消抖之后按键的状态
    //key_flag:按键消抖结束时产生一个时钟周期的高电平脉冲
    //
    module KeyFilter(
    	input 		Clk,
    	input	 	Rst_n,
    	input 		key_in,
    	output reg 	key_flag,
    	output reg 	key_state
    );
    
    	//按键的四个状态
    	localparam
    		IDLE 		= 4'b0001,
    		FILTER1 	= 4'b0010,
    		DOWN 		= 4'b0100,
    		FILTER2 	= 4'b1000;
    
    	//状态寄存器
    	reg [3:0] curr_st;
    	
    	//边沿检测输出上升沿或下降沿
    	wire pedge;
    	wire nedge;
    	
    	//计数寄存器
    	reg [19:0]cnt;
    	
    	//使能计数寄存器
    	reg en_cnt;
    	
    	//计数满标志信号
    	reg cnt_full;//计数满寄存器
    	
    //------<边沿检测电路的实现>------
    	//边沿检测电路寄存器
    	reg key_tmp0;
    	reg key_tmp1;
    	
    	//边沿检测
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			key_tmp0 <= 1'b0;
    			key_tmp1 <= 1'b0;
    		end
    		else begin
    			key_tmp0 <= key_in;
    			key_tmp1 <= key_tmp0;
    		end	
    	end
    		
    	assign nedge = (!key_tmp0) & (key_tmp1);
    	assign pedge = (key_tmp0)  & (!key_tmp1);
    
    //------<状态机主程序>------	
    	//状态机主程序
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			curr_st <= IDLE;
    			en_cnt <= 1'b0;
    			key_flag <= 1'b0;
    			key_state <= 1'b1;
    		end
    		else begin
    			case(curr_st)
    				IDLE:begin
    					key_flag <= 1'b0;
    					if(nedge)begin
    						curr_st <= FILTER1;
    						en_cnt <= 1'b1;
    					end
    					else
    						curr_st <= IDLE;
    				end
    				
    				FILTER1:begin
    					if(cnt_full)begin
    						key_flag <= 1'b1;
    						key_state <= 1'b0;
    						curr_st <= DOWN;
    						en_cnt <= 1'b0;
    					end	
    					else if(pedge)begin
    						curr_st <= IDLE;
    						en_cnt <= 1'b0;
    					end
    					else
    						curr_st <= FILTER1;
    				end
    				
    				DOWN:begin
    					key_flag <= 1'b0;
    					if(pedge)begin
    						curr_st <= FILTER2;
    						en_cnt <= 1'b1;
    					end
    					else
    						curr_st <= DOWN;
    				end
    				
    				FILTER2:begin
    					if(cnt_full)begin
    						key_flag <= 1'b1;
    						key_state <= 1'b1;
    						curr_st <= IDLE;
    						en_cnt <= 1'b0;
    					end	
    					else if(nedge)begin
    						curr_st <= DOWN;
    						en_cnt <= 1'b0;
    					end
    					else
    						curr_st <= FILTER2;
    				end
    				
    				default:begin
    					curr_st <= IDLE;
    					en_cnt <= 1'b0;
    					key_flag <= 1'b0;
    					key_state <= 1'b1;
    				end
    			endcase
    		end
    	end
    	
    //------<20ms计数器>------		
    	//20ms计数器
    	//Clk 50_000_000Hz
    	//一个时钟周期为20ns
    	//需要计数20_000_000 / 20 = 1_000_000次
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			cnt <= 20'd0;
    		else if(en_cnt)
    			cnt <= cnt + 1'b1;
    		else
    			cnt <= 20'd0;
    	end
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			cnt_full <= 1'b0;
    		else if(cnt == 999_999)
    			cnt_full <= 1'b1;
    		else
    			cnt_full <= 1'b0;
    	end
    	
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153

    HEX8.v:

    module HEX8(
    		input 					Clk,			//50M
    		input 					Rst_n,			//复位
    		input 					En,				//数码管显示使能
    		input 		[31:0]		disp_data,		//8 × 4 = 32(8个数码管,数据格式为hex,总共输32位)
    		output reg 	[7:0]		seg, 			//数码管段选
    		output 		[7:0]		sel	 			//数码管位选(数码管选择)
    );
    	reg [7:0]sel_r;
    
    //----------<分频器>----------
    	reg [14:0]divider_cnt;//25000-1
    	
    	reg clk_1K;
    	
    	reg [3:0]data_tmp;//待显示数据缓存
    	
    	//1KHz分频计数器
    	always@(posedge Clk or negedge Rst_n)begin
    		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;
    	end
    	
    	//1KHz扫描时钟
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			clk_1K <= 1'b0;
    		else if(divider_cnt == 24999)
    			clk_1K <= ~clk_1K;
    		else
    			clk_1K <= clk_1K;
    	end
    	
    //----------<6位循环移位寄存器>-----------	
    	always@(posedge clk_1K or negedge Rst_n)begin
    		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;
    	end	
    
    //----------<6选1多路器>----------		
    	always@(*)begin
    		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
    	end
    
    //--------------------		
    	always@(*)begin
    		case(data_tmp)
    			4'h0:seg = 8'hc0;
    			4'h1:seg = 8'hf9;
    			4'h2:seg = 8'ha4;
    			4'h3:seg = 8'hb0;
    			4'h4:seg = 8'h99;
    			4'h5:seg = 8'h92;
    			4'h6:seg = 8'h82;
    			4'h7:seg = 8'hf8;
    			4'h8:seg = 8'h80;
    			4'h9:seg = 8'h90;
    			4'ha:seg = 8'h88;
    			4'hb:seg = 8'h83;
    			4'hc:seg = 8'hc6;
    			4'hd:seg = 8'ha1;
    			4'he:seg = 8'h86;
    			4'hf:seg = 8'h8e;
    		endcase
    	end
    		
    //----------<2选1多路器>----------		
    	assign sel = (En)?(sel_r):8'b1111_1111;
    		
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    m74HC595_Driver.v:

    module m74HC595_Driver(
    		Clk,
    		Rst_n,
    		Data,
    		S_EN,
    		SH_CP,
    		ST_CP,
    		DS
    	);
    
    	parameter DATA_WIDTH = 16;
    
    	input 		Clk;
    	input 		Rst_n;
    	input 		[DATA_WIDTH-1 : 0] Data;	//data to send
    	input 		S_EN;						//send en
    	output reg 	SH_CP;						//shift clock
    	output reg 	ST_CP;						//latch data clock
    	output reg 	DS;							//shift serial data
    	
    	parameter CNT_MAX = 4;
    		
    	reg [15:0] divider_cnt;//分频计数器
    	wire sck_pluse;
    	
    	reg [4:0]SHCP_EDGE_CNT;//SH_CP EDGE counter
    	
    	reg [15:0]r_data;
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			r_data <= 16'd0;
    		else if(S_EN)
    			r_data <= Data;
    		else
    			r_data <= r_data;
    	end
    		
    	//clock divide
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			divider_cnt <= 16'd0;
    		else if(divider_cnt == CNT_MAX)
    			divider_cnt <= 16'd0;
    		else
    			divider_cnt <= divider_cnt + 1'b1;
    	end
    	
    	assign sck_pluse = (divider_cnt == CNT_MAX);
    	
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)
    			SHCP_EDGE_CNT <= 5'd0;
    		else if(sck_pluse)begin
    			if(SHCP_EDGE_CNT ==  5'd31)
    				SHCP_EDGE_CNT <= 5'd0;
    			else
    				SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
    		end
    		else
    			SHCP_EDGE_CNT <= SHCP_EDGE_CNT;
    	end	
    		
    	always@(posedge Clk or negedge Rst_n)begin
    		if(!Rst_n)begin
    			SH_CP <= 1'b0;
    			ST_CP <= 1'b0;
    			DS <= 1'b0;	
    		end
    		else begin
    			case(SHCP_EDGE_CNT)
    				5'd0: begin SH_CP <= 1'b0; ST_CP <= 1'b1; DS <= r_data[15]; end
    				5'd1: begin SH_CP <= 1'b1; ST_CP <= 1'b0;end
    				5'd2: begin SH_CP <= 1'b0; DS <= r_data[14];end
    				5'd3: begin SH_CP <= 1'b1; end
    				5'd4: begin SH_CP <= 1'b0; DS <= r_data[13];end
    				5'd5: begin SH_CP <= 1'b1; end
    				5'd6: begin SH_CP <= 1'b0; DS <= r_data[12];end
    				5'd7: begin SH_CP <= 1'b1; end
    				5'd8: begin SH_CP <= 1'b0; DS <= r_data[11];end
    				5'd9: begin SH_CP <= 1'b1; end
    				5'd10:begin SH_CP <= 1'b0; DS <= r_data[10];end
    				5'd11:begin SH_CP <= 1'b1; end
    				5'd12:begin SH_CP <= 1'b0; DS <= r_data[9];end
    				5'd13:begin SH_CP <= 1'b1; end
    				5'd14:begin SH_CP <= 1'b0; DS <= r_data[8];end
    				5'd15:begin SH_CP <= 1'b1; end
    				5'd16:begin SH_CP <= 1'b0; DS <= r_data[7];end
    				5'd17:begin SH_CP <= 1'b1; end
    				5'd18:begin SH_CP <= 1'b0; DS <= r_data[6];end
    				5'd19:begin SH_CP <= 1'b1; end
    				5'd20:begin SH_CP <= 1'b0; DS <= r_data[5];end
    				5'd21:begin SH_CP <= 1'b1; end
    				5'd22:begin SH_CP <= 1'b0; DS <= r_data[4];end
    				5'd23:begin SH_CP <= 1'b1; end
    				5'd24:begin SH_CP <= 1'b0; DS <= r_data[3];end
    				5'd25:begin SH_CP <= 1'b1; end
    				5'd26:begin SH_CP <= 1'b0; DS <= r_data[2];end
    				5'd27:begin SH_CP <= 1'b1; end
    				5'd28:begin SH_CP <= 1'b0; DS <= r_data[1];end
    				5'd29:begin SH_CP <= 1'b1; end
    				5'd30:begin SH_CP <= 1'b0; DS <= r_data[0];end
    				5'd31:begin SH_CP <= 1'b1; end
    				default:begin SH_CP <= 1'b0;ST_CP <= 1'b0;DS <= 1'b0;	end
    			endcase	
    		end
    	end
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    csdn

    🧸结尾


  • 相关阅读:
    常用的shell命令
    用frp搞个内网穿透
    索引和事务
    mssql拿shell
    ctfshow web41-web50
    Linux选择题笔记
    (附源码)ssm高考志愿智能选择系统 毕业设计 134565
    React技术栈 --》组件生命周期和Vue拓展 ## Day6
    关于链表指针的深刻理解
    Android:安卓学习笔记之共享元素的简单理解和使用
  • 原文地址:https://blog.csdn.net/m0_56262476/article/details/132783448