之前的串口用得有些不稳定所以弄了这个串口,这个串口适用于各种常见得波特率设置,这里不讲解串口原理,直接代码分享和输入输出讲解。
一、代码
1.UART_RX
`timescale 1ns / 1ps
module uart_rx
#(parameter BPS =5208)
(
input clk ,
input rst_n ,
input din ,
output reg[7:0] dout /* synthesis syn_keep=1 */,
output reg dout_vld
);
//parameter BPS = 434;//115200 5208; //9600波特率
reg [14:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ 3:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg rx0 ;
reg rx1 ;
reg rx2 ;
wire rx_en/* synthesis syn_keep=1 */;
reg flag_add ;
//对数据的跨时钟处理,防止出现亚稳态
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
rx0 <= 1'b1;
rx1 <= 1'b1;
rx2 <= 1'b1;
end
else begin
rx0 <= din;
rx1 <= rx0;
rx2 <= rx1;
end
end
assign rx_en = rx2 & ~rx1;
//检测到下降沿,即空闲位从1置为0,数据传输开始
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 15'd0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 15'd0;
else
cnt0 <= cnt0 + 15'd1;
end
end
assign add_cnt0 = flag_add;
assign end_cnt0 = add_cnt0 && cnt0==(BPS-15'd1);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 4'd0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 4'd0;
else
cnt1 <= cnt1 + 4'd1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==4'd9-4'd1 ; //由于是接收程序,此处也不设校验位,所以只需要接收数据就可以,后面的第10位必然位停止位,可以不理,节省资源
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
flag_add <= 1'b0;
end
else if(rx_en && flag_add==1'b0) begin
flag_add <= 1'b1;
end
else if(end_cnt1) begin
flag_add <= 1'b0;
end
end
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
dout <= 8'd0;
end
else if(add_cnt0 && cnt0==(BPS/2-1) && cnt1!=0) begin //在中间时刻采样,此时的数据比较稳定,从低位到高位依次采样
case(cnt1)
4'd1:dout[0]<=rx2;
4'd2:dout[1]<=rx2;
4'd3:dout[2]<=rx2;
4'd4:dout[3]<=rx2;
4'd5:dout[4]<=rx2;
4'd6:dout[5]<=rx2;
4'd7:dout[6]<=rx2;
4'd8:dout[7]<=rx2;
default:
;
endcase
end
else
;
end
//传输完数据信号
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
dout_vld <= 1'b0;
end
else if(end_cnt1) begin
dout_vld <= 1'b1;
end
else begin
dout_vld <= 1'b0;
end
end
endmodule
模块接口说明
uart_rx
引脚 | 位宽 | 方向 | 说明 |
---|---|---|---|
clk | 1 | IN | 模块时钟50M |
rst_n | 1 | IN | 复位信号低位有效 |
din | 1 | IN | RX数据输入 |
dout | 8 | OUT | 数据输出 |
dout_vld | 1 | OUT | 数据输出有效 |
2.UART_TX
下面展示一些 内联代码片
。
`timescale 1ns / 1ps
module uart_tx
#(parameter BPS =5208)
(
input clk ,
input rst_n ,
input [7:0] din ,
input din_vld,
output reg rdy ,
output byte_1 ,
output reg dout
);
//parameter BPS = 434;
reg [7:0] tx_data_tmp;
reg flag_add ;
reg [14:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ 3:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
wire [ 9:0] data ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_add <= 1'b0;
end
else if(flag_add==1'b0 && din_vld)begin
flag_add <= 1'b1;
end
else if(end_cnt1)begin
flag_add <= 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 15'd0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 15'd0;
else
cnt0 <= cnt0 + 15'd1;
end
end
assign add_cnt0 = flag_add;
assign end_cnt0 = add_cnt0 && cnt0==(BPS-15'd1);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 4'd0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 4'd0;
else
cnt1 <= cnt1 + 4'd1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==4'd10-4'd1 ;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_data_tmp <=8'd0;
end
else if(flag_add==1'b0 && din_vld) begin
tx_data_tmp <= din;
end
end
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
dout <= 1'b1;
end
else if(add_cnt0 && cnt0==1-1)begin
dout<= data[cnt1];
end
end
assign data = {1'b1,tx_data_tmp,1'b0}; //传输时是从低到高 data = {停止位,数据[7],数据[6] ~ 数据[0],起始位};
always @( * )begin
if(din_vld || flag_add)
rdy = 1'b0;
else
rdy = 1'b1;
end
assign byte_1 = end_cnt1;
endmodule
模块接口说明
uart_tx
引脚 | 位宽 | 方向 | 说明 |
---|---|---|---|
clk | 1 | IN | 模块时钟50M |
rst_n | 1 | IN | 复位信号低位有效 |
din | 8 | IN | 发送数据输入 |
din_vld | 1 | IN | 发送数据输入有效 |
rdy | 1 | OUT | busy信号 1:不忙 0:忙 |
byte_1 | 1 | OUT | 一字节传输完成标志 |
dout | 1 | OUT | TX数据输出 |
二、总结注意
波特率分频系数设置
parameter BPS =5208
常用波特率 50M时钟分频系数
BAUD_9600 5208
BAUD_19200 2604
BAUD_38400 1302
BAUD_115200 434
busy信号注意
always @( * )begin
if(din_vld || flag_add)
rdy = 1'b0;
else
rdy = 1'b1;
end
该处使用组合逻辑将数据有效赋值给busy信号忙状态,建议使用时序逻辑给数据输入有效赋值,或使用组合逻辑试将代码这里的有效位移除。