串口通讯
一、串口简介
串行接口,COM接口,只须要两根线就能实现两台设备之间的通讯。UART指的是异步的串行接口,通用异步收发。标准经常使用的是RS-232标准接口
如今电脑上没有串口了,因此使用的是USB转串口芯片,CH340芯片。
换句话说,只须要两根数据线UART_RXD和UART_TXD,就能完成两台设备之间的通讯。安全
二、串口时序
两根数据线各自独立互不影响,两者的时序是相同的。不一样之处是UART_RXD是主机MASTER发送给从机SLAVE,UART_TXD是SLAVE发送给MASTER。异步
因为两根线的时序彻底相同且独立,下面以UART_TXD为例。
空闲状态时,UART_TXD一直拉高,当要传输数据以前,拉低一个数据位,此后开始传输数据。数据以后有一个校验位,校验位以后是中止位,中止位以后进入下一个传输周期。至此,完成了一个数据包的传输。
注意:
(1)、传输的数据是从低比特位开始传,好比101010,接受端的接受顺序是010101。
(2)、传输数据的位数是MASTER与SLAVE约定好的,能够是四、五、六、七、8位,时序图中是以八位为例。
(3)、校验位通常是奇偶检验。固然,也能够选择没有检验位,前提是MASTER与SLAVE约定好,在SLAVE解析接收到的数据的时候,不安排校验位的解析。
(4)、中止位,中止位是保证两段传输之间必定要有间隔。两段传输之间能够没有空闲时间,可是,中止位必定要有。设计
三、时间的问题
从时序图上能够看出,串口的发送和接受是没有时钟的,换句话说,这是一个异步时序。那么如何肯定每一个位所须要的时间就尤其重要。
这个问题的要点是波特率,每秒发送/接受单位的个数。咱们使用的串口是以比特为单位,因此这里波特率与咱们的比特率相同。常见的波特率的数值有9600,19200,38400,57600,115200等。以9600为例,表示一秒钟发送/接受9600个比特。全部咱们能够计算出单个bit所占用的时间为 1s/9600 = 104166ns。咱们传输的起始标志位,传输的数据的每一位,校验位(无关紧要),在9600波特率的状况下,各自占据了104166ns的时间。
因此,假设从MASTER发送8'b11001110给SLAVE的的话,数据线的电平变化以下:blog
因此,FPGA在接收串口数据的时候,按照每一个位的时间,设计计数器。假设是50MHz的时钟,那么接受一个bit须要的时钟周期是
104166ns/20ns = 5208个周期,因此在计数器数到5208/2 +1的时候,将UART_RXD的当前值寄存便可。注意接受数据的时候先接受到的是起始位,最后的空闲位置就没有必要接受了。接口
四、代码
项目要求:电脑经过串口发送八位数据给FPGA,FPGA经过这八位数据来控制八个LED灯的亮暗。波特率9600,无校验位。input
代码以下:it
`module uart(
clk ,
rst_n ,
rx_uart ,
led
);
input clk ;
input rst_n ;
input rx_uart ;
output reg[7:0] led ;module
reg flag_add;
//波特率9600 1s/9600 = 104166ns
//50M时钟,一个周期是20ns,104166ns/20ns=5208个时钟周期
//用计数器来计数时钟周期,每到5208从rx_uart取出一个数据
//一个数据包是八个数据+一个起始位数据sed
//计数器cnt0用来计数每一个bit的5208个周期
reg[12:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;im
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = flag_add;
assign end_cnt0 = add_cnt0 && cnt0 == 5208 - 1 ;
//计数器1用来计数接受的bit数量
reg[3:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 9 - 1;
//flag_add的定义,rx_uart降低沿的时候拉高,end_cnt1的时候拉低
//须要一个rx_uart边沿检测电路,注意,rx_uart是一个异步信号,与本地时钟无关。
//能够把rx_uart放进敏感列表,可是他会被FPGA误认为是个时钟,形成电路不稳定
//安全的作法:用本地时钟对rx_uart进行延一拍,而后先后两拍比较完成边沿检测
//可是,由于本项目中的rx_uart是个异步信号,可能在时钟边沿变化,
//不能知足setup time 和 保持时间 因此就延两拍更安全。(两拍之后的信号是稳定信号)
reg rx_uart_ff0;
reg rx_uart_ff1;
reg rx_uart_ff2;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
rx_uart_ff0 <= 1;
rx_uart_ff1 <= 1;
rx_uart_ff2 <= 1;
end
else begin
rx_uart_ff0 <= rx_uart;
//rx_uart_ff0是异步信号打一拍的结果,不能作条件用。之后的能够
rx_uart_ff1 <= rx_uart_ff0;
rx_uart_ff2 <= rx_uart_ff1;
end
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
flag_add <= 0;
end
else if(!rx_uart_ff1 & rx_uart_ff2) begin//降低沿
flag_add <= 1;
end
else if(end_cnt1)
flag_add <= 0;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
led <= 8'hff;
end
else if(add_cnt0 && cnt0 == 5208/2-1 && cnt1>0)begin
led[cnt1-1] <= rx_uart_ff1;
end
endmodule`