概述
目录
- 串口简介
- 串口通信简介
- 同步通信
- 异步通信
- 串口接头
- 协议标准
- 电气特性
- 时序介绍
- 设计思想
串口通信是FPGA较为基础的一个实验,本人在初学FPGA后决定将其整理一下并进一步加强自身理解。
串口简介
串口也称串行通信接口或串行通讯接口,是采用串行通信方式的扩展接口。目前人们使用的所有计算机操作系统都支持串行接口,其运用较为广泛。
串行指的是其将发送的数据一位一位的通过数据线传给对方,因此其通信只需要tx、rx两根数据线,从而大大降低了成本,较适合远距离通信。但也正是因为其串行的特点导致其通信速度较慢。
串口通信简介
由于其为串行通信,因此串口传送一个字节的数据要分为8次,并且从低到高按顺序一位一位进行传送。因此双方在传送数据时每一位都要有一个固定的时间间隔,以便能正确识别出两位数据;同时接收方也要能确定一个字节数据的开始于结束,因此双方还必须约定开始位与结束位。
常用的串行通信方式分为同步通信和异步通信。
同步通信
同步通信(SYNC:synchronous data communication)是指在约定的通信速率下,发送端和接收端的时钟信号频率和相位始终保持一致(同步),这样就保证了通信双方在发送和接收数据时具有完全一致的定时关系。
同步通信把许多字符组成一个信息组(信息帧),每帧的开始用同步字符来指示,一次通信只传送一帧信息。在传输数据的同时还需要传输时钟信号,以便接收方可以用时针信号来确定每个信息位。
同步通信的优点是传送信息的位数几乎不受限制,一次通信传输的数据有几十到几千个字节,通信效率较高。同步通信的缺点是要求在通信中始终保持精确的同步时钟,即发送时钟和接收时钟要严格的同步(常用的做法是两个设备使用同一个时钟源)。
异步通信
异步通信(ASYNC:asynchronous data communication),又称为起止式异步通信,是以字符为单位进行传输的,字符之间没有固定的时间间隔要求,而每个字符中的各位则以固定的时间传送。
在异步通信中,收发双方取得同步是通过在字符格式中设置起始位和停止位的方法来实现的。具体来说就是,在一个有效字符正式发送之前,发送器先发送一个起始位,然后发送有效字符位,在字符结束时再发送一个停止位,起始位至停止位构成一帧。停止位至下一个起始位之间是不定长的空闲位,并且规定起始位为低电平(逻辑值为0),停止位和空闲位都是高电平(逻辑值为1),这样就保证了起始位开始处一定会有一个下跳沿,由此就可以标志一个字符传输的起始。而根据起始位和停止位也就很容易的实现了字符的界定和同步。
显然,采用异步通信时,发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,可以互不同步。
串口接头
常用的串口接头有两种,一种是9针串口(简称DB-9),一种是25针串口(简称DB-25)。每种接头都有公头和母头之分,其中带针状的接头是公头,而带孔状的接头是母头。9针串口的外观如下图所示。
两种接口的管脚说明如下图所示:
协议标准
常用的串行通信接口标准有RS-232C、RS-422、RS-423和RS-485。其中,RS-232C作为串行通信接口的电气标准定义了数据终端设备(DTE:data terminal equipment)和数据通信设备(DCE:data communication equipment)间按位串行传输的接口信息,合理安排了接口的电气信号和机械要求,在世界范围内得到了广泛的应用。下面简单介绍下RS-232C的相关特性。
电气特性
RS-232C对电器特性、逻辑电平和各种信号功能都做了规定,如下:
在TXD和RXD数据线上:
(1)逻辑1为-3~-15V的电压
(2)逻辑0为3~15V的电压
在RTS、CTS、DSR、DTR和DCD等控制线上:
(1)信号有效(ON状态)为3~15V的电压
(2)信号无效(OFF状态)为-3~-15V的电压
由此可见,RS-232C是用正负电压来表示逻辑状态,与晶体管-晶体管逻辑集成电路(TTL)以高低电平表示逻辑状态的规定正好相反。
时序介绍
起始位: 在发送一个字节的数据前先发出一个逻辑“0”信号,表示传输的开始。
数据位: 在起始位之后所接的为数据位,注意串口通信是从低位开始传输,由低到高发送数据位。
奇偶校验位: 可选择是否添加奇偶校验,此位可让接收端验证收到的数据是否有错误。奇校验或偶校验都是判断“1”的个数为奇数或偶数。
停止位: 它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
空闲位: 处于逻辑“1”的状态。
发送过程时序图如下图所示。
TX为发送数据的数据线;SCLK为时钟线,当通信双方约定好波特率之后,双方都将按照此波特率进行传输数据,因此SCLK为FPGA端与接收端进行时钟对准的时钟线,其周期将根据波特率的变化而变化。
在传输过程中,首先将TX线拉低,此时即发送起始位信号,紧随其后的就是8位数据位,从低到高的数据依次发送,最后将TX拉高传输停止位。
设计思想
通过上文分析的时序图后可知数据传输需要SCLK时钟线,因此我们需要一个计数器来根据波特率来产生SCLK信号;由于在不同的时间需要传输不同的数据位因此我们还需要一个计数器来计数已发送的数据bit,并根据此来传输对应的数据。
发送端代码:
module uart_tx(
input clk,
input rst,
input data_rdy,
input [7:0] data,
output reg txd,
output reg tx_busy
);
parameter BAUD = 9600;
parameter CLOCK= 50_000_000;
localparam TIME_BIT = CLOCK / BAUD ;
wire tx_start;
reg flag;
reg [12:0] cnt_clk;
reg [ 3:0] cnt_length;
always@(posedge clk or negedge rst)begin //计时一个bit
if(!rst)begin
cnt_clk <= 13'd0;
end
else if(flag == 1'b1)begin
if(cnt_clk == TIME_BIT - 1)begin
cnt_clk <= 13'd0;
end
else begin
cnt_clk <= cnt_clk + 1'b1;
end
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
cnt_length <= 4'd0;
end
else begin
if(cnt_clk == TIME_BIT - 1 && flag == 1'b1 && cnt_length == 10 - 1)begin
cnt_length <= 4'd0;
end
else if(cnt_clk == TIME_BIT - 1 && flag == 1'b1)begin
cnt_length <= cnt_length + 1'b1;
end
end
end
assign tx_start = data_rdy & (!flag);
always@(posedge clk or negedge rst)begin
if(!rst)begin
flag <= 1'b0;
end
else if(tx_start)begin
flag <= 1'b1;
end
else if(cnt_clk == TIME_BIT - 1 && flag == 1'b1 && cnt_length == 10 - 1)begin
flag <= 1'b0;
end
end
always@(*)begin
if(!rst)begin
tx_busy = 1'b0;
end
else if(flag || data_rdy)begin
tx_busy = 1'b1;
end
else begin
tx_busy = 1'b0;
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
txd <= 1'b1;
end
else if(flag == 1'b1)begin
case(cnt_length)
4'd0:begin
txd <= 1'b0;
end
1,2,3,4,5,6,7,8:begin
txd <= data[cnt_length - 1];
end
9:begin
txd <= 1'b1;
end
endcase
end
end
endmodule
接收端代码
module uart_rx(
input clk,
input rst,
input rxd,
output reg[7:0] data,
output reg data_rdy
);
parameter BAUD = 9600;
parameter CLOCK= 50_000_000;
localparam TIME_BIT = CLOCK / BAUD ;
reg rxd_d1;
reg rxd_d2;
wire rxd_negedge;
reg flag/*synthesis noprune*/;
reg [12:0] cnt_clk;
reg [ 3:0] cnt_length;
reg [ 7:0] data_r;
always@(posedge clk or negedge rst)begin
if(!rst)begin
rxd_d1 <= 1'b1;
rxd_d2 <= 1'b1;
end
else begin
rxd_d1 <= rxd;
rxd_d2 <= rxd_d1;
end
end
assign rxd_negedge = rxd_d2 & (!rxd_d1); //检测下降沿,即为起始位
always@(posedge clk or negedge rst)begin //计时一个bit
if(!rst)begin
cnt_clk <= 13'd0;
end
else if(flag == 1'b1)begin
if(cnt_clk == TIME_BIT - 1)begin
cnt_clk <= 13'd0;
end
else begin
cnt_clk <= cnt_clk + 1'b1;
end
end
else begin
cnt_clk <= 13'd0;
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
cnt_length <= 4'd0;
end
else if(flag == 1'b1)begin
if(cnt_clk == TIME_BIT - 1 && flag == 1'b1 && cnt_length == 10 - 1)begin
cnt_length <= 4'd0;
end
else if(cnt_clk == TIME_BIT - 1 && flag == 1'b1)begin
cnt_length <= cnt_length + 1'b1;
end
end
else begin
cnt_length <= 4'd0;
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
flag <= 1'b0;
end
else begin
if(rxd_negedge)begin
flag <= 1'b1;
end
else if(cnt_clk == TIME_BIT - 2 && cnt_length == 10 - 1)begin
flag <= 1'b0;
end
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
data_r <= 8'd0;
end
else if(flag == 1'b1 && cnt_clk == 2604 - 1)begin //在中间读取数据
case(cnt_length)
1,2,3,4,5,6,7,8:begin
data_r[cnt_length - 1] <= rxd_d2;
end
default:;
endcase
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
data_rdy <= 1'b0;
data <= 8'd4;
end
else begin
if(cnt_clk == 2604 - 1 && flag == 1'b1 && cnt_length == 10 - 1)begin
data_rdy <= 1'b1;
data <= data_r;
end
else begin
data_rdy <= 1'b0;
end
end
end
endmodule
最后
以上就是和谐眼神为你收集整理的FPGA串口通信实验的全部内容,希望文章能够帮你解决FPGA串口通信实验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复