用Verilog写一个串口接收程序

用Verilog写一个异步串口UART接收程序

  • 异步串口通信协议
  • 串口接收原理
    • 接收流程
    • 状态机流图
    • RTL图
    • 源代码

异步串口通信协议

信息帧从一个低电平起始位开始,后面是5个至8个数据位(这里串口助手可以调),一个可选的奇偶校验位和1个或几个高电平停止位。

串口接收原理

假设串口的波特率是115200bps(波特率是指1秒最大传输的数据位数),这里为了提高接收机的抗干扰能力,采用8倍发射波特率的时钟去接收数据。对于一位数据,在接收时钟下如果接收到的’0’的数量多于’1’的数量,则判定为’0’,否则判定为’1’。

接收流程

  1. 首先将接收到的数据与接收时钟对其,这里可以用D触发器实现。
  2. 判断下降沿的到来:当检测到第一个低电平时,状态机状态将从R_IDLE跳到R_START状态。
  3. R_START状态中判断接下来的几个时钟是否是’0’多于’1’。如果是,则表明起始位的到来,并跳转到R_RECV状态。
  4. R_RECV中设置一个计数器,用于接收一定数量的数据位,接收完成后跳转到R_STOP状态。(这里没有用到奇偶校验位)
  5. R_STOP状态中接收停止位,接收完成则重新跳回R_IDLE状态,等待下一帧数据。

状态机流图

用Verilog写一个串口接收程序_第1张图片

RTL图

用Verilog写一个串口接收程序_第2张图片

源代码

module receive(
	input clk_8uart,
	input rxd,
	output rxd_ena,
	output[7:0] rxd_data
    );

reg rxd_delay;
reg[3:0] state; //控制状态转换
reg[3:0] rxd_cnt; //计时钟点数
reg[3:0] zero_cnt; //检测0的数量
reg[3:0] one_cnt; //检测1的数量
reg[3:0] bit_cnt; //8bit计数器
reg[7:0] rxd_output;
reg rxd_finish; //指示一个字节接收完成

localparam R_IDLE = 4'b0001;
localparam R_BEGIN = 4'b0010;
localparam R_RECV = 4'b0100;
localparam R_STOP = 4'b1000;

//*********与接收时钟对齐**********
always@(posedge clk_8uart)
begin
	rxd_delay <= rxd;
end
//*********状态转换**********
always@(posedge clk_8uart)
begin
	case(state)
		R_IDLE:  //空闲状态,等待下降沿
		begin
		rxd_finish <= 1'b0;
		bit_cnt <= 4'd0;
		rxd_output <= 8'd0;
			if(rxd_delay == 1'b0)
			begin
				state <= R_BEGIN;
				rxd_cnt <= 4'd0;
				zero_cnt <= 4'd0;
				one_cnt <= 4'd0;
			end
			else
				state <= R_IDLE;
		end
		R_BEGIN: //接收到下降沿后,判断是否是起始位(1bit低电平)
		begin
			if(rxd_delay == 1'b0)
				zero_cnt <= zero_cnt + 4'd1;
			else
				one_cnt <= one_cnt + 4'd1;
	
			if(rxd_cnt >= 4'd6)
			begin
				if(zero_cnt > one_cnt)
				begin
					rxd_cnt <= 4'd0;
					zero_cnt <= 4'd0;
					one_cnt <= 4'd0;
					state <= R_RECV;
				end
				else
				begin
					rxd_cnt <= 4'd0;
					zero_cnt <= 4'd0;
					one_cnt <= 4'd0;
					state <= R_IDLE;
				end
			end
			else
				rxd_cnt <= rxd_cnt + 4'd1;
		end
		R_RECV: //开始接收8bit数据
		begin
			if(rxd_delay == 1'b0)
				zero_cnt <= zero_cnt + 4'd1;
			else
				one_cnt <= one_cnt + 4'd1;
				
			if(rxd_cnt >= 4'd7)
			begin
				if(zero_cnt > one_cnt)
				begin
					rxd_cnt <= 4'd0;
					zero_cnt <= 4'd0;
					one_cnt <= 4'd0;
					rxd_output[bit_cnt] <= 1'b0;
				end
				else
				begin
					rxd_cnt <= 4'd0;
					zero_cnt <= 4'd0;
					one_cnt <= 4'd0;
					rxd_output[bit_cnt] <= 1'b1;
				end
				
				if(bit_cnt < 4'd7)
				begin
					bit_cnt <= bit_cnt + 4'd1;
					state <= R_RECV;
				end
				else
				begin
					bit_cnt <= 4'd0;
					state <= R_STOP;
				end
			end
			else
				rxd_cnt <= rxd_cnt + 4'd1;
		end
		R_STOP: //接受完8bit数据,接收停止位
		begin
			if(rxd_delay == 1'b0)
				zero_cnt <= zero_cnt + 4'd1;
			else
				one_cnt <= one_cnt + 4'd1;
				
			if(rxd_cnt >= 4'd7)
			begin
				if(zero_cnt > one_cnt)
				begin
					rxd_cnt <= 4'd0;
					zero_cnt <= 4'd0;
					one_cnt <= 4'd0;
					rxd_finish <= 1'b0;
					state <= R_IDLE;
				end
				else
				begin
					rxd_cnt <= 4'd0;
					zero_cnt <= 4'd0;
					one_cnt <= 4'd0;
					rxd_finish <= 1'b1;
					state <= R_IDLE;
				end
			end
			else
				rxd_cnt <= rxd_cnt + 4'd1;
		end
		default: state <= R_IDLE;
	endcase
end

assign rxd_ena = rxd_finish;
assign rxd_data = rxd_output;

endmodule

总体思路如上,如要加入额外功能,可在此基础上修改。(比如要接收奇偶校验位,并判断接收信息是否正确。)

你可能感兴趣的:(FPGA,verilog)