FPGA 串口UART学习笔记1串口通信

串口通信 

1、串口简介
串行接口,COM接口,只需要两根线就能实现两台设备之间的通信。UART指的是异步的串行接口,通用异步收发。标准常用的是RS-232标准接口 现在电脑上没有串口了,所以使用的是USB转串口芯片,CH340芯片。
换句话说,只需要两根数据线UART_RXD和UART_TXD,就能完成两台设备之间的通信。
2、串口时序
两根数据线各自独立互不影响,二者的时序是相同的。不同之处是UART_RXD是主机MASTER发送给从机SLAVE,UART_TXD是SLAVE发送给MASTER。 由于两根线的时序完全相同且独立,下面以UART_TXD为例。 空闲状态时,UART_TXD一直拉高,当要传输数据之前,拉低一个数据位,此后开始传输数据。数据之后有一个校验位,校验位之后是停止位,停止位之后进入下一个传输周期。至此,完成了一个数据包的传输。
注意:
(1)、传输的数据是从低比特位开始传,比如101010,接受端的接受顺序是010101。
(2)、传输数据的位数是MASTER与SLAVE约定好的,可以是4、5、6、7、8位,时序图中是以八位为例。
(3)、校验位一般是奇偶检验。当然,也可以选择没有检验位,前提是MASTER与SLAVE约定好,在SLAVE解析接收到的数据的时候,不安排校验位的解析。
(4)、停止位,停止位是保证两段传输之间一定要有间隔。两段传输之间可以没有空闲时间,但是,停止位一定要有。
3、时间的问题
从时序图上可以看出,串口的发送和接受是没有时钟的,换句话说,这是一个异步时序。那么如何确定每个位所需要的时间就尤为重要。 这个问题的要点是波特率,每秒发送/接受单位的个数。我们使用的串口是以比特为单位,所以这里波特率与我们的比特率相同。常见的波特率的数值有9600,19200,38400,57600,115200等。以9600为例,表示一秒钟发送/接受9600个比特。所有我们可以计算出单个bit所占用的时间为 1s/9600 = 104166ns。我们传输的起始标志位,传输的数据的每一位,校验位(可有可无),在9600波特率的情况下,各自占据了104166ns的时间。 所以,假设从MASTER发送8'b11001110给SLAVE的的话,数据线的电平变化如下:
所以,FPGA在接收串口数据的时候,按照每个位的时间,设计计数器。假设是50MHz的时钟,那么接受一个bit需要的时钟周期是 104166ns/20ns = 5208个周期,所以在计数器数到5208/2 +1的时候,将UART_RXD的当前值寄存即可。注意接受数据的时候先接受到的是起始位,最后的空闲位置就没有必要接受了。
4、代码 项目要求:电脑通过串口发送八位数据给FPGA,FPGA通过这八位数据来控制八个LED灯的亮暗。波特率9600,无校验位。 代码如下: ``

 1 module uart(
 2     clk        ,
 3     rst_n    ,
 4     rx_uart    ,
 5     led
 6 );
 7 input            clk        ;
 8 input            rst_n    ;
 9 input            rx_uart    ;
10 output reg[7:0] led        ;
11 
12 reg flag_add;
13 //波特率9600 1s/9600 = 104166ns
14 //50M时钟,一个周期是20ns,104166ns/20ns=5208个时钟周期
15 //用计数器来计数时钟周期,每到5208从rx_uart取出一个数据
16 //一个数据包是八个数据+一个起始位数据
17 
18 //计数器cnt0用来计数每个bit的5208个周期
19 reg[12:0] cnt0   ;
20 wire    add_cnt0    ;
21 wire    end_cnt0    ;
22 
23 always @(posedge clk or negedge rst_n)begin
24     if(!rst_n)begin
25         cnt0 <= 0;
26     end
27     else if(add_cnt0)begin
28         if(end_cnt0)
29             cnt0 <= 0;
30         else
31             cnt0 <= cnt0 + 1;
32         end
33     end
34 assign add_cnt0 = flag_add;       
35 assign end_cnt0 = add_cnt0 && cnt0 == 5208 - 1 ;   
36 
37 //计数器1用来计数接受的bit数量
38 reg[3:0] cnt1   ;
39 wire    add_cnt1    ;
40 wire    end_cnt1    ;
41 
42 always @(posedge clk or negedge rst_n)begin
43     if(!rst_n)begin
44         cnt1 <= 0;
45     end
46     else if(add_cnt1)begin
47         if(end_cnt1)
48             cnt1 <= 0;
49         else
50             cnt1 <= cnt1 + 1;
51         end
52     end
53 assign add_cnt1 = end_cnt0;       
54 assign end_cnt1 = add_cnt1 && cnt1 == 9 - 1;   
55 
56 //flag_add的定义,rx_uart下降沿的时候拉高,end_cnt1的时候拉低
57 //需要一个rx_uart边沿检测电路,注意,rx_uart是一个异步信号,与本地时钟无关。
58 //可以把rx_uart放进敏感列表,但是他会被FPGA误认为是个时钟,造成电路不稳定
59 //安全的做法:用本地时钟对rx_uart进行延一拍,然后前后两拍比较完成边沿检测
60 //但是,因为本项目中的rx_uart是个异步信号,可能在时钟边沿变化,
61 //不能满足setup time 和 保持时间 所以就延两拍更安全。(两拍以后的信号是稳定信号)
62 reg rx_uart_ff0;
63 reg rx_uart_ff1;
64 reg rx_uart_ff2;
65 always@(posedge clk or negedge rst_n)
66     if(!rst_n)begin
67         rx_uart_ff0 <= 1;
68         rx_uart_ff1 <= 1;
69         rx_uart_ff2 <= 1;
70     end
71     else begin
72         rx_uart_ff0 <= rx_uart;        
73         //rx_uart_ff0是异步信号打一拍的结果,不能做条件用。以后的可以
74         rx_uart_ff1 <= rx_uart_ff0;
75         rx_uart_ff2 <= rx_uart_ff1;
76     end
77 
78 always@(posedge clk or negedge rst_n)
79     if(!rst_n)begin
80         flag_add <= 0;
81     end
82     else if(!rx_uart_ff1 & rx_uart_ff2) begin//下降沿
83         flag_add <= 1;
84     end
85     else if(end_cnt1)
86         flag_add <= 0;
87         
88 always@(posedge clk or negedge rst_n)
89     if(!rst_n)begin
90         led <= 8'hff;
91     end
92     else if(add_cnt0 && cnt0 == 5208/2-1 && cnt1>0)begin
93         led[cnt1-1] <= rx_uart_ff1;
94     end
95 
96 
97 endmodule

 

posted on 2021-08-13 16:14  xxXxMa  阅读(971)  评论(0编辑  收藏  举报

导航