基于FPGA的UART回环设计
基于FPGA的UART回环设计
文章目录
- 基于FPGA的UART回环设计
- 一. UART介绍
- 二. 协议原理
- 三. rx接收设计
- 1. 计数器设计
- 2. 数据接收
- 四. tx发送设计
- 五. 实现效果
- 六. 主要代码
一. UART介绍
UART(Universal Asynchronous Receiver/Transmitter),即通用异步收发传输器,它是一种通用的串行、异步通信总线,有两条数据线,可以实现全双工的发送与接收。
UART因为是异步通信,所以不包括同步信号中的时钟线,其两条数据数据线TX和rRX分别用来发送和接收数据。UART负责处理串行端口与发送接收数据总线见的串并转换,并规定了数据包格式,TX和RX采用相同的数据包格式和波特率就可以进行数据传输。
二. 协议原理
发送端UART将要发送的数据组装成一个数据包通过TX端口串行将数据发送出去,接收端UART通过其RX端口串行对数据包的数据进行采集,从数据包中恢复出发送端发送的数据。
UART的数据包包括1个开始位、5到9位数据(取决于UART设置),1个奇偶校验位,1或者2个停止位。
开始位:UART数据传输线空闲时保持高电平,开始发送数据时,发送端UART将数据线从高拉到低一个时钟周期(其长短根据波特率决定)。当接收端UART探测到下降沿时,开始根据波特率读取数据。
数据位:数据位中为要传输的真是数据,如果使用奇偶校验位,数据帧中可包含5到8位数据,如果不适用奇偶校验位的话,数据帧中最长则可包含9位数据。
奇偶校验位:奇偶校验位用于在接收端UART来显示传输过程中是否有数据被改变。在传输过程中,位数据可能因为电磁辐射、不匹配的波特率、或者长距离传输等原因被改变。当接收UART读取完数据帧后,计算其中1的个数,并且检查1的总个数是奇数还是偶数。如果奇偶校验位是0,则数据帧中1的个数应该是偶数个,如果奇偶校验位是1,则数据帧中1的个数则应该是奇数个。当奇偶校验位的值与数据位帧相匹配时,则UART认为传输中没有错误(具有局限)。但是如果奇偶校验位是0,数据帧的1的总个数是奇数;或者奇偶校验位是1,数据帧中1的共个数是偶数,则UART则会知道帧数据已经被改变。
停止位:为了表示传输包的结束,发送端UART将数据传输线从低电平拉到高电平至少1到2个数据位宽。
前面已经对数据包/数据发送时序进行了介绍,下面在对波特率以及与其容易混淆的比特率的基础知识进行补充。
波特率定义是:每秒钟传送的符号(码元)数量,单位波特(Baud、B,即symbol/s)。
比特率定义是:每秒钟传送的位(比特)数量,基本单位是比特每秒(bps,bit per second)。.
从两者的定义可以看出要想知道波特率和比特率的关系,就首先的弄清楚码元数量和比特数量的关系。此处引用参考资料2中的解释:把通信系统中码元类比为公共交通车辆,例如公交车、地铁、的士……。通信系统所传输的比特数量类比为出行的人,则比特率为出行人口流动速度,波特率就是发车率。就像例子中提及的公交车、地铁、的士可以搭乘不同数量的出行人员一样,不同码元也可以用不同位数的比特表示。码元所需要的比特位数,由码元支持的状态数量确定。
UART中每个码元就是一位数据,存在两种状态,也即波特率单位变为每秒位数,与比特率定义相同,可以理解为此处的波特率就是比特率,本次采用的波特率为9600bps、14400bps、38400bps、115200bps。
三. rx接收设计
本次采用可调节波特率设置
always @(*) begincase(sw)2'b00: baud = 5208; //9600波特率2'b01: baud = 3472; //14400波特率2'b10: baud = 1302; //38400波特率2'b11: baud = 434; //115200波特率default: baud = 5208; endcase
end
当sw开关拨动到不同位置时,可设置不同的波特率
1. 计数器设计
波特率计数器设计,为简单的基础计数器,最大数为可设置的调节数
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt <= 0;end else if(add_cnt)beginif(end_cnt)begincnt <= 0;endelse begincnt <= cnt + 1;endendelse begincnt <= cnt;end
endassign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));always @(posedge clk or negedge rst_n)beginif(!rst_n)beginadd_cnt <= 1'b0;endelse if(rx_start)beginadd_cnt <= 1'b1;endelse if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))beginadd_cnt <= 1'b0;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_r <= 2'b00;endelse beginrx_r <= {rx_r[0],rx};end
endassign rx_start = ~rx_r [0] & rx_r [1];
其中,add_cnt为波特率计数器开始计数的标志,当输入的数据起始位到来,即检测其下降沿rx_r,波特率计数器开始计数。
end_cnt为波特率计数器结束的标志,当计到最大的波特率时停止,或者在10bit即停止位时(本设计由于硬件问题,停止位为半个波特率的周期),计数器停止计数。
比特计数器设计
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt ;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;
开始计数标志为波特率计数器结束,当计到最大bit数,即11个bit时停止。
2. 数据接收
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data_r <= 11'b11111111111;endelse if(cnt == (baud - 1)>>2)beginrx_data_r[cnt_bit] <= rx; end
end
由于uart收发数据为串行数据,这里要将串行数据转为并行数据,设置数据寄存器rx_data_r,按照bit计数器将数据存入寄存器
assign rx_check = ~^ rx_data_r[8 :1];
奇偶校验位处理,本设计位奇校验rx_check为第十位奇偶校验位,对前8bit数据位进行按位异或运算,所得结果为0则代表数据位有偶数个,1则代表为奇数个,但本设计为奇校验,对所得数再取反,才使得所有数据加校验位为奇数个1。
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data <= 8'hff;endelse if((cnt == (baud - 1)>>1)&&cnt_bit == 4'd9)beginif((rx_check == rx_data_r[9]))beginrx_data <= rx_data_r[8:1];endelse beginrx_data <=8'hff;endend
end
将数据位的8bit数据存入寄存器,方便传出和后续使用。
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_done <= 1'b0;endelse if(end_cnt_bit)beginrx_done <= 1'b1;endelse beginrx_done <= 1'b0;end
end
rx停止信号,方便tx发数据以及后续的处理。
四. tx发送设计
波特率计数器与bit计数器与rx相同,这里不再赘述。
always @(posedge clk or negedge rst_n)beginif(!rst_n)begindata<=11'b11111111111;endelse if(tx_start)begindata<={1'b1,tx_check,tx_data,1'b0}; endelse begindata<=data;end
end
发出数据的处理,uart为低位先发,先发0起始位,后接所发的数据1-8bit的tx_data为rx数据寄存器传入的数据,后接校验位tx_check(与rx中同理,对tx_data按位取异或再取反),后接停止位1.
always @(posedge clk or negedge rst_n)beginif(!rst_n)begintx<=1'b1;endelse if(end_cnt)begintx<=data[cnt_bit];endelse if(end_cnt_bit)begintx<=1'b1;endelse begintx<=tx;end
end
uart数据为串行数据,将缓存的data数据挨个发出。
五. 实现效果
电脑向FPGA发送hello,FPGA接收到并返回电脑hello
sw[0]与sw[1]都为1,同时led[2]与led[3]亮起,对应波特率为115200,数据传输正确。
sw[0]为0,sw[1]为1,led[2]量led[3]灭,对应波特率为38400,
数据传输正确。
电脑向FPGA发送“开”,led[0]亮起
发送关led[0]熄灭。
六. 主要代码
uart_rx.v
module uart_rx (input clk,input rst_n,input rx,input [1:0] sw,output reg [7:0] rx_data,output reg rx_done
);wire rx_check;//比特率计数器
reg [12:0] cnt;
reg add_cnt;
wire end_cnt; //bit计数器
reg [3:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;//下降沿检测
reg [1:0] rx_r;
wire rx_start;reg [10:0] rx_data_r;reg [12:0] baud;always @(*) begincase(sw)2'b00: baud = 5208; //9600波特率2'b01: baud = 3472; //14400波特率2'b10: baud = 1302; //38400波特率2'b11: baud = 434; //115200波特率default: baud = 5208; endcase
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt <= 0;end else if(add_cnt)beginif(end_cnt)begincnt <= 0;endelse begincnt <= cnt + 1;endendelse begincnt <= cnt;end
endassign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));always @(posedge clk or negedge rst_n)beginif(!rst_n)beginadd_cnt <= 1'b0;endelse if(rx_start)beginadd_cnt <= 1'b1;endelse if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))beginadd_cnt <= 1'b0;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_r <= 2'b00;endelse beginrx_r <= {rx_r[0],rx};end
endassign rx_start = ~rx_r [0] & rx_r [1];always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt ;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data_r <= 11'b11111111111;endelse if(cnt == (baud - 1)>>2)beginrx_data_r[cnt_bit] <= rx; end
endassign rx_check = ~^ rx_data_r[8 :1];always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data <= 8'hff;endelse if((cnt == (baud - 1)>>1)&&cnt_bit == 4'd9)beginif((rx_check == rx_data_r[9]))beginrx_data <= rx_data_r[8:1];endelse beginrx_data <=8'hff;endend
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_done <= 1'b0;endelse if(end_cnt_bit)beginrx_done <= 1'b1;endelse beginrx_done <= 1'b0;end
end
endmodule
uart_tx.v
module uart_tx (input clk,input rst_n,input [7:0] tx_data,input tx_start,input [1:0] sw,output reg tx,output tx_done
);
//奇偶校验位
wire tx_check;
//波特率计数器参数
reg [12:0] cnt;
reg add_cnt;
wire end_cnt;//bit计数器参数
reg [3:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;reg [12:0] baud;
reg [10:0] data;//数据寄存器always @(*) begincase(sw[1:0])2'b00: baud = 5208; //9600波特率2'b01: baud = 3472; //14400波特率2'b10: baud = 1302; //38400波特率2'b11: baud = 434; //115200波特率default: baud = 5208; endcase
end//波特率计数器
always @(posedge clk or negedge rst_n) begin if(!rst_n)begincnt <= 0;end else if(add_cnt)beginif(end_cnt)begincnt <= 0;endelse begincnt <= cnt + 1;endendelse begincnt <= cnt;end
endassign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));always @(posedge clk or negedge rst_n)beginif(!rst_n)beginadd_cnt <= 1'b0;endelse if(tx_start)beginadd_cnt <= 1'b1;endelse if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))beginadd_cnt <= 1'b0;end
end//bit计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;assign tx_check = ~^tx_data;always @(posedge clk or negedge rst_n)beginif(!rst_n)begindata<=11'b11111111111;endelse if(tx_start)begindata<={1'b1,tx_check,tx_data,1'b0}; //数据加启示位endelse begindata<=data;end
endalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintx<=1'b1;endelse if(end_cnt)begintx<=data[cnt_bit];endelse if(end_cnt_bit)begintx<=1'b1;endelse begintx<=tx;end
endassign tx_done = end_cnt_bit;
endmodule
led.v
module led (input clk,input rst_n,input [1:0] sw,input [7:0] data,output reg [2:0] led
);
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginled[1:0] <= 2'b00;endelse begincase(sw)2'b00: led[1:0] <= 2'b00;2'b10: led[1:0] <= 2'b01;2'b01: led[1:0] <= 2'b10;2'b11: led[1:0] <= 2'b11;default: led[1:0] <= 2'b00;endcaseendendalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginled[2] <= 1'b0;endelse if(data == 8'h80)beginled[2] <= 1'b1;endelse if(data == 8'hb3)beginled[2] <= 1'b0;endelse beginled[2] <= led[2];endendendmodule
uart_loop.v
module uart_loop (input clk,input rst_n,input rx,input [1:0] sw,output [2:0] led,output tx
);
wire [7:0] data;
wire tx_done;uart_rx inst_uart_rx (.clk(clk),.rst_n(rst_n),.rx(rx),.sw(sw),.rx_data(data),.rx_done(rx_done)
);uart_tx inst_uart_tx (.clk(clk),.rst_n(rst_n),.tx_data(data),.tx_start(rx_done),.sw(sw),.tx(tx),.tx_done(tx_done)
);
led inst_led (.clk(clk),.rst_n(rst_n),.sw(sw),.data(data),.led(led)
);endmodule