FPGA学习之串口篇

FPGA学习之串口篇


前言

  UART(Universal Asynchronous Receiver/Transmitter)中文名称为通用异步收发器,是一种全双工异步通信总线,俗称串口。串口在MCU中很常见,串口仅用两根信号线就可以实现全双工通信,是各类MCU和一些传感器的标配,在高端SOC芯片中通常用USB取代UART。
  串口通信蕴含着最朴素的数字通信思想:通过协议规定收发双方的行为;通过约定动作获得收发同步;通过冗余实现错误检测。串口的传输格式如下:
在这里插入图片描述
  串口在不发送数据时,数据信号线上总是呈现高电平状态,称为空闲状态。当有数据发送时, 信号线变成低电平,并持续一个波特的时间, 用于表示发送字符的开始,该位称为起始位。起始位之后,在信号线上依次出现待发送的每一位字符数据, 并且按照先低位后高位的顺序逐位发送。待发送的位数可以选择5位、6位、7位或8位,而数据位的后面可以加上一位奇偶校验位,也可以选择不加。最后传送的是停止位,一般选择1位、1.5位或2位。


二、UART发送代码

  可以通过参数传递修改波特率。

module uart_tx #(
	parameter Burd_rate    = 9600 ,         // 波特率
	parameter Clk_freq     = 100_000_000    // 时钟频率
)(
	input clk_in,
	input rst_n,

	input  		 tx_flag,
	input  [7:0] tx_data,
	
	output reg tx

);

localparam CNT_MAX = Clk_freq/Burd_rate;
 
reg [7:0]   tx_data_reg;
reg [0:0]   trans_en;
reg [13:0]  burd_cnt;
reg  		bit_flag;
reg  [3:0]  bit_cnt;

// tx_flag为高时寄存tx_data
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		tx_data_reg <= 'h0;
	else if(tx_flag)
		tx_data_reg <= tx_data;
	else
		tx_data_reg <= tx_data_reg;
		
// 生成发送使能信号
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		trans_en <= 'h0;
	else if(tx_flag)
		trans_en <= 1'b1;		
	else if(bit_cnt == 4'd9 && bit_flag == 1'b1)
		trans_en <= 1'b0;		

// 累加波特计数器
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		burd_cnt <= 'h0;
	else if((trans_en == 0) || (burd_cnt == CNT_MAX -1))
		burd_cnt <= 'h0;		
	else if(trans_en)
		burd_cnt <= burd_cnt + 1'b1;	
		
// 生成比特发送使能信号
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		bit_flag <= 'b0;
	else if(burd_cnt ==  1)
		bit_flag <= 1'b1;		
	else 
		bit_flag <= 'b0;
		
// 累加比特计数器
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		bit_cnt <= 'b0;
	else if(bit_cnt ==  4'd9 && bit_flag == 1'b1)
		bit_cnt <= 'b0;		
	else if(bit_flag ==  1'b1)
		bit_cnt <= bit_cnt + 1'b1;
		
// 发送比特数据
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		tx <= 1'b1;
	else if(bit_flag == 1'b1) begin
		case(bit_cnt)
			0: tx <= 1'b0;
			1: tx <= tx_data_reg[0];
			2: tx <= tx_data_reg[1];
			3: tx <= tx_data_reg[2];
			4: tx <= tx_data_reg[3];
			5: tx <= tx_data_reg[4];
			6: tx <= tx_data_reg[5];
			7: tx <= tx_data_reg[6];
			8: tx <= tx_data_reg[7];
			9: tx <= 1'b1;
			default:tx <= 1'b1;
		endcase
	end

endmodule

三、UART接收代码

  可以通过参数传递修改波特率。

module uart_rx #(
	parameter Burd_rate    = 9600 ,         // 波特率
	parameter Clk_freq     = 100_000_000    // 时钟频率
)(
	input clk_in,
	input rst_n,
	input rx,
	
	output reg rx_flag,
	output reg [7:0] rx_data
);

localparam CNT_MAX =  Clk_freq/Burd_rate;
 
reg [2:0] rx_reg;
reg 	  start_flag;
reg 	  receive_en;
reg [13:0] buad_cnt;
reg [3:0] bit_cnt;
reg 	  bit_flag;
reg [7:0] rx_data_reg;
reg [7:0] rx_data;
reg [0:0] rx_flag;

wire fall_edge;
// 提取 rx打拍信号的下降沿
assign fall_edge = (~rx_reg[1]) && rx_reg[2] ;

// 将rx打三拍
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		rx_reg <= 3'b000;
	else 
		rx_reg <= {rx_reg[1],rx_reg[0],rx};
		
// 产生起始信号		
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		start_flag <= 1'b0;
	else if(fall_edge && (receive_en == 1'b0))
		start_flag <= 1'b1;
	else
		start_flag <= 1'b0;

// 产生计数使能信号		
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		receive_en <= 1'b0;
	else if(start_flag == 1'b1 && bit_cnt == 4'b0)
		receive_en <= 1'b1;
	else if((bit_cnt == 4'd9) && (buad_cnt == CNT_MAX-1))
	//else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
		receive_en <= 1'b0;	
	else		
		receive_en <= receive_en;	
		
// 波特计数器累加	
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		buad_cnt <= 'h0;
	else if(buad_cnt == CNT_MAX - 1 || receive_en == 1'b0)
		buad_cnt <= 'h0;
	else if(receive_en == 1'b1)
		buad_cnt <= buad_cnt + 1'b1;	

// 比特采样标志信号
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		bit_flag <= 1'b0;
	else if(buad_cnt == CNT_MAX/2 - 1)
		bit_flag <= 1'b1;
	else 
		bit_flag <= 1'b0;
		
// 接收比特计数器累加		
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		bit_cnt <= 4'b0;
	else if(bit_cnt == 4'd9  && buad_cnt== CNT_MAX-1)
		bit_cnt <= 4'b0;
	else if(bit_flag )
		bit_cnt <= bit_cnt + 1'b1;	
		
// 接收数据移位,先接收的是低位,最后接收的是高位		
always@(posedge clk_in or negedge rst_n)
	if(!rst_n)
		rx_data_reg <= 8'b0;
	else if(bit_flag && bit_cnt > 4'd0)
		rx_data_reg <= {rx_reg[2],rx_data_reg[7:1]};
	else if(bit_cnt == 4'b0)
		rx_data_reg <= 8'b0;
	else
		rx_data_reg <= rx_data_reg;
		
// 输出数据有效信号和八位数据		
always@(posedge clk_in or negedge rst_n)
	if(!rst_n) begin
		rx_data <= 8'b0;
		rx_flag <= 1'b0;
	end
	else if((bit_cnt == 4'd9) && (buad_cnt== CNT_MAX -1)) begin
		rx_data <= rx_data_reg;
		rx_flag <= 1'b1;		
	end
	else begin
		rx_data <= 8'b0;
		rx_flag <= 1'b0;
	end
	
endmodule

三、 总结

  串口一次最多传输8位而不是更多位的原因在于收发双方并不是时钟同源的,并不像SPI系统有一个主时钟供主、从机共享。如果收发双方时钟偏差超过5%,则双方对同一数据序列采样经过20个时钟周期后,就会出现一个样点差异。 这种情况必然会导致数据统计错误。而UART的设计标准就是允许收发两端的频率偏差在10%以内,因此当接收8位数据后,收发双方的误差刚好控制在一位以内,对数据的采样不会出错,可正确通信。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值