一、UART协议
1、基本概念
通用异步收发传输器,是一种异步收发传输器,在发送数据通过将并行数据转换成串行数据进行传输,在接收数据时将串行数据转换成并行数据。
串行通信分为同步串行通信和异步串行通信。同步串行通信即需要时钟的参与,通信双方需要在同一时钟的控制下,同步传输数据;异步串行通信则不需要时钟的干预,通信双方使用各种的时钟来控制数据的发送和接收。uart属于异步串行通信,即没有时钟信号来同步或验证从发送器发送并由接收器接收的数据,这就要求发送器和接收器必须事先就时序参数达成一致。
2、UART协议
UART串口协议规定,当总线处于空闲状态时信号线的状态为高电平,表示当前线路上没有数据传输。
起始位:开始进行数据传输时发送方要先发送一个低电平来表示传输字符的开始。
数据位:起始位之后就需要传输数据,数据位可以是5~9位,构成一个字符,一般是8位,先发送最低位后发送最高位。
奇偶校验位:奇偶校验位是用来检验数据在传输过程中是否出错。在奇校验时,发送方应使数据位中1的个数与校验位中1的个数为奇数,接收方在接收数据时,对1的个数进行检测,若1的个数不为奇数个,则说明数据在传输过程中存在差错。偶校验则相反。
停止位:数据结束标志,可以是1位或者2位的高电平。由于数据在传输线上是定时传输的,并且每一个设备有自己的时钟,很可能在通信中两台设备之间出现了小小的不同步,因此停止位不仅仅是表示数据传输的结束,并且提供计算机校正时钟的机会。停止位越多,数据传输越稳定,但是数据传输速度越慢。
3、波特率
在电子通信领域,波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。
波特率表示每秒钟传送码元符号的个数(表示一秒钟能传输的最大的bit数),它是对符号传输速率的一种度量,用单位时间内载波调制状态改变的次数来表示,1波特指每秒传输1个字符。
数据传输速率使用波特率来表示,单位bps(bits per second),常见的波特率有9600、19200、38400、57600、115200等。例如将串口波特率设置位115200bps,那么传输一个bit需要的时间是1/115200 ≈ 8.68us ==8680 ns 。这里拿我所使用的板子晶振(时钟频率:50MHz)来说,算出传输1bit需要的时间为8680ns,时钟周期为20ns,8680/20 = 434,转换成相应的时钟频率为434hz.相当于(50*10^6)/115200。因此公式为:时钟频率/波特率。
二、实验设计
实验:urat发送数据
//uart通信发送数据
module uart01(
clk,
rst,
baud_set,
send_en,
data,
uart_tx,
tx_down);
input clk;
input rst;
input[2:0] baud_set;//9600、19200、38400、57600、115200
input[7:0] data;
input send_en;//开关信号,发送数据
output reg uart_tx;//接收信号
output reg tx_down;//发完信号
reg[17:0] btl;
//参数定义
parameter SCLK = 50_000_000;//系统时钟 50MHZ
always@(*)
case(baud_set)
0: btl = SCLK/9600;
1: btl = SCLK/19200;
2: btl = SCLK/38400;
3: btl = SCLK/57600;
4: btl = SCLK/115200;
default: btl = SCLK/9600;//默认9600
endcase
//分频
reg[17:0] div_cnt;//基本时钟
always@(posedge clk or negedge rst)
if(!rst)
div_cnt <= 0;
else if(send_en) begin
if(div_cnt == btl-1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
//数据位+起始位+停止位 = 10位数据。但需要11个波特率时钟脉冲,第一个脉冲标记传
//输的开始,第11个脉冲标记一次传输的结束。
reg[3:0] bps_cnt;
//assign bps_clk = (div_cnt == 1);
always@(posedge clk or negedge rst)
if(!rst)
bps_cnt <= 0;
else if(send_en) begin
if(div_cnt == 1) begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
end
else
bps_cnt <= 0;
//发送数据
always@(posedge clk or negedge rst)
if(!rst) begin
uart_tx <= 1;//uart_tx复位时为1,开始传送数据为0。
tx_down <= 0;
end
else case(bps_cnt)
1: begin uart_tx <= 0; tx_down <= 0; end
2: uart_tx <= data[0];
3: uart_tx <= data[1];
4: uart_tx <= data[2];
5: uart_tx <= data[3];
6: uart_tx <= data[4];
7: uart_tx <= data[5];
8: uart_tx <= data[6];
9: uart_tx <= data[7];
10: uart_tx <= 1;
11: begin uart_tx <= 1; tx_down <= 1'b1; end
default: uart_tx <= uart_tx;
endcase
endmodule
激励:
//激励
module uart01_tb();
reg clk;
reg rst;
reg[2:0] baud_set;
reg send_en;
reg[7:0] data;
wire uart_tx;
wire tx_down;
uart01 uartdemo(
.clk(clk),
.rst(rst),
.baud_set(baud_set),
.send_en(send_en),
.data(data),
.uart_tx(uart_tx),
.tx_down(tx_down)
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst = 0;
data = 0;
send_en = 0;
baud_set = 4;
#201;
rst = 1;
#100;
data = 8'h57;
send_en = 1;
#20;
@(posedge tx_down);//等待tx_down发送完
send_en = 0;
#200000;
data = 8'h75;
send_en = 1;
#20;
@(posedge tx_down);
send_en = 0;
#2000000000;//2s
$stop;
end
endmodule
仿真波形如下:
通过仿真来看,大致完成了设计要求。
若 //assign bps_clk = (div_cnt == 1);添加bps_clk
则仿真波形图为:
三、串口发送数据任务
设计一个数据发送器,每10ms以115200的波特率发送一个数据,每次发送的数据比前一个数大一
module uart( clk,
rst,
baud_set,
send_en,
data,
uart_tx,
tx_down
);
input clk;
input rst;
input[2:0] baud_set;//9600、19200、38400、57600、115200
input[7:0] data;
input send_en;//开关信号,发送数据
output reg uart_tx;//接收信号
output reg tx_down;//发完信号
reg[17:0] btl;
//参数定义
parameter SCLK = 50_000_000;//系统时钟 50MHZ
always@(*)
case(baud_set)
0: btl = SCLK/9600;
1: btl = SCLK/19200;
2: btl = SCLK/38400;
3: btl = SCLK/57600;
4: btl = SCLK/115200;
default: btl = SCLK/9600;//默认9600
endcase
//分频
reg[17:0] div_cnt;//基本时钟
always@(posedge clk or negedge rst)
if(!rst)
div_cnt <= 0;
else if(send_en) begin
if(div_cnt == btl-1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
//数据位+起始位+停止位 = 10位数据。但需要11个波特率时钟脉冲,第一个脉冲标记传
//输的开始,第11个脉冲标记一次传输的结束。
reg[3:0] bps_cnt;
assign bps_clk = (div_cnt == 1);
always@(posedge clk or negedge rst)
if(!rst)
bps_cnt <= 0;
else if(send_en) begin
if(bps_clk) begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
end
else
bps_cnt <= 0;
//发送数据
always@(posedge clk or negedge rst)
if(!rst) begin
uart_tx <= 1;//uart_tx复位时为1,开始传送数据为0。
tx_down <= 0;
end
else case(bps_cnt)
0: tx_down <= 0;
1: uart_tx <= 0;
2: uart_tx <= data[0];
3: uart_tx <= data[1];
4: uart_tx <= data[2];
5: uart_tx <= data[3];
6: uart_tx <= data[4];
7: uart_tx <= data[5];
8: uart_tx <= data[6];
9: uart_tx <= data[7];
10: uart_tx <= 1;
11: begin uart_tx <= 1; tx_down <= 1'b1; end
default: uart_tx <= uart_tx;
endcase
endmodule
module uartDemo( clk,
rst,
uart_tx
);
input clk;
input rst;
output uart_tx;
reg[7:0] data;
reg send_en;
//例化模块
uart demo01(
.clk(clk),
.rst(rst),
.baud_set(3'd4),
.send_en(send_en),
.data(data),
.uart_tx(uart_tx),
.tx_down(tx_down)
);
//10ms分频 =10^7ns = 500000 *20ns
parameter CNT = 500_000;
reg[18:0] cnt1;
always@(posedge clk or negedge rst)
if(!rst)
cnt1 <= 0;
else if(cnt1 == CNT-1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
//产生send_en信号
always@(posedge clk or negedge rst)
if(!rst)
send_en <= 0;
else if(cnt1 == 1)
send_en <= 1'b1;
else if(tx_down)
send_en <= 0;
//data变化
always@(posedge clk or negedge rst)
if(!rst)
data <= 0;
else if(tx_down)//发完就+1
data <= data+1'b1;
endmodule
激励:
module uartDemo( clk,
rst,
uart_tx
);
input clk;
input rst;
output uart_tx;
reg[7:0] data;
reg send_en;
//例化模块
uart demo01(
.clk(clk),
.rst(rst),
.baud_set(3'd4),
.send_en(send_en),
.data(data),
.uart_tx(uart_tx),
.tx_down(tx_down)
);
//10ms分频 =10^7ns = 500000 *20ns
parameter CNT = 500_000;
reg[18:0] cnt1;
always@(posedge clk or negedge rst)
if(!rst)
cnt1 <= 0;
else if(cnt1 == CNT-1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
//产生send_en信号
always@(posedge clk or negedge rst)
if(!rst)
send_en <= 0;
else if(cnt1 == 1)
send_en <= 1'b1;
else if(tx_down)
send_en <= 0;
//data变化
always@(posedge clk or negedge rst)
if(!rst)
data <= 0;
else if(tx_down)//发完就+1
data <= data+1'b1;
endmodule
仿真波形如下:
可以看出来波形是不太对的,计数器01和02持续的时间太短了,那么为什么会出现这样的问题呢?
data自增是因为tx_down为高电平1,由仿真图看出tx_down=1维持了三个周期,原意是维持一个周期即可,因此需要修改tx_down=1的条件。根据仿真图看出,在(bps_clk == 1) && (bps_cnt == 10)时,下一时刻就是tx_down=1。
修改代码:
module uart( clk,
rst,
baud_set,
send_en,
data,
uart_tx,
tx_down
);
input clk;
input rst;
input[2:0] baud_set;//9600、19200、38400、57600、115200
input[7:0] data;
input send_en;//开关信号,发送数据
output reg uart_tx;//接收信号
output reg tx_down;//发完信号
reg[17:0] btl;
//参数定义
parameter SCLK = 50_000_000;//系统时钟 50MHZ
always@(*)
case(baud_set)
0: btl = SCLK/9600;
1: btl = SCLK/19200;
2: btl = SCLK/38400;
3: btl = SCLK/57600;
4: btl = SCLK/115200;
default: btl = SCLK/9600;//默认9600
endcase
//分频
reg[17:0] div_cnt;//基本时钟
always@(posedge clk or negedge rst)
if(!rst)
div_cnt <= 0;
else if(send_en) begin
if(div_cnt == btl-1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
//数据位+起始位+停止位 = 10位数据。但需要11个波特率时钟脉冲,第一个脉冲标记传
//输的开始,第11个脉冲标记一次传输的结束。
reg[3:0] bps_cnt;
assign bps_clk = (div_cnt == 1);
always@(posedge clk or negedge rst)
if(!rst)
bps_cnt <= 0;
else if(send_en) begin
if(bps_clk) begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
end
else
bps_cnt <= 0;
//发送数据
always@(posedge clk or negedge rst)
if(!rst) begin
uart_tx <= 1;//uart_tx复位时为1,开始传送数据为0。
tx_down <= 0;
end
else case(bps_cnt)
1: uart_tx <= 0;
2: uart_tx <= data[0];
3: uart_tx <= data[1];
4: uart_tx <= data[2];
5: uart_tx <= data[3];
6: uart_tx <= data[4];
7: uart_tx <= data[5];
8: uart_tx <= data[6];
9: uart_tx <= data[7];
10: uart_tx <= 1;
11: uart_tx <= 1;
default: uart_tx <= uart_tx;
endcase
//将tx_down信号单独拿出来
always@(posedge clk or negedge rst)
if(!rst)
tx_down <= 0;
else if((bps_clk == 1) && (bps_cnt == 10))//使tx_down只保持一个周期
tx_down <= 1;
else
tx_down <= 0;
endmodule
仿真波形如下:基本完成了设计要求。
但是为了更加完善我们的代码,还需要进行一些修改。
(1)由于串口是一个异步的收发器,因此为了保证发送的数据在时钟到来的时候是稳定的,这里也需要对输入数据进行寄存。
//寄存数据,防止数据被修改
reg [7:0]r_Data;
always@(posedge clk)
if(Send_Go)
r_Data <= data;
else
r_Data <= r_Data;
完整的代码为:文章来源:https://www.toymoban.com/news/detail-757300.html
module uart( clk,
rst,
baud_set,
Send_Go,
data,
uart_tx,
tx_down
);
input clk;
input rst;
input[2:0] baud_set;//9600、19200、38400、57600、115200
input[7:0] data;
input Send_Go;//控制数据发送
output reg uart_tx;//接收信号
output reg tx_down;//发完信号
reg[17:0] btl;
//参数定义
parameter SCLK = 50_000_000;//系统时钟 50MHZ
always@(*)
case(baud_set)
0: btl = SCLK/9600;
1: btl = SCLK/19200;
2: btl = SCLK/38400;
3: btl = SCLK/57600;
4: btl = SCLK/115200;
default: btl = SCLK/9600;//默认9600
endcase
reg send_en;
always@(posedge clk or negedge rst)
if(!rst)
send_en <= 0;
else if(Send_Go)//数据发送
send_en <= 1;
else if(tx_down)//停止发送
send_en <= 0;
//寄存数据,防止数据被修改
reg [7:0]r_Data;
always@(posedge clk)
if(Send_Go)
r_Data <= data;
else
r_Data <= r_Data;
//分频
reg[17:0] div_cnt;//基本时钟
always@(posedge clk or negedge rst)
if(!rst)
div_cnt <= 0;
else if(send_en) begin
if(div_cnt == btl-1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
//数据位+起始位+停止位 = 10位数据。但需要11个波特率时钟脉冲,第一个脉冲标记传
//输的开始,第11个脉冲标记一次传输的结束。
reg[3:0] bps_cnt;
assign bps_clk = (div_cnt == 1);
always@(posedge clk or negedge rst)
if(!rst)
bps_cnt <= 0;
else if(send_en) begin
if(bps_clk) begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
end
else
bps_cnt <= 0;
//发送数据
always@(posedge clk or negedge rst)
if(!rst) begin
uart_tx <= 1;//uart_tx复位时为1,开始传送数据为0。
tx_down <= 0;
end
else case(bps_cnt)
1: uart_tx <= 0;
2: uart_tx <= r_Data[0];
3: uart_tx <= r_Data[1];
4: uart_tx <= r_Data[2];
5: uart_tx <= r_Data[3];
6: uart_tx <= r_Data[4];
7: uart_tx <= r_Data[5];
8: uart_tx <= r_Data[6];
9: uart_tx <= r_Data[7];
10: uart_tx <= 1;
11: uart_tx <= 1;
default: uart_tx <= uart_tx;
endcase
//将tx_down信号单独拿出来
always@(posedge clk or negedge rst)
if(!rst)
tx_down <= 0;
else if((bps_clk == 1) && (bps_cnt == 10))//使tx_down只保持一个周期
tx_down <= 1;
else
tx_down <= 0;
endmodule
module uartDemo( clk,
rst,
uart_tx
);
input clk;
input rst;
output uart_tx;
reg[7:0] data;
reg Send_Go;
//例化模块
uart demo01(
.clk(clk),
.rst(rst),
.baud_set(3'd4),
.Send_Go(Send_Go),
.data(data),
.uart_tx(uart_tx),
.tx_down(tx_down)
);
//10ms分频 =10^7ns = 500000 *20ns
parameter CNT = 500_000;
reg[18:0] cnt1;
always@(posedge clk or negedge rst)
if(!rst)
cnt1 <= 0;
else if(cnt1 == CNT-1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
//产生send_en信号
always@(posedge clk or negedge rst)
if(!rst)
Send_Go <= 0;
else if(cnt1 == 1)
Send_Go <= 1;//一个脉冲信号
else
Send_Go <= 0;
//data变化
always@(posedge clk or negedge rst)
if(!rst)
data <= 0;
else if(tx_down)//发完就+1
data <= data+1'b1;
endmodule
module uartDemo_tb();
reg clk;
reg rst;
wire uart_tx;
uartDemo shiyan01(
.clk(clk),
.rst(rst),
.uart_tx(uart_tx)
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst = 0;
#201;
rst = 1;
#5000000;//50ms
end
endmodule
仿真波形节选如下:文章来源地址https://www.toymoban.com/news/detail-757300.html
到了这里,关于FPGA实现UART通信(1)---发送数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!