前言
串口做为最基本的通信方式,掌握串口的收发及其的有必要,本文从以Verilog 语言实现串口通信的收发代码。串口协议讲解
我们常说的串口波特率38400,115200,9600,其实指的就是1s内发送的数据个数,而一帧串口数据包含了(1 起始位)+(8 数据位)+(1 校验位)+(1 停止位), 以115200波特率为例子,1s内发送/接收的字节数就等于 115200 /(1+8+1+1) = 10472字节数,在低速通信中这个效率还是挺可观的。
串口在接收/发送闲时 数据线保持为高电平,当有数据要发送时会先拉低数据线通知对方有数据要接收。紧接着会发送数据的低字节串口发送接收数据都是LSB模式。
在发送完成数据位时,就要发送校验位(可选),最后发送停止位,这样串口的一帧数据就发送完成。
代码实现
代码比较简单这里就不作讲解了。
串口发送
接口源码
`timescale 1ns / 1ps
//
// Company:
/*
PARITY_BIT:
0:无校验位
1:奇校验
2:偶校验
*/
// Engineer: Deam
//
module usart_tx #(
parameter USART_BPS=115200, //波特率
parameter SYS_FRE=50, //单位MHz
parameter PARITY_BIT=0 //校验位
)(
input i_sys_clk,
input i_reset_n,
input i_start,
input [7:0] i_data,
output o_dout,
output o_busy
);
parameter USART_BPS_CNT=SYS_FRE*1000_000/USART_BPS - 1;
/********************检测上升沿*******************************/
reg [1:0]start_f;
always @(posedge i_sys_clk) begin
start_f<={start_f[0],i_start};
end
wire start_flag=(start_f==2'b01);
/********************锁住开始信号*************************/
wire tx_done_flag;
reg start_lock;
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n)
start_lock<=0;
else if(tx_done_flag)
start_lock<=0;
else if(start_flag)
start_lock<=1;
end
/********************开始串口时钟分频**************************/
function integer calc_width(input integer data);
integer i;
begin
i=0;
while(2**i<data)
i=i+1;
calc_width=i-1;
end
endfunction
parameter clk_width=calc_width(USART_BPS_CNT);
reg [clk_width:0]clk_cnt;
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n)
clk_cnt<=0;
else if(start_lock)begin
if(clk_cnt==USART_BPS_CNT)
clk_cnt<=0;
else
clk_cnt<=clk_cnt+1;
end
else
clk_cnt<=0;
end
wire clk_full = (clk_cnt==USART_BPS_CNT) ? 1:0;
/********************开始发送数据**************************/
parameter data_width=(PARITY_BIT==0)?8:9;
reg [data_width:0]data_buf; //防止发送过程中数据改变
reg [3:0]tx_bit_cnt;
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n)begin
tx_bit_cnt<=0;
data_buf<=0;
end
else if(start_flag) begin
if(PARITY_BIT[1] == 0)
data_buf<={^i_data,i_data,1'b0};
else
data_buf<={!(^i_data),i_data,1'b0};
tx_bit_cnt<=1;
end
else if(clk_full)begin
tx_bit_cnt<=tx_bit_cnt+1;
data_buf<=data_buf>>1;
end
end
localparam tx_bit = (PARITY_BIT==0) ? 11:12 ;
assign o_busy=tx_bit_cnt>0&&tx_bit_cnt<tx_bit?1:0;
assign o_dout=((tx_bit_cnt>0)&&(tx_bit_cnt<tx_bit-1))?data_buf[0]:1'b1;
assign tx_done_flag=(tx_bit_cnt==tx_bit-1)&&clk_full;
/********************************************************/
endmodule
仿真源码
`timescale 1ns / 1ps
// Company:
// Engineer: Deam
//
// Module Name: F:/FPGA/2020/Usart/tb_usart_tx.v
// Project Name: Usart
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: Usart_tx
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module tb_usart_tx;
// Inputs
reg i_sys_clk;
reg i_reset_n;
reg i_start;
reg [7:0] i_data;
// Outputs
wire o_dout;
wire o_busy;
parameter PARITY_BIT=0;
parameter USART_BPS=115200;
parameter SYS_FRE=50;
parameter CLK_CNT_F=SYS_FRE*1000_000/USART_BPS-1;
parameter CLK_CNT=CLK_CNT_F*12;
usart_tx #(
.PARITY_BIT(PARITY_BIT),
.SYS_FRE(SYS_FRE),
.USART_BPS(USART_BPS)
)uut(
.i_sys_clk(i_sys_clk),
.i_reset_n(i_reset_n),
.i_start(i_start),
.i_data(i_data),
.o_dout(o_dout),
.o_busy(o_busy)
);
task send_data;
input [7:0] data;
begin
i_data=data;
#1 i_start=1;
#1 i_start=0;
end
endtask
always #0.5 i_sys_clk=~i_sys_clk;
initial begin
i_sys_clk = 0;
i_reset_n = 0;
i_start = 0;
i_data = 0;
#10 i_reset_n=1;
#10 ;
#CLK_CNT send_data(8'h56);
#CLK_CNT send_data(8'h51);
#CLK_CNT send_data(8'h52);
end
endmodule
仿真脚本
# ./当前路径
# ../上一级目录
#退出当前仿真
quit -sim
#清空命令行
.main clear
#创建库
#vlib lib
#vib ./lib/work
#将创建库映射至物理逻辑库
#vmap work .lib/work
#编译文件
vlog -work work ./../usart_tx.v
vlog -work work ./../tb_usart_tx.v
#启动仿真
vsim -voptargs=+acc work.tb_usart_tx
#添加波形
add wave tb_usart_tx/*
#10进制显示波形
#add wave -radix unsigned /tb_usart_rx/o_data
#模拟显示
#add wave -format Analog-Step -height 74 -max 255.0 /tb_usart_rx/o_data
#运行仿真
run 100us
仿真结果
串口接收
在串口接收中,做了数据的三次采样,在实际工程中可能不止3次采样。
接口源码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
/*
PARITY_BIT:
0:无校验位
1:奇校验
2:偶校验
*/
//
module usart_rx #(
parameter USART_BPS=115200, //波特率
parameter SYS_FRE=50, //单位MHz
parameter PARITY_BIT=0 //校验位
)(
input i_sys_clk,
input i_reset_n,
input i_din,
output reg o_rx_err,
output reg o_rx_done,
output reg [7:0] o_data
);
localparam USART_BPS_CNT=SYS_FRE*1000_000/USART_BPS - 1;
reg [3:0]rx_bit_cnt;
/********************检测下降沿*******************************/
reg [1:0]start_f;
always @(posedge i_sys_clk) begin
start_f<={start_f[0],i_din};
end
wire start_flag=(start_f==2'b10);
/********************锁住开始信号*************************/
reg start_lock;
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n)
start_lock<=0;
else if(o_rx_done)
start_lock<=0;
else if(start_flag)
start_lock<=1;
end
/********************开始分频***************************/
parameter clk_width=calc_width(USART_BPS_CNT);
reg [clk_width:0]clk_cnt;
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n)
clk_cnt<=0;
else if(start_lock)begin
if(clk_cnt==USART_BPS_CNT)
clk_cnt<=0;
else
clk_cnt<=clk_cnt+1;
end
else
clk_cnt<=0;
end
wire clk_full = (clk_cnt == USART_BPS_CNT);
wire clk_full_f = (clk_cnt == USART_BPS_CNT-1);
wire clk_full_ff = (clk_cnt == USART_BPS_CNT-2);
wire clk_sampl = (clk_cnt == USART_BPS_CNT>>1 || clk_cnt == USART_BPS_CNT>>2 ||
clk_cnt == (USART_BPS_CNT>>2)*3);
/****************数据多次采样*****************************/
parameter data_width=(PARITY_BIT==0)?9:10;
reg [data_width:0]data_buff;
reg [2:0]rx_buff; //采集3次数据
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n) begin
rx_bit_cnt<=0;
o_rx_done<=0;
rx_buff<=0;
end
else if(rx_bit_cnt>data_width)begin
rx_bit_cnt<=0;
o_rx_done<=1;
rx_buff<=0;
end
else if(clk_full)begin
rx_bit_cnt<=rx_bit_cnt+1;
end
else if(clk_sampl)begin
rx_buff<={rx_buff[1:0],i_din};
end
else
o_rx_done<=0;
end
/*******************数据接收并作处理*****************************/
wire chack_code=(data_buff[data_width-1]== ^data_buff[data_width-2:1]);
always @(posedge i_sys_clk or negedge i_reset_n) begin
if(!i_reset_n) begin
data_buff<=0;
o_data<=8'h00;
o_rx_err<=0;
end
else if(clk_full_ff)begin //提前两个时钟装载数据
if(rx_buff==3'h0)
data_buff<={0,data_buff[data_width:1]};
else //if(rx_buff==3'hf)
data_buff<={1,data_buff[data_width:1]};
end
else if(clk_full_f && rx_bit_cnt==data_width) begin //提前一个时钟计算校验位
if((PARITY_BIT==0) ||(PARITY_BIT==1 && chack_code)||(PARITY_BIT==2 && !chack_code))
o_rx_err<=0;
else
o_rx_err<=1;
end
else if(o_rx_done && !o_rx_err)
o_data<=data_buff[8:1];
end
/********************************************************/
function integer calc_width(input integer data);
integer i;
begin
i=0;
while(2**i<data)
i=i+1;
calc_width=i-1;
end
endfunction
/************************************************/
endmodule
仿真源码
`timescale 1ns / 100ps
// Company:
// Engineer: Deam
//
// Project Name: Usart
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: Usart_rx
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module tb_usart_rx;
// Inputs
reg i_sclk;
reg i_reset_n;
reg i_din;
// Outputs
wire [7:0] o_data;
wire o_done;
wire o_rx_err;
// Instantiate the Unit Under Test (UUT)
parameter PARITY_BIT=1;
parameter USART_BPS=115200;
parameter SYS_FRE=50;
usart_rx #(
.PARITY_BIT(PARITY_BIT),
.SYS_FRE(SYS_FRE),
.USART_BPS(USART_BPS)
)usart_rx(
.i_sys_clk(i_sclk),
.i_reset_n(i_reset_n),
.i_din(i_din),
.o_data(o_data),
.o_rx_done(o_done),
.o_rx_err(o_rx_err)
);
parameter CLK_CNT=SYS_FRE*1000_000/USART_BPS;
task send_data;
input [7:0] data;
integer i,j;
begin
#0 i_din<=0;
for(i=0;i<8;i=i+1)
#CLK_CNT i_din<=data[i];
if(PARITY_BIT==0)begin
#CLK_CNT i_din<=1;
#10 i_din<=1;
end
if(PARITY_BIT==1)begin
#CLK_CNT i_din<=^data;
#CLK_CNT i_din<=1;
#10 i_din<=1;
end
if(PARITY_BIT==2)begin
#CLK_CNT i_din<=!(^data);
#CLK_CNT i_din<=1;
#10 i_din<=1;
end
#CLK_CNT i_din<=1;
end
endtask
always #0.5 i_sclk=~i_sclk;
initial begin
// Initialize Inputs
i_sclk = 0;
i_reset_n = 0;
i_din = 1;
#10 i_reset_n=1;
#200;
repeat (10)
send_data($random);
end
endmodule
仿真脚本
# ./当前路径
# ../上一级目录
#退出当前仿真
quit -sim
#清空命令行
.main clear
#创建库
#vlib lib
#vib ./lib/work
#将创建库映射至物理逻辑库
#vmap work .lib/work
#编译文件
vlog -work work ./../usart_rx.v
vlog -work work ./../tb_usart_rx.v
#启动仿真
vsim -voptargs=+acc work.tb_usart_rx
#添加波形
add wave tb_usart_rx/*
#10进制显示波形
#add wave -radix unsigned /tb_usart_rx/o_data
#模拟显示
add wave -format Analog-Step -height 74 -max 255.0 /tb_usart_rx/o_data
#运行仿真
run 100us