一、UART(串口)结构:
(1)、所需引脚:两个引脚:一个用来发送输出输出,一个用来接收数据;
(2)、在发送数据的时候将并行数据转换成串行数据来传输;
(3)、在接收数据的时候将收到的串行数据转换成并行数据;
二、UART的时序
1、接收时序(针对的是串口助手(我们操控的)发送的数据给板子,板子来实现接收):
(1)知道了外部信息是通过两个线交流的,那么要清楚怎么通过两个线实现交流的,所以他们就要遵循同一个协议(比如说两个人交流遵循同一个语言:中文,才能实现交流)。
(2)协议内容:
第一:对于RXD线(串口助手发送数据给板子的数据线),在常态(没有任何操作的时候)是高电平的状态(这个状态不是我们设置的,是硬件工程师设计的,不管我们的事);
第二:由于常态是高电平的状态,那么我们就可以认为当数据线是0的时候就表示数据开始传输了(串口助手开始发送东西过来了);
第三:当接收完数据之后,我们再接收一个校验位(就是一个普通的奇偶检验位,根据接收到的数据来判断这一次传输出现差错没有);
第四:接收完之后回到空闲状态的高电平就表示我们接收完成了;
总结:
一帧数据由4部分组成
1、起始位(1bit)
2、数据位(6/7/8bit)、LSB、MSB
3、奇偶校验位(1bit1):奇偶校验位算法:^data_temp:按位异或(相异为1,相同为0)
4、停止位(1bit/1.5bit/2bit)
注:通信双方要用同一个通信协议,不然不能进行通信。
2、发送时序(针对的是板子发送数据给串口助手,串口助手接收到数据):
第一:对于TXD线(板子发送数据给串口助手的数据线),在常态(没有任何操作的时候)是高电平的状态(这个状态不是我们设置的,是硬件工程师设计的,不管我们的事),首先发送一个高电平;
第二:然后根据数据位的01,分别发送高低电平;
第三:当发送完数据之后,我们再发送一个校验位(就是一个普通的奇偶检验位,根据发送的数据来判断这一次传输出现差错没有);
第四:发送完之后回到空闲状态的高电平就表示我们发送完成了;
3、收发参照速率
(1)什么意思呢:要接收数据的话,双方(串口助手和板子)速率不一样的话,那么可能导致一个发完了,另外一个才接收到第二个数据位,所以双发要遵循一个传播速率:波特率;
(2)波特率:表示传输一bit的数据需要的时间。
(3)一般的波特率:9600; 115200;57600;38400; 115200;
verilog参考代码:
(1)接收模块:(串口助手发送板子接收)
module uart_receiver(
input sys_clk,
input sys_rst_n,
input [3:0] bud_set,
input rxd, //接收数据(11位数据),单bit
input key_in,//奇偶校验位控制
output rx_done, //接收完成标志
output reg [7:0] data_rx//接收到的数据整合成8位
);
parameter FREQ = 50_000_000;//系统时钟
//计数模块
reg [15:0] cnt_bps;//波特率计数
wire add_bps;
wire end_bps;
reg [12:0] max_bps;
//发送位模块,10位
reg [3:0] cnt_bit;//发送数据位计数
wire add_bit;
wire end_bit;
reg start_keep;//当识别到下降沿的时候进行电平保持,不能收数据的时候为低,为高代表可以开是收数据
//缓存信号
reg [10:0] data_temp;
//打拍
reg tx_en_d0;
reg tx_en_d1;
//打拍识别出下降沿
wire start_flag;
//识别起始位和终止位,产生停止
/* reg stop_flag; */
/* reg start_bit;//开始位接收
reg stop_bit;//结束位寄存 */
reg check_bit;//0为偶校验,1为奇校验
//按键控制奇偶校验
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
check_bit <= 1'd0;
end
else if(key_in)begin
check_bit <= ~check_bit;
end
else begin
check_bit <= check_bit;
end
end
//当识别到rdx数据的下降沿,开始接收
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_en_d0 <= 1'b0;
tx_en_d1 <= 1'b0;
end
else begin
tx_en_d0 <= rxd;
tx_en_d1 <= tx_en_d0;
end
//rxd_r <= {rxd_r[0],rxd}
end
assign start_flag = ~tx_en_d0 && tx_en_d1;//检验下降沿
//检测到发送结束,开始进行接收
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
start_keep <= 1'b0;
end
else if(start_flag)begin
start_keep <= 1'b1;
end
else if(rx_done || data_temp[0] || !data_temp[10])begin
start_keep <= 1'b0;
end
else if((check_bit == 1'b0) && cnt_bit >= 10 && ((^data_temp[8:1]) != data_temp[9]))begin//偶校验
start_keep <= 1'b0;
end
else if((check_bit == 1'b1) && cnt_bit >= 10 && ((~(^data_temp[8:1])) != data_temp[9]))begin//奇校验
start_keep <= 1'b0;
end
else begin
start_keep <= start_keep;
end
end
/* //采集起始位和停止位
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
start_bit <= 1'b0;//默认是0
stop_bit <= 1'b1;//默认是一
end
else if(cnt_bit == 0 && cnt_bps == (max_bps >> 1))begin
start_bit <= rxd;
end
else if(cnt_bit == 9 && cnt_bps == (max_bps >> 1))begin
stop_bit<= rxd;
end
else begin
start_bit <= start_bit;
stop_bit <= stop_bit;
end
end */
/* //接收数据第一位接收之后为0的时候开始,不然终止
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
stop_flag <= 1'd0;
end
else if(start_bit != 0)begin
stop_flag <= 1;
end
else if(stop_bit != 1)begin
stop_flag <= 1;
end
else begin
stop_flag <= 0;//如果以上两个不满足,则保持低
end
end */
//计数器计数模块
assign add_bps = start_keep;
assign end_bps = add_bps && cnt_bps >= max_bps - 16'd1;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
cnt_bps <= 16'd0;
end
else if(add_bps)begin
if(end_bps)begin
cnt_bps <= 16'd0;
end
else begin
cnt_bps <= cnt_bps + 16'b1;
end
end
else begin
cnt_bps <= 16'b0;
end
end
//计数位变化
assign add_bit = end_bps;
assign end_bit = add_bit && cnt_bit >= 4'd10;//一共输入11位,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
cnt_bit <= 4'd0;
end
else if(add_bit)begin
if(end_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 4'b1;
end
end
else if(start_keep == 1'b0)begin//当检测到输入第一位不是0时,重新开始
cnt_bit <= 4'd0;
end
else begin
cnt_bit <= cnt_bit;
end
end
//产生波特率计数器的最大值
always @(*)begin
case(bud_set)
3'd0 : max_bps = FREQ / 9600;
3'd1 : max_bps = FREQ / 115200;
3'd2 : max_bps = FREQ / 57600;
3'd3 : max_bps = FREQ / 38400;
default : max_bps = FREQ / 115200;
endcase
end
//保存数据
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
data_temp <= 11'b1000_0000_000;
end
else if(!start_keep)begin
data_temp <= 11'b1000_0000_000;
end
else if(start_keep && (cnt_bps == (max_bps >> 1)))begin
case(cnt_bit)
4'd0: data_temp[0] <= rxd; //起始位
4'd1 : data_temp[1] <= rxd;
4'd2 : data_temp[2] <= rxd;
4'd3 : data_temp[3] <= rxd;
4'd4 : data_temp[4] <= rxd;
4'd5 : data_temp[5] <= rxd;
4'd6 : data_temp[6] <= rxd;
4'd7 : data_temp[7] <= rxd;
4'd8 : data_temp[8] <= rxd;
4'd9 : data_temp[9] <= rxd;//偶校验位
4'd10: data_temp[10] <= rxd; //停止位
default :;
endcase
end
else begin
data_temp <= data_temp;
end
end
//发送完毕的时候开始进行
assign rx_done = end_bit;//输出一个结束脉冲
//数据发送
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
data_rx <= 8'd0;
end
else if(cnt_bps == ( max_bps >> 1) && cnt_bit == 4'd10)begin
data_rx <= data_temp[8:1];
end
else begin
data_rx <= data_rx;
end
end
/* assign data_rx = data_temp; */
endmodule
(2)发送模块:(板子发送串口助手接收):
module uart_send(
input sys_clk,
input sys_rst_n,
input [7:0] uart_din,//原始数据,8位,数据要保存下来,以免丢失
input tx_en,//发送数据使能,发送一次数据使能一次
input [3:0] bud_set,
input key_in,//按键控制奇偶校验
output reg uart_tx, //发送的数据,一位一位的发送,包括起始位,数据位,停止位(有奇偶校验位)
output send_stop//数据传输完成标志信号
);
parameter FREQ = 50_000_000;//系统时钟
//计数模块
reg [15:0] cnt_bps;//波特率计数
wire add_bps;
wire end_bps;
reg [12:0] max_bps;
//发送位模块,10位
reg [3:0] cnt_bit;//发送数据位计数
wire add_bit;
wire end_bit;
/* //打拍
reg tx_en_d0;
reg tx_en_d1;
wire start_flag;//使能信号来临识别脉冲 */
reg start_keep;//当识别到上升沿的时候进行电平保持,当送完数据的时候为低,为高代表可以开始输出数据
//缓存信号
reg [7:0] data_temp;
reg send_en_R;
reg check_bit;//0为偶校验,1为奇校验
//按键控制奇偶校验
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
check_bit <= 1'd0;
end
else if(key_in)begin
check_bit <= ~check_bit;
end
else begin
check_bit <= check_bit;
end
end
//Logic Description
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
send_en_R <= 1'b0;
end
else begin
send_en_R <= tx_en;
end
end //always end
//根据检测的上升沿生成电平信号,当送完数据的时候为低,为高代表可以开始输出数据
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
start_keep <= 1'b0;
end
else if(tx_en)begin
start_keep <= 1'b1;
end
else if(end_bit)begin
start_keep <= 1'b0;
end
else begin
start_keep <= start_keep;
end
end
//产生波特率计数器的最大值
always @(*)begin
case(bud_set)
3'd0 : max_bps = FREQ / 9600;
3'd1 : max_bps = FREQ / 115200;
3'd2 : max_bps = FREQ / 57600;
3'd3 : max_bps = FREQ / 38400;
default : max_bps = FREQ / 115200;
endcase
end
/* //使能信号识别,高有效,使能
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
cnt_bps <= 16'd0;
end
else begin
tx_en_d0 <= tx_en;
tx_en_d1 <= tx_en_d0;
end
end
assign start_flag = ~tx_en_d0 && tx_en_d1;//检验上升沿 */
//数据缓存
//使能信号来临,开始计时,缓存信号,
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
data_temp <= 8'b11111111;
end
else if(send_en_R)begin
data_temp <= uart_din;
end
else begin
data_temp <= data_temp;
end
end
//计数器计数模块
assign add_bps = start_keep;
assign end_bps = add_bps && cnt_bps >= max_bps - 16'd1;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
cnt_bps <= 16'd0;
end
else if(add_bps)begin
if(end_bps)begin
cnt_bps <= 16'd0;
end
else begin
cnt_bps <= cnt_bps + 16'b1;
end
end
else begin
cnt_bps <= 16'b0;
end
end
//计数位变化
assign add_bit = end_bps;
assign end_bit = add_bit && cnt_bit >= 4'd10;//一共输出11位,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
cnt_bit <= 4'd0;
end
else if(add_bit)begin
if(end_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 4'b1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
//数据输出,根据数据位选,计数BPS_CNT次,换一位
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
uart_tx <= 1'b1;
end
else if(start_keep)begin
case(cnt_bit)
4'd0 : uart_tx = 1'b0;//起始位
4'd1 : uart_tx = data_temp[0];
4'd2 : uart_tx = data_temp[1];
4'd3 : uart_tx = data_temp[2];
4'd4 : uart_tx = data_temp[3];
4'd5 : uart_tx = data_temp[4];
4'd6 : uart_tx = data_temp[5];
4'd7 : uart_tx = data_temp[6];
4'd8 : uart_tx = data_temp[7];
4'd9 : uart_tx = (check_bit == 1'b0 ? (^data_temp) : (~(^data_temp)));//发送奇偶校验位
4'd10 : uart_tx = 1'b1;//停止位
default :uart_tx = 1'b1;
endcase
end
else begin
uart_tx = 1'b1;
end
end
assign send_stop = end_bit;//输出一个结束脉冲
endmodule