串口通信广义的概念有很多,我们这里主要讲RS232这一类型,首先RS232使用的电平是负逻辑电平,所以首先必须采取电平转换芯片,一般采用MAX3232这种芯片,具体的硬件连接图如图所示
:
再来看串口传输的协议格式:
另外讲解一些串口传输的基础知识:
1.波特率:每秒传输码元符号的个数,假设每秒传输1200个码元,然后每个码元由8bit组成,那么波特率就是1200*8=9600bps,所以我们一般定义的波特率有9600波特,19200波特之类的。本文中我们采用9600波特率,它代表每秒传输9600bit,所以每bit大概传输时间为1000000000/9600=104166ns,按照我们的频率50M来计算,那么传输一个bit的时间大概需要5208这么多个时钟周期,这里我们利用上升沿接收数据,即(当计数记到2603的时候,我们接收数据),这里可用计数器实现。具体代码如下:
module posedge_bps(
clk,
rst_n,
bps_start,
clk_bps_posedge_data
);
input clk;
input rst_n;
input bps_start;
output reg clk_bps_posedge_data;
reg [12:0] bps_posedge_cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
bps_posedge_cnt<=13'd0;
clk_bps_posedge_data<=0;
end
else
///当检测到bps_start为低电平或者bps_podedge_cnt==5207(即还没有开始传输初始状态,或者计数计满了的时候清零),bps_start由串口数据帧格式控制,当检测到数据位由1变为0的时候表示,bps_start启动了。 当计数记到2603,即一半的时候,正好在数据位的中间,那么就将这几个数据采下来。
begin
if((!bps_start)||(bps_posedge_cnt==13'd5207))
begin
bps_posedge_cnt<=13'd0;
clk_bps_posedge_data<=0;
end
else
begin
bps_posedge_cnt<=bps_posedge_cnt+1;
if(bps_posedge_cnt==13'd2603)
clk_bps_posedge_data<=1;
else
clk_bps_posedge_data<=0;
end
end
endmodule
2.接收数据代码如下:
从串口传输数据帧格式中我们可以看到,当检测到数据位由1变为0的时候(脉冲边沿检测可以搞定),这就是数据开始传输的标志,第一位是起始位,在数据中间采样点的时候(2603),我们不用采下来,后面紧接着8位数据位,我们可以采,然后结束位,不管。在采数据的过程中,我们可以设置一个标志位(receiver_data_flag),再采数据过程中一直保持高点平,当数据接收完毕的时候,将它拉低,所以在发送数据的时候,一旦检测到这个标志位由高变低,则表示接收数据结束,可以发送数据了。
module uart_receiver(
//////////////global input interface
clk,
rst_n,
/////////////Uart input interface
clk_bps_posedge,
uart_data_in,
////////////Uart output interface
receiver_data,
receiver_data_start_flag,
bps_start
);
input clk;
input rst_n;
input clk_bps_posedge;
input uart_data_in ;
output reg [7:0] receiver_data;
output receiver_data_start_flag;
output bps_start;
//////////////////////////闂佸憡鑹鹃張顒勵敆閻愮儤鐓傚┑鐘插暞閺夊綊鏌ㄥ☉妯垮缂佽鲸鎸搁~銏ゅΨ閵夘喗些婵
/*reg rst_n1,rst_n2;
wire true_rst_n;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
rst_n1<=0;
rst_n2<=0;
end
else
begin
rst_n1<=1;
rst_n2<=rst_n1;
end
assign true_rst_n=rst_n2;
*/
/////////////////////////////////////////////////闂佽桨鑳舵晶妤€鐣垫担鍦枖閻庯綆鍠楅鏍ㄧ箾鐏炴儳顕滄い銉ユ噹闇
reg uart_data_in1,uart_data_in2;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
uart_data_in1<=0;
uart_data_in2<=0;
end
else
begin
uart_data_in1<=uart_data_in;
uart_data_in2<=uart_data_in1;
end
assign uart_negedge_data=((~uart_data_in1)&(uart_data_in2))?1'b1:1'b0;
////////////
reg clk_bps_posedge1,clk_bps_posedge2;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
clk_bps_posedge1<=0;
clk_bps_posedge2<=0;
end
else
begin
clk_bps_posedge1<=clk_bps_posedge;
clk_bps_posedge2<=clk_bps_posedge1;
end
assign clk_bps_posedge_true=clk_bps_posedge2;
/////////////////////////////////
reg bps_start_r;
reg receiver_data_start_flag_r;
reg [3:0] receiver_data_cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
bps_start_r<=0;
receiver_data_start_flag_r<=0;
end
else
if(uart_negedge_data)
begin
bps_start_r<=1;
receiver_data_start_flag_r<=1;
end
else
if(receiver_data_cnt==4'd10)
begin
receiver_data_start_flag_r<=0;
bps_start_r<=0;
end
assign bps_start=bps_start_r;
assign receiver_data_start_flag=receiver_data_start_flag_r;
////////////////////////
reg [7:0] receiver_data_r;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
receiver_data_r<=0;
receiver_data_cnt<=0;
end
else
if(receiver_data_start_flag)
begin
if((clk_bps_posedge_true)&&(receiver_data_cnt<=9))
begin
receiver_data_cnt<=receiver_data_cnt+1;
case(receiver_data_cnt)
0: ;
1:receiver_data_r[0]<=uart_data_in;
2:receiver_data_r[1]<=uart_data_in;
3:receiver_data_r[2]<=uart_data_in;
4:receiver_data_r[3]<=uart_data_in;
5:receiver_data_r[4]<=uart_data_in;
6:receiver_data_r[5]<=uart_data_in;
7:receiver_data_r[6]<=uart_data_in;
8:receiver_data_r[7]<=uart_data_in;
9: ;
default:;
endcase
end
else
begin
receiver_data<=receiver_data_r;
// receiver_data_cnt<=0;
end
end
else
begin
receiver_data<=receiver_data;
receiver_data_cnt<=0;
end
endmodule
3.发送数据代码如下:
一旦检测到接收的数据标志位由高变低,表示接收数据完成,可以发送数据了,也在上升沿发送(2603)。
module uart_trans(
clk,
rst_n,
trans_data_start,
trans_data,
uart_data_out,
bps_start2,
clk_bps_trans
);
input clk;
input rst_n;
input trans_data_start;
input [7:0] trans_data;
input clk_bps_trans;
output uart_data_out;
output reg bps_start2;
////////////////////////////////////////
/*reg rst_n3,rst_n4;
wire true_rst_n;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
rst_n3<=0;
rst_n4<=0;
end
else
begin
rst_n3<=1;
rst_n4<=rst_n3;
end
assign true_rst_n=rst_n4;
*/
////////////////////////
reg trans_data_start1,trans_data_start2;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
trans_data_start1<=0;
trans_data_start2<=0;
end
else
begin
trans_data_start1<=trans_data_start;
trans_data_start2<=trans_data_start1;
end
assign trans_data_negedge_start=((~trans_data_start1)&(trans_data_start2))?1'b1:1'b0;
/////////////////////////////////
reg trans_data_en;
reg [3:0] trans_data_cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
bps_start2<=0;
trans_data_en<=0;
end
else
if(trans_data_negedge_start)
begin
trans_data_en<=1;
bps_start2<=1;
end
else
if(trans_data_cnt==4'd10)
begin
trans_data_en<=0;
bps_start2<=0;
end
reg uart_data_out;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
trans_data_cnt<=0;
uart_data_out<=0;
end
else
if(trans_data_en)
begin
if((clk_bps_trans)&(trans_data_cnt<=9))
trans_data_cnt<=trans_data_cnt+1;
case(trans_data_cnt)
0:uart_data_out<=1;
1:uart_data_out<=trans_data[0] ;
2:uart_data_out<=trans_data[1] ;
3:uart_data_out<=trans_data[2] ;
4:uart_data_out<=trans_data[3] ;
5:uart_data_out<=trans_data[4] ;
6:uart_data_out<=trans_data[5] ;
7:uart_data_out<=trans_data[6] ;
8:uart_data_out<=trans_data[7] ;
9:uart_data_out<=1;
default:;
endcase
if(trans_data_cnt==10)
trans_data_cnt<=0;
end
endmodule
4.采用同步释放,异步复位电路,防止亚稳态的产生,具体代码如下:
module sys(
clk,
rst_n,
true_rst_n
);
input rst_n,clk;
output true_rst_n;
reg rst_n1,rst_n2;
wire true_rst_n;
always@(posedge clk )
if(!rst_n)
begin
rst_n1<=0;
rst_n2<=0;
end
else
begin
rst_n1<=1;
rst_n2<=rst_n1;
end
assign true_rst_n=rst_n2;
endmodule
5.顶层模块例化,分层写有利于后期维护和修改,具体如下:
///////////
module uart(
clk,
rst_n,
uart_data_in,
uart_data_out
);
input clk;
input rst_n;
input uart_data_in;
output uart_data_out;
wire bps_start;
wire clk_bps_posedge_data;
wire [7:0] receiver_data;
wire receiver_data_start_flag;
wire bps_start2 ;
wire clk_bps_posedge1;
wire true_rst_n;
posedge_bps U1(
.clk (clk ),
.rst_n (true_rst_n ),
.bps_start (bps_start ),
.clk_bps_posedge_data (clk_bps_posedge_data )
);
uart_receiver U2 (
.clk (clk) ,
.rst_n (true_rst_n) ,
.clk_bps_posedge (clk_bps_posedge_data),
.uart_data_in (uart_data_in) ,
.receiver_data (receiver_data) ,
.receiver_data_start_flag (receiver_data_start_flag) ,
.bps_start (bps_start)
);
posedge_bps U3(
.clk (clk ),
.rst_n (true_rst_n ),
.bps_start (bps_start2 ),
.clk_bps_posedge_data (clk_bps_posedge1 )
);
uart_trans U4(
.clk (clk ) ,
.rst_n (true_rst_n ) ,
.trans_data_start (receiver_data_start_flag ) ,
.trans_data (receiver_data ) ,
.uart_data_out (uart_data_out ) ,
.bps_start2 (bps_start2 ) ,
.clk_bps_trans (clk_bps_posedge1 )
);
sys U5 (
.clk(clk),
.rst_n(rst_n),
.true_rst_n(true_rst_n)
);
endmodule
6.仿真代码:
`timescale 1 ns/ 1 ns
module uart_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg rst_n;
reg uart_data_in;
// wires
wire uart_data_out;
// assign statements (if any)
uart i1 (
// port map - connection between master ports and signals/registers
.clk(clk),
.rst_n(rst_n),
.uart_data_in(uart_data_in),
.uart_data_out(uart_data_out)
);
initial
begin
clk=0;
forever #10 clk=~clk;
end
initial begin
rst_n=0;
#8000 ;
rst_n=1;
end
initial begin
uart_data_in=1;
end
task task_data_in;
input [7:0] uart_data;
begin
#200000;
uart_data_in<=0;
#104166;
uart_data_in<=uart_data[0];
#104166;
uart_data_in<=uart_data[1];
#104166;
uart_data_in<=uart_data[2];
#104166;
uart_data_in<=uart_data[3];
#104166;
uart_data_in<=uart_data[4];
#104166;
uart_data_in<=uart_data[5];
#104166;
uart_data_in<=uart_data[6];
#104166;
uart_data_in<=uart_data[7];
#104166;
uart_data_in<=1;
#200000;
end
endtask
initial begin
#200;
task_data_in(8'hcb);
#200;
task_data_in(8'h0a);
#200;
task_data_in(8'h09);
#200;
task_data_in(8'h0f);
end
endmodule
7.RTL示图和仿真结果:
1).RTL
2).仿真结果
记住:调试的售后可用串口调试助手等等,波特率需保持一致。