UART串口发送模块设计(学习记录)

在这里插入图片描述
send_en:发送使能信号

data_byte:待发送字符串

baud_set:波特率设置

rs232_tx:发送信号线

tx_done:发送结束标志

uart_state:模块状态 空闲为0,工作为1

在这里插入图片描述
照图施工,按照一直模块,编写各模块代码。

module uart_tybe_tx(
	Clk,
	Rst_n,
	data_byte,
	send_en,
	buad_set,
	
	Rs232_Tx,
	Tx_done,
	uart_state
);
	input Clk;
	input Rst_n;
	input [ 7:0]data_byte;
	input send_en;
	input [2:0]baud_set;

	output reg Rs232_Ttx;
	output reg Tx_done;
	output reg uart_state;
逻辑设计//
	reg dps_clk;//波特率时钟
	reg [15:0]div_cnt;//16位分频计数器
	reg [15:0]dsp_DR;//分频计数器最大值
	reg [3:0]dps_cnt;//波特率时钟计数器,计数到11,四位计数器
	reg [7:0]r_data_byte;//data_byte寄存器(防止中途数据改变)
	localparam STATE_BIT = 1'B0;//局部参数localparam 定义起始位
	localparam STOP_BIT = 1'B1;//定义结束位
	
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)
			uart_state <= 1'b0;
		else if(send_en)
			uart_state <= 1'b1;
		else if(dps_cnt == 4'd11)
			uart_state <= 1'b0;
		else 
			uart_state <= uart_state;
	
	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;
			//该过程能保证该数据在使用过程中是稳定的
//查找表设计
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)
			dps_DR <= 16'd5207;//默认对应9600波特率
		else begin
			case(baud_set)//总共三位,最多八种
				0:dps_DR  <= 16'd5207;
				1: dps_DR <= 16'2602;//19200
				2: dps_DR <= 16'1301;//38400
				3: dps_DR <= 16'd868;//57600
				4: dps_DR <= 16'd433;//115200
				//还有三种
				default:dps_DR <= 16'd5207;
			endcase
		end
//counter
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)
			div_cnt <= 16'd0;
		else if(uart_state)begin  //计数使能前提
			if(div_cnt == dps_DR)
				div_cnt <= 16'd0;
			else 
				div_cnt <= div_cnt +1'b1;//自加
		end
		else
			div_cnt <= 16'd0;

//单周期脉冲波特率时钟的产生dps_clk
always@(posedge Cclk or negedge Rst_n)
	if(!Rst_n)
		dps_clk <= 1'b0;
	else if(div_cnt == 16'd1)  //刚开始计数的时候就产生一个高脉冲
		dps_clk <= 1'b1;
	else 
		dps_clk <= 1'b0;
		
//dps counter
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)
			dps_cnt <= 4'd0;
		else if(dps_cnt == 4'd11)//清零
			dps_cnt <= 4'd0;
		else if(dps_clk)
			dps_cnt <= dps_cnt +1'b1;
		else 				//dps_clk为低电平的时候
			dps_cnt <= dps_cnt;

//发送结束标志位
always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Tx_done <= 1'b0;
	else if(dps_cnt == 4'd11)
		Tx_done <= 1'b1;
	else 
		Tx_done <= 1'b0;

//十选一多路器
always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Rs232_Tx <= 1'b1;//默认状态为高电平
	else begin 
		case(dps_cnt)
			0:Rs232_Tx <= STATE_BIT;//起始位
			1:Rs232_Tx <= r_data_byte[0];
			2:Rs232_Tx <= r_data_byte[1];
			3:Rs232_Tx <= r_data_byte[2];
			4: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

接下来为仿真程序:(uart_tybe_tx_tb)

`timescale 1ns/1ns
`define clk_period 20

module uart_tybe_tx_tb;
	reg Clk;
	reg Rrst_n;
	reg [7:0]data_byte;
	reg send_en;
	reg [2:0]baud_set;
	
	wire Rs232_Tx;
	wire Tx_done;
	wire yart_state;

	uart_tybe_tx uart_tybe_tx0(
		.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;
		data_byte = 8'd0;
		send_en =  1'b0;
		baud_set = 3'd4;//115200
		#(`clk_period*20+1)//+1为了与系统时钟避开,能更好看清仿真图像
		Rst_n = 1'b1;
		#(`clk_period*50);
		data_byte = 8'haa;
		send_en = 1'b1;
		#`clk_period;
		send_en = 1'b0;

		@(posedge Tx_done)//等待信号上升沿

		#(`clk_period*5000);//重新发送
		data_byte = 8'h55;
		send_en = 1'b1;
		#`clk_period;
		send_en = 1'b0;

		@(posedge Tx_done)
		#(`clk_period*5000);
		$stop;
	end
endmodule

仿真图象如下:觉得有点问题,之后再修改来看看
之后为板机调试,结合按键和led,会再建一个顶层模块和按键模块。
uart_tx_top:

//板机调试,新建一个顶层模块
module uart_tx_top(Clk,Rst_n,Rs232_Tx,key_in0,led);
	
	input Clk;
	input key_in0;
	input Rst_n;
	output Rs232_Tx;
	output led;
	
	wire send_en;
	wire [7:0]data_byte;
	wire key_state0;
	wire key_flag0;
	
	assign send_en = key_flag0 & !key_state0;//按键检测信号与按键状态为低电平
	uart_tybe_tx uart_tybe_tx0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(3'd4),//对应115200,虽说是端口,但是可以给定值
		
		.Rs232_Tx(Rs232_Tx),
		.Tx_done(),//不需要设计逻辑去控制,即可不使用
		.uart_state(led)//将串口的状态连接到led上,直接通过led显示串口状态
	);
	key_filter key_filter0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_in(key_in0),
		.key_state(key_state0),//key_state0和key_flag0作为控制信号
		.key_flag(key_flag0)
	);
	
	issp issp(
	.probe(),//探针不需要使用,就不连接,被综合掉
		.source(data_byte)
	);
	//一般不在顶层写代码
endmodule 

按键模块(key_filter)

module key_filter(Clk,Rst_n,key_in,key_state,key_flag);//filter:滤波
	input Clk;
	input Rst_n;
	input key_in;
	
	output reg key_state;//按键稳定状态
	output reg key_flag;//按键检测成功标志信号

	localparam//定义状态
		IDLE 	=	4'b0001,//空闲状态
		FILTER0 = 4'b0010,//按下滤波状态
		DOWN 	=	4'b0100,//按下稳定状态
		FILTER1 =	4'b1000;//释放滤波状态
		
		//在quartus中,寄存器放在哪里都行,但在modelsim中不行	
	reg[3:0]state;//状态寄存器
	reg key_temp0,key_temp1;//定义两个寄存器 
	wire pedge,negdge;//定义一个上升沿和一个下降沿
	
	reg cnt_full;//定义计数满寄存器(计数器满标志信号)
	reg [19:0]cnt;//定义计数器20位宽,需要计数20ms
					 //给出使能计数信号后,才开始计数,其余时候为清零状态
	reg en_cnt;//使能计数寄存器
	
	reg key_in_s0,key_in_s1;//两个同步寄存器
//同步逻辑(异步信号的同步处理)
	always@(posedge Clk or negedge Rst_n)//复位后寄存器有初始值
		if(!Rst_n)begin
			key_in_s0 <= 1'b0;
			key_in_s1 <= 1'b0;
		end 
		else begin
			key_in_s0 <= key_in;
			key_in_s1 <= key_in_s0;//key_in_s1就是已经同步到系统时钟	
		end 
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已同步到系统时钟域中)的电平状态	
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)begin
			key_temp0 <= 1'b0;
			key_temp1 <= 1'b0;
		end
		else begin
			key_temp0 <= key_in_s1;	//这里对于temp0使用两级D触发器,提高稳定性
			key_temp1 <= key_temp0;
		end
//检测上升沿与下降沿(边沿检测)
		assign negdge = !key_temp0 & key_temp1;//组合逻辑输出值为1,则检测到下降沿
		assign pedge = key_temp0 & (!key_temp1);//!为取非 ~为按位取反
		//eg:0110~1001   !(0110)=0   !(0000)=1
//状态机主程序
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin//所有赋值的值,再复位的时候都要赋个初值
			key_flag <= 1'b0;
			key_state <= 1'b1;
			state <= IDLE;
			en_cnt <= 1'b0;
		end 
		else begin
			case(state)
				IDLE:
					begin 
					key_flag <= 1'b0;//因为在FILTER1里拉高了,在这里要清零
					if(negdge)begin
						state <= FILTER0;
						en_cnt <= 1'b1;
					end 
					else 
						state <= IDLE;
					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 <= IDLE;
						en_cnt <= 1'b0;//表示这只是抖动,不用计数,等待下降沿的到来才开始计数
					end
					else
						state <= FILTER0;
				DOWN:
					begin
					key_flag <= 1'b0;//该语句与之后的if语句并行的
					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;
						//en_cnt <= 1'b0;//计数满了之后清零
						state <= IDLE;
					end 
					else if(negdge)begin//如果还没有计数完,检测到上升沿
						state <= DOWN;
						en_cnt <= 1'b0;//表示这只是抖动,不用计数,等待下降沿的到来才开始计数
					end
					else
						state <= FILTER1;
				default:
					begin
					en_cnt <= 1'b0;//给一个回到正常状态的值
					key_flag <= 1'b0;//默认为没有按键的时候的一个状态
					key_state <= 1'b1;
					state <= IDLE;
					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 == 999999)//1000000-1
			cnt_full <=  1'b1;
		else 				
			cnt_full <= 1'b0;	
	endmodule 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值