用Verilog写一个串口接收程序

异步串口通信协议

信息帧从一个低电平起始位开始,后面是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状态,等待下一帧数据。

状态机流图

在这里插入图片描述

RTL图

在这里插入图片描述

源代码

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

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

  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基本的串口程序,使用Verilog语言实现: module uart ( input clk, // 时钟信号 input reset, // 复位信号 input tx_en, // 发送使能信号 input [7:0] tx_data, // 发送数据 output tx, // 发送数据线 input rx, // 接收数据线 output [7:0] rx_data, // 接收数据 output rx_valid // 接收数据有效信号 ); // 定义常量 parameter BAUD_RATE = 9600; // 波特率 parameter CLK_FREQ = 50000000; // 时钟频率 // 定义寄存器 reg [3:0] state_reg; // 状态寄存器 reg [7:0] tx_reg; // 发送数据寄存器 reg [3:0] bit_count_reg; // 位计数寄存器 reg [31:0] baud_count_reg; // 波特率计数寄存器 reg rx_data_reg; // 接收数据寄存器 reg rx_valid_reg; // 接收数据有效寄存器 // 初始化寄存器 initial begin state_reg = 4'b0000; tx_reg = 8'h00; bit_count_reg = 4'b0000; baud_count_reg = 32'h00000000; rx_data_reg = 1'b0; rx_valid_reg = 1'b0; end // 发送数据状态机 always @ (posedge clk) begin if (reset) begin state_reg <= 4'b0000; tx_reg <= 8'h00; bit_count_reg <= 4'b0000; baud_count_reg <= 32'h00000000; tx <= 1'b1; end else begin case (state_reg) 4'b0000: begin // 空闲状态 if (tx_en) begin // 发送使能 tx_reg <= tx_data; bit_count_reg <= 4'b0000; baud_count_reg <= 32'h00000000; tx <= 1'b0; state_reg <= 4'b0001; // 进入发送状态 end else begin tx <= 1'b1; end end 4'b0001: begin // 发送状态 if (baud_count_reg == (CLK_FREQ / BAUD_RATE) - 1) begin // 波特率计数到达 baud_count_reg <= 32'h00000000; if (bit_count_reg == 4'b1000) begin // 位计数到达 tx <= 1'b1; state_reg <= 4'b0000; // 进入空闲状态 end else begin tx <= tx_reg[bit_count_reg]; bit_count_reg <= bit_count_reg + 1; end end else begin baud_count_reg <= baud_count_reg + 1; end end default: begin // 错误状态 state_reg <= 4'b0000; end endcase end end // 接收数据状态机 always @ (posedge clk) begin if (reset) begin rx_data_reg <= 1'b0; rx_valid_reg <= 1'b0; end else begin case (state_reg) 4'b0000: begin // 空闲状态 if (!rx) begin // 开始接收数据 baud_count_reg <= 32'h00000000; bit_count_reg <= 4'b0000; rx_data_reg <= 1'b0; rx_valid_reg <= 1'b0; state_reg <= 4'b0010; // 进入接收状态 end end 4'b0010: begin // 接收状态 if (baud_count_reg == (CLK_FREQ / BAUD_RATE) - 1) begin // 波特率计数到达 baud_count_reg <= 32'h00000000; bit_count_reg <= bit_count_reg + 1; if (bit_count_reg == 4'b1000) begin // 位计数到达 rx_valid_reg <= 1'b1; state_reg <= 4'b0000; // 进入空闲状态 end else begin rx_data_reg <= {rx_data_reg[6:0], rx}; // 接收数据 end end else begin baud_count_reg <= baud_count_reg + 1; end end default: begin // 错误状态 state_reg <= 4'b0000; end endcase end end // 输出接收数据 assign rx_data = rx_data_reg; assign rx_valid = rx_valid_reg; endmodule 注意:此代码未经过编译和测试,仅作为示例提供。实际使用时需要根据具体的硬件平台和应用场景进行修改和调试。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值