边缘检测工程:串口接收模块代码解析

边缘检测工程:串口接收模块代码解析

  本文为明德扬原创文章,转载请注明出处!

   串口接收模块的功能:接收上位机通过串口发送过来的数据,进行串并转换之后送给下游模块。

  注:串口波特率9600,无奇偶校验位。

一、设计架构
在这里插入图片描述

上图是与上位机通信的串口的时序图。我们从图中可以获取到如下关键信息。

  1. 串口数据线位宽为1bit,默认状态下为高电平。

  2. 每次上游模块发送数据,都是先发送1位的起始位0,然后发送8位的数据,最后是1位的停止位1。

  3. 每1位所占的时间,可以通过波特率来计算。计算方法如下:

    波特率是指1s内发送或接受了多少比特数据,若波特率为9600 bit/s,则1s可以传输9600bit,那么传输1bit的时间为:1/9600(s)。以mp801开发板为例,时钟频率为50M,那么发送1bit就需要5208个时钟周期。

串口接收模块采用两个计数器的架构,这两个计数器分别表示接收1bit需要的时间和共需要接收多少bit。其结构图如下:
在这里插入图片描述
计数器cnt0:对接收1bit数据需要的时间进行计数;接收1bit需要5208个时钟周期。该计数器的计数周期为5208。

  计数器cnt1:对接收多少bit的数据进行计数;停止位不参与,起始位加上数据位共9bit。该计数器的计数周期为9。


  本工程使用了检测信号下降沿的方法,信号下降沿的检测方法:

  检查uart_rx的下降沿,就要用到FPGA里的边沿检测技术。所谓的边沿检测,就是检测输入信号,或者FPGA内部逻辑
  信号 的跳变,即上升沿或者下降沿的检测。就比如前面uart_rx由1变0时,就出现了下降沿,接着一次指令结束,
  uart_rx由0变1时,就出现了上升沿,边沿检测技术这在FPGA电路设计泛。电路图如下:

在这里插入图片描述
对应的信号列表如下图所示:
在这里插入图片描述
中间信号,trigger连到触发器的信号输入端D,触发器的输出器连的是tri_ff0。将trigger取反,与tri_ff0相与,
就得到信号neg_edge,如果neg_edge=1就表示检测到trigger的下降沿。将tri_ff0取反,与trigger相与,就得到
信号pos_edge,如果pos_edge=1,就表示检测到trigger的上升沿。
我们来讲解这个原理,信号的波形图如下:
在这里插入图片描述
Tri_ff0是触发器的输出,因此tri_ff0的信号与trigger信号相似,但是相差了一个时钟周期。我们也可以理解为:
每个时钟上升沿看到的tri_ff0的值,其实就是triffer信号上一个时钟看到的值,也就是tri_ff0是trigger之前的值。

我们在看第3时钟上升沿,此时trigger值为0,而tri_ff0的值为1,即当前trigger的值为0,之前的值为1,这就是下降沿,此时neg_edge为1。当看到neg_edge为1,就表示检测到trigger的下降沿了。同理在第7个时钟上升沿,看到trigger值为1,而之前值为0,pos_edge为1,表示检测到trigger的上升沿。

  本模块输入信号uart_rx是异步信号,异步信号都需要经验同步化后,才能够使用。异步信号同步化如下。

在前面讨论边沿检测的波形中,我们把trigger当做理想的同步信号来考虑,也就是trigger满足D触发器的建立和保持时间,在同步系统中实现边沿检测不是问题。但如果trigger不是理想的同步信号,例如外部按键信号,以及本工程的uart_rx信号。这些信号什么时候产生变化,是外部传输指令给FPGA,对于FPGA来说完全是随机的。很有可能出现,信号在时钟上升沿产生变化的情况,从而无法满足触发器的建立时间和保持时间要求,出现亚稳态的情况,从而导致系统崩溃。如下图,我们先将信号用2个触发器进行了寄存,确定了信号的稳定性,然后再进行边沿检测,达成了同步系统中实现边沿检测的需求。
在这里插入图片描述
那么边沿检测的代码设计就需要先进行触发器的设计,假设输入的信号trigger不是同步信号,要将该信号用2个
触发器进行寄存,得到tri_ff0和tri_ff1。需要特别注意的是,在第一个触发器阶段,信号依旧有亚稳态的情况,
因此tri_ff0绝对不可以拿来当条件使用,只能使用tri_ff1。接着进行检测边沿,根据前面所说,得出同步信号后
再用 寄存器寄存,得到tri_ff2。根据tri_ff1和tri_ff2,我们就可以得到边沿检测结果。当tri_ff11且
tri_ff2
0时,上升沿的pos_edge有效;当tri_ff10且tri_ff21时,下降沿的neg_edge有效,代码如下:
1
2  
3
4     always @(posedgeclk or negedgerst_n)begin
5        if(rst_n1’b0)begin
6         tri_ff0 <= 0;
7         tri_ff1 <= 0;
8         tri_ff2 <= 0;
9       end
10       else begin
11        tri_ff0 <= trigger ;
12        tri_ff1 <= tri_ff0 ;
13        tri_ff2 <= tri_ff1 ;
14       end
15     end
16     [size=9.0000pt]
17     assign neg_edge = tri_ff1
0 && tri_ff21;
18     assign pos_edge = tri_ff1
1 && tri_ff2==0;
19
1

     综上所述,如果进来的信号是异步信号,那么就需要先进行同步化,再做检测,即通过打两拍的方式,实现了信号的
     同步化;再通过打一拍的方式,实现边沿检测电路。反之,如果进来的信号本身就是同步信号,那就没有必要做
     同步化了,可以直接做边沿检测。

二、信号的意义

信号            类型           意义
clk            输入信号        时钟信号。
rst_n           输入信号        复位信号,低电平有效。

uart_rx          输入信号    串口接收数据线,位宽为1bit,空闲时为高电平,开始
                     收时,会变为低电平,持续1/9600(s),然后是数据、停止
                     位等。
rx_data          输出信号    从串口中接收到的1字节数据。

rx_vld           输出信号   串口接收数据有效指示信号。当其为高电平时,对应的输出
                     rx_data有效,表示接收到1个字节的数据。注意,1个时钟的
                     高电平表示接收到1个字节数据。
uart_ff0          内部信号    输入进来的uart_rx信号寄存一拍后的信号。
                     该信号的目的是为了做时序同步。
uart_ff1          内部信号    对uart_ff0寄存一拍后的信号。
                     该信号的目的是为了做时序同步。该信号就是同步化后的,
                     可以使用的信号。 
uart_ff2          内部信号    对uart_ff1寄存一拍的信号。
                     该信号的目的,是与uart_ff1配合,检测uart_ff1的下降沿。
                     
flag_add          内部信号    模块处于接收数据状态的指示信号,当其为1时,表示正在
                     接收数据;为0时,表示空闲状态。
                     产生逻辑是:当检测到uart_ff1的下降沿时变高,当接收完整
                     个字节数据时(计数器cnt1数完了)就变低。
cnt0            内部信号   对每输入1bit数据的时间进行计数,接收1bit需要5208个时钟
                     周期。该计数器的计数周期为5208。
add_cnt0          内部信号   计数器cnt0计数有效信号。当处于处于接收状态时,该信号
                     有效。
end_cnt0          内部信号   计数器cnt0的结束条件。接收1bit需要5208个时钟周期,所以
                     数到5208个就结束。
cnt1            内部信号   对接收多少bit的数据进行计数,停止位不参与,起始位加上
                     数据位共9bit。该计数器的计数周期为9。
add_cnt1           内部信号   计数器cnt1的加一条件。接收完1位(end_cnt0),就有效。
end_cnt1          内部信号   计数器cnt1的结束条件,共接收9bit数据,所以数到9个就结
                     束。
add_en           内部信号   uart_ff1下降沿有效指示信号。
                     当检测到接收的数据前一时刻uart_ff2为高电平,
                     当前时刻uart_ff1为低电平时,就有效。

三、参考代码

 下面展出本模块的设计,欢迎进一步交流,如果需要整个项目源代码,欢迎与明德扬联系。

1       module uart_rx(
2          clk ,
3          rst_n ,
4          uart_rx ,
5          rx_vld ,
6          rx_data
7          );
8       [size=9.0000pt]
9
10         parameter CNT_BTL = 20’d2604;
11         parameter CNT_MID = 20’d1302;
12         parameter CNT_RX = 4’d9;
13         parameter DATA_W = 8;
14      [size=9.0000pt]
15         input clk ;
16         input rst_n ;
17         input uart_rx ;
18      [size=9.0000pt]
19         wire uart_rx ;
20         output[DATA_W-1:0] rx_data ;
21         output rx_vld ;
22
23      [size=9.0000pt]
24         reg [DATA_W-1:0] rx_data ;
25         reg rx_vld ;
26      [size=9.0000pt]
27         reg uart_rx_ff0 ;
28         reg uart_rx_ff1 ;
29         reg uart_rx_ff2 ;
30         reg flag_add_add ;
31         reg [19:0] cnt0 ;
32         wire add_cnt0 ;
33         wire end_cnt0 ;
34      [size=9.0000pt]
35         reg [3:0] cnt1 ;
36         wire add_cnt1 ;
37         wire end_cnt1 ;
38         wire add_en ;
39      [size=9.0000pt]
40         always @(posedge clk or negedge rst_n)begin
41           if(!rst_n)begin
42             cnt0 <= 0;
43           end
44           else if(add_cnt0)begin
45             if(end_cnt0)
46               cnt0 <= 0;
47             else
48               cnt0 <= cnt0 + 1;
49           end
50         end
51      [size=9.0000pt]
52         assign add_cnt0 = flag_add[size=9.0000pt];
53         assign end_cnt0 = add_cnt0 && cnt0== CNT_BTL-1;
54      [size=9.0000pt]
55         always @(posedge clk or negedge rst_n)begin
56           if(!rst_n)begin
57             cnt1 <= 0;
58           end
59           else if(add_cnt1)begin
60             if(end_cnt1)
61               cnt1 <= 0;
62             else
63               cnt1 <= cnt1 + 1;
64           end
65         end
66      [size=9.0000pt]
67        assign add_cnt1 = end_cnt0;
68        assign end_cnt1 = add_cnt1 && cnt1== CNT_RX-1;
69      [size=9.0000pt]
70        always @(posedge clk or negedge rst_n)begin
71          if(rst_n1’b0)begin
72            uart_rx_ff0 <= 1’b1;
73            uart_rx_ff1 <= 1’b1;
74            uart_rx_ff2 <= 1’b1;
75          end
76          else begin
77            uart_rx_ff0 <= uart_rx;
78            uart_rx_ff1 <= uart_rx_ff0;
79            uart_rx_ff2 <= uart_rx_ff1;
80          end
81        end
82        assign add_en = uart_rx_ff2&&~uart_rx_ff1;
83
84        always @(posedge clk or negedge rst_n)begin
85          if(rst_n
1’b0)begin
86            flag_add <= 1’b0;
87          end
88          else if(add_en)begin
89            flag_add <= 1’b1;
90          end
91          else if(end_cnt1)begin
92            flag_add <= 1’b0;
93          end
94        end
95
96        always @(posedge clk or negedge rst_n)begin
97          if(rst_n1’b0)begin
98            rx_vld <= 1’b0;
99          end
100         else if(end_cnt1)begin
101           rx_vld <= 1’b1;
102         end
103         else begin
104           rx_vld <= 1’b0;
105         end
106       end
107
108       always @(posedge clk or negedge rst_n)begin
109         if(rst_n
1’b0)begin
110           rx_data <= 8’b0;
111         end
112         else if(cnt1!=0&&add_cnt0&&cnt0==CNT_MID-1)begin
113           rx_data[cnt1-1] <= uart_rx_r2;
114         end
115       end
116
117     endmodule

   以上就是基于FPGA的边缘检测工程串口接收模块的代码分享,关注明德扬获取边缘检测工程完整的源代码!
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值