UART 是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。串口作为常用的三大低速总线之一。不同于 SPI、IIC 是同步通信接口,UART是全双工异步通信接口,接受方是在数据的起始位和停止位的帮助下实现信息同步的。UART 通信只有两根信号线,串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。
区别异步与同步通信以及单工、半双工与全双工通信:
1、 在异步通信中,数据被分成小块,每个小块都包含一些数据以及同步信息。发送和接收之间没有固定的时间间隔。一方发送,不考虑另一方是否收到,直接进行下一次传输。异步通信常用于短距离数据传输,例如串口通信。
2、在同步通信中,数据被分成连续的块,每个块都有一个固定的大小,并且发送方和接收方使用共同的时钟源来保持数据的同步。
3、单工通信只能在一个方向上传输数据。例如,广播电台只能向听众发送信息,而不能接收来自听众的信息。
4、 半双工通信允许数据在两个方向之间进行传输,但是不能同时进行。在半双工通信中,每个方向上只能有一个方向进行数据传输。例如iic通信。
5、全双工通信允许数据在两个方向上同时传输。这意味着通信双方可以同时发送和接收数据。例如串口和spi通信。
波特率(baud rate)是指数字通信中单位时间内传输的符号数。在串行通信中,数据被拆分为一个个符号,每个符号由一定数量的比特(bit)组成,每个比特在一定时间内传输完成。波特率就是指每秒钟可以传输的比特数。串口常见的波特率有 4800、9600、115200 等。如波特率为9600,串口发送或者接收 1bit 数据的时间则为一个波特,即 1/9600 秒。正常情况下,用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1 * 10^9) / 9600) / 20 ≈ 5208 个系统时钟周期。在串口RS232通信过程中,接收方必须检测到一个波特时间的起始位才能开始接收数据,再接收一个波特时间的停止位。
在FPAG设计中,亚稳态通常指的是时序亚稳态,即由于电路中存在的不完美、不均匀等因素,导致电路的时序存在一定的不稳定性。具体而言,时序亚稳态可能会导致电路在某些情况下无法正常工作或者工作不稳定。可以采用时钟树设计来保证时序的稳定性;可以采用布线规则来保证信号的可靠传输;可以采用静态时序分析和动态时序分析来预测电路的时序特性等。
而对于用Verilog设计来说,时钟设计是考虑较多的,而时钟设计需要考虑以下几个方面:
-
时钟树设计:时钟树是将时钟信号分配给所有时序元件的网络。
-
时钟缓冲器的选择:时钟缓冲器是将时钟信号从一个时钟域转换到另一个时钟域的关键元件。选择合适的时钟缓冲器可以提高时钟信号的稳定性和传输质量。常用触发器同步器和FIFO来解决。
i、触发器同步器通常由两个触发器和一些逻辑门组成(也可能多个触发器,如本项目工程中使用三个触发器)。其中,一个触发器用于接收异步输入信号,并在时钟边沿触发后将输入信号存储到寄存器中;另一个触发器用于接收同步时钟信号,并在时钟边沿触发时将存储的数据输出。逻辑门用于控制时序和信号方向,从而实现数据的同步传输(注意:对异步信号进行同步处理只能减少亚稳态产生的概率,无法彻底消除亚稳态的现象)。 i i、FIFO(First-In, First-Out)是一种常用的存储器件,可以缓存数据并按照输入的顺序输出。(具体不展开) -
时钟分频:时钟分频是将时钟信号的频率降低到更低的频率,以减少时钟信号传输时延和抖动。时钟分频还可以降低电路的功耗和热功耗,提高电路的可靠性和性能。
uart接收模块
module uart_rs232_rx(
input wire clk_sys ,
input wire rst_n_sys ,
input wire rx_data , //接收的数据
output reg [7:0]po_data ,
output reg po_flag
);
reg rx_reg1,rx_reg2,rx_reg3 ;
reg [12:0]baud_cnt ;
reg [3:0] bit_cnt ;
reg start_flag ;
reg work_en ;
reg baud_flag ;
reg rx_flag ;
reg [7:0]rx_data_reg ;
parameter BIT_CNT_MAX =13'D19 ;//仿真用,需自行修改
parameter BIT_CNT_X =13'D9 ;
parameter BAUD_CNT_MAX=4'D8 ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
begin
rx_reg1<=1'b1 ;
rx_reg2<=1'b1 ;
rx_reg3<=1'b1 ;
end
else
begin
rx_reg1<=rx_data ;
rx_reg2<=rx_reg1 ;
rx_reg3<=rx_reg2 ;
end
//
always@(posedge clk_sys or negedge rst_n_sys)
if (~rst_n_sys)
start_flag<=1'b0 ;
else if(rx_reg2==1'b0 && rx_reg3==1'b1 && work_en==1'b0)
start_flag<=1'b1 ;
else
start_flag<=1'b0 ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
work_en<=1'b0 ;
else if(start_flag==1'b1)
work_en<=1'b1 ;
else if(baud_cnt==BIT_CNT_X && bit_cnt==BAUD_CNT_MAX)
work_en<=1'b0 ;
else
work_en<=work_en ;
always@(posedge clk_sys or negedge rst_n_sys)
if (~rst_n_sys)
bit_cnt<=1'b0 ;
else if(work_en==1'b0)
bit_cnt<=1'b0 ;
else if(baud_flag==1'b1)
bit_cnt<=bit_cnt+1'b1 ;
else
bit_cnt<=bit_cnt ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
baud_flag<=1'b0 ;
else if(baud_cnt==BIT_CNT_X-1'b1)
baud_flag<=1'b1 ;
else
baud_flag<=1'b0 ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
baud_cnt<=13'b0 ;
else if(work_en==1'b0 || baud_cnt==BIT_CNT_MAX)
baud_cnt<=13'b0 ;
else if(work_en==1'b1)
baud_cnt<=baud_cnt+1'b1 ;
else
baud_cnt<=baud_cnt ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
rx_flag<=1'b0 ;
else if(baud_cnt==BIT_CNT_X && bit_cnt==BAUD_CNT_MAX)
rx_flag<=1'b1 ;
else
rx_flag<=1'b0 ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
rx_data_reg<=8'd0 ;
else if(bit_cnt>=4'b1 && bit_cnt<=4'd8 && baud_flag==1'b1)
rx_data_reg<={rx_reg3,rx_data_reg[7:1]} ;
else
rx_data_reg<=rx_data_reg ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
po_data<=8'b0 ;
else if(rx_flag==1'b1)
po_data<=rx_data_reg ;
else
po_data<=po_data ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
po_flag<=1'b0 ;
else
po_flag<=rx_flag ;
endmodule
uart发送模块
module uart_rs232_tx(
input wire clk_sys ,
input wire rst_n_sys ,
input wire [7:0] pi_data ,
input wire pi_flag ,
output reg tx_data
);
reg [3:0] bit_cnt ;
reg [12:0] baud_cnt ;
reg tx_work_en ;
parameter BAUD_CNT_MAX=13'd19 ; //仿真用的波特时间400ns
parameter BIT_CNT_MAX =4'd8 ; //八个有效数据
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
tx_work_en<=1'b0 ;
else if(bit_cnt==BIT_CNT_MAX && baud_cnt==BAUD_CNT_MAX)
tx_work_en<=1'b0 ;
else if(pi_flag==1'b1)
tx_work_en<=1'b1 ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
bit_cnt<=4'b0 ;
else if (tx_work_en==1'b0)
bit_cnt<=4'b0 ;
else if (baud_cnt==BAUD_CNT_MAX)
bit_cnt<=bit_cnt+1'b1 ;
else
bit_cnt<=bit_cnt ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
baud_cnt<=4'b0 ;
else if (tx_work_en==1'b0 || baud_cnt==BAUD_CNT_MAX)
baud_cnt<=4'b0 ;
else
baud_cnt<=baud_cnt+1'b1 ;
always@(posedge clk_sys or negedge rst_n_sys)
if(~rst_n_sys)
tx_data<=1'b1 ;
else if (baud_cnt==13'b0 && tx_work_en==1'b1)
begin
case (bit_cnt)
4'd0:tx_data <= 1'b0 ;
4'd1:tx_data <= pi_data[0] ;
4'd2:tx_data <= pi_data[1] ;
4'd3:tx_data <= pi_data[2] ;
4'd4:tx_data <= pi_data[3] ;
4'd5:tx_data <= pi_data[4] ;
4'd6:tx_data <= pi_data[5] ;
4'd7:tx_data <= pi_data[6] ;
4'd8:tx_data <= pi_data[7] ;
default:tx_data<=1'b1 ;
endcase
end
else if(tx_work_en==1'b0)
tx_data<=1'b1 ;
else
tx_data<=tx_data ;
endmodule
出现错误时,我们可以按这个顺序排查错误。首先检查是否使用了正确的波特率。波特率必须与发送端和接收端相同并且时钟频率必须与波特率相匹配。其次,检查是否使用了正确的数据位数和校验位。这些参数必须与发送端和接收端一致。最后,检查程序是否正确。确保程序没有逻辑错误,比如发送和接收的数据类型不匹配。最后,检查是否使用了正确的发送和接收协议。协议必须包括正确的开始位、数据位、校验位和停止位。文章来源:https://www.toymoban.com/news/detail-735861.html
总结个人收获与感受:我学会了如何使用Verilog语言来实现串口传输。我学会了如何编写Verilog代码来控制串口的收发数据,并将数据转换为适当的信号进行传输。我掌握了RS232串口传输的原理和工作方式。我学会了如何正确配置串口通信参数,包括波特率、数据位、停止位和校验位等,以及如何处理错误和异常情况。文章来源地址https://www.toymoban.com/news/detail-735861.html
到了这里,关于【学习笔记】串口通信RS232的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!