基于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;
这句更新,暂时这么写吧!
效果
路漫漫其修远兮,吾将上下而求索