基于FPGA自适应串口通信(Auto Baud Rate)

做的课设,相当于复习了一遍verilog。


实现了

1.接收端固定模式:8N1 BAUD:921600。

2.发送端8N1,任意波特率(不取极端值)。

3.数码管显示波特率(16进制)。

 

用了

1.两天一夜。

2.ego1平台+usb2uart。


整体框图

输入tx信号经过频率捕获层获得100Mhz下的计数值,送给波特率产生层得到接收层波特率。

接收层接收数据送给串口FIFO层,串口FIFO层根据发送层的反馈将数据送出FIFO。

发送层将数据按固定921600波特率发给PC机。从而实现波特率交换。


频率捕获层算法

1.uart协议

这里只考虑最常用的8N1,即8位数据,无校验,1位停止位。

在时钟线上升沿,tx线下降沿并维持到下一个上升沿即为一个开始位。然后8位数据(低位在前)。然后到第10位停止位高电平。

值得一提的是,tx线不会在某个电平完后恢复初始电平,比如说上一个数据位是低,当前位还是低,这个数据位结束后不会变回高再变低。

有什么影响?倘若tx发送0xff,那么其实只有一个低电平脉宽!即起始位那个脉宽。

2.算法

 先打两拍,同步时钟以及滤除尖峰脉冲。考虑到输入的数据信号大多为ascii码。

我们从里面找一个最特殊的,U,这个字符二进制为10101010,再加上开始结束位的tx线电平变化为:

0101010101,这里有4个或者说5个低电平脉宽,这是最理想的情况。这样测出来的脉宽都是波特率。

但是如果是一般的字符,发送一个字节可能获得多个低电平脉宽,从里面找最小的有极大可能就是波特率(极小可能最小的低电平脉宽是波特率的倍数)。

所以我们的算法就是多次测频率,从里面找一个最小的,即认为是波特率。注意这里的频率,波特率都是相对于ego1的100Mhz时钟,并不需要化为实际波特率。

程序

 1 `timescale 1ns / 1ps
 2 
 3 
 4 module cap_freqence(
 5             input        clk,
 6             input        rst_n,
 7             input         freq_in,
 8             input         cap_en,
 9             output     freq_cap//27bit 0-134,217,727 对输入信号波特率测得的频率            
10     );
11 reg[26:0]  freq_cap = 14'd10416;    //rst ??????
12 reg[26:0] tmp_cap,sequence_cap[2:0];    //cap 5 times and Use the smallest one as baud
13 reg [2:0] cap_cnt;
14 // reg cap_finish;
15 reg s0_RS_232_RX,s1_RS_232_RX;        //同步寄存器,消除亚稳态
16 reg tmp0_RS_232_RX,tmp1_RS_232_RX;    //数据寄存器,移位寄存器
17 reg cap_state;                        //0:idle 1:work
18 wire neg_RX;//下降沿
19 always@(posedge clk or negedge rst_n)//同步寄存器,消除亚稳态
20     if(!rst_n)
21         begin
22             s0_RS_232_RX <= 1'b0;
23             s1_RS_232_RX <= 1'b0;    
24         end
25     else 
26         begin
27             s0_RS_232_RX <= freq_in;   //次态
28             s1_RS_232_RX <= s0_RS_232_RX;//现态
29         end
30 always@(posedge clk or negedge rst_n)//数据寄存器
31     if(!rst_n)
32         begin
33             tmp0_RS_232_RX <= 1'b0;
34             tmp1_RS_232_RX <= 1'b0;    
35         end
36     else 
37         begin
38             tmp0_RS_232_RX <= s1_RS_232_RX;
39             tmp1_RS_232_RX <= tmp0_RS_232_RX;    
40         end
41 //共打两拍再检测输入脚
42 assign neg_RX = (!tmp0_RS_232_RX) & tmp1_RS_232_RX;//下降沿检测
43 
44 always@(posedge clk or negedge rst_n)//cap工作状态
45     if(!rst_n)begin
46         cap_state <= 0;
47         end
48     else if(neg_RX)                    //下降沿开始测频
49         cap_state <= 1; 
50     else if (tmp0_RS_232_RX)        //高电平退出测频
51         cap_state <= 0; 
52 
53 always@(posedge clk or negedge rst_n)//以100Mhz时钟测输入引脚低电平脉宽
54     if(!rst_n)
55         begin
56             tmp_cap <= 27'd0;
57             
58         end
59     else if(cap_state)                //串口工作状态(接受)时计数
60         begin
61             if(tmp0_RS_232_RX)        //计完一次频率
62                 begin
63                    sequence_cap[cap_cnt] <= tmp_cap;
64                    tmp_cap <= 27'd0;
65                    cap_cnt <= cap_cnt + 1;//0 1 2 3,4测完4次后为4
66                 end
67             else
68                tmp_cap <= tmp_cap + 1'b1; 
69         end
70         
71 integer i;
72 reg[26:0] min_cap;
73 
74 always@(cap_cnt)                    //测完一个序列便从测频序列里找出最小脉宽即为bps
75     begin
76         if (cap_cnt == 2'd5)
77             begin
78                 min_cap=sequence_cap[0];//1st one as minimal cap
79                 for (i=1;i<5;i=i+1)
80                     if (sequence_cap[i] < min_cap)
81                         if (sequence_cap[i] > 10)
82                             min_cap = sequence_cap[i];
83                 if (cap_en)            //手动控制更新baud
84                     freq_cap = min_cap;
85             end
86     end
87     
88 endmodule

这个程序有瑕疵,用了初始化。不懂

freq_cap
这个变量放在rst里面复位后,之后都不能被下面
freq_cap = min_cap;
这句更新,暂时这么写吧!


效果

猜你喜欢

转载自www.cnblogs.com/katachi/p/10962505.html