FPGA -- 串口的实现(超级灵活,超级好用)

前言

串口做为最基本的通信方式,掌握串口的收发及其的有必要,本文从以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
仿真结果

在这里插入图片描述

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值