UART 协议
异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的时间间隔是固定的。
串行接口是一种可以将接受来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接受的串行数据流转换为并行的数据字符供给CPU的器件。
起始位:先发出一个逻辑”0”信号,表示传输字符的开始。
数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输
校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
发送
在串口通信中波特率表示每秒能传输的数据位,9600就是每秒能传输9600位
字节的发送
//-------1字节数据的发送-------
module uart_tx(
input clk,
input rst_n,
input cnt_start, //发送使能
input [7:0]tx_data,
output reg tx,
output reg tx_done //传输一个字节完成标志
);
reg [12:0]cnt_bps;
parameter bps_t = 13'd5207; //传输1bit所需计数值
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_bps <= 13'd0;
else if(cnt_bps == bps_t)
cnt_bps <= 13'd0;
else if(cnt_start)
cnt_bps <= cnt_bps + 1'b1;
else
cnt_bps <= 1'b0;
end
wire bps_sig;
assign bps_sig = (cnt_bps == 13'd2604) ? 1'b1 : 1'b0; //将采集数据的时刻放在波特率计数器每次循环计数的中间位置
reg [3:0]state;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
state <= 4'd0;
tx <= 1'b1;
tx_done <= 1'b0;
end
else begin
case(state)
0: if(cnt_start & bps_sig) begin
state <= state + 1'b1;
tx <= 1'b0;
end
else begin
state <= state;
tx <= 1'b1;
end
1,2,3,4,5,6,7,8: if(bps_sig) begin
tx <= tx_data[state - 1'b1]; //注意,从低位依次往高位发送
state <= state + 1'b1;
end
else begin
state <= state;
tx <= tx;
end
9,10: if(bps_sig) begin
state <= state + 1'b1;
tx <= 1'b1;
end
11: begin
state <= state + 1'b1;
tx_done <= 1'b1;
end
12: begin
state <= 1'b0;
tx_done <= 1'b0;
end
endcase
end
end
endmodule
数据的接收
,字节传输形式:1-0-x-x-x-x-x-x-x-x-0-1,检测到下降沿即-1-0-(起始标志)后开始进行接收字节,将接收到的一一存入寄存器中,8个bit后结束接收,就是简单的1个字节的接收。
值得注意的是,虽然1字节=8bit,但是因为有起始标志(2bit:-1-0-)和结束标志(2bit:-0-1-),所以在发送和接收过程中,都需要考虑到这一点。
//-------1字节的接收-------
module uart_rx(
input clk,
input rst_n,
input data_rx,
output [7:0] data_tx,
output reg rx_int //接收字节时状态为1
);
reg [12:0]cnt_bps;
reg bps_start;
parameter bps_t = 13'd5207; //传输1bit所需计数值
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_bps <= 13'd0;
else if(cnt_bps == bps_t)
cnt_bps <= 13'd0;
else if(bps_start)
cnt_bps <= cnt_bps + 1'b1;
else
cnt_bps <= 1'b0;
end
wire bps_sig;
assign bps_sig = (cnt_bps == 13'd2604) ? 1'b1 : 1'b0; //将采集数据的时刻放在波特率计数器每次循环计数的中间位置
reg [1:0] rx;
always @(posedge clk or negedge rst_n)begin
if(!rst_n) rx <= 2'b11;
else begin
rx[0] <= data_rx;
rx[1] <= rx[0];
end
end
wire nege_edge;
assign nege_edge= rx[1] & ~rx[0]; //检测下降沿
reg [3:0]num;
aaa@qq.com(posedge clk or negedge rst_n)begin
if(!rst_n) begin
bps_start <= 1'b0;
rx_int <= 1'b0;
end
else if(nege_edge)begin
bps_start <= 1'b1;
rx_int <= 1'b1;
end
else if(num == 4'd10)begin
bps_start <= 1'b0;
rx_int <= 1'b0;
end
end
reg [7:0] rx_data_temp_r; //当前数据接收寄存器
reg [7:0] rx_data_r; //用来锁存数据
aaa@qq.com(posedge clk or negedge rst_n)begin
if(!rst_n) begin
rx_data_r <= 8'd0;
rx_data_temp_r <= 8'd0;
num <= 4'd0;
end
else if(rx_int) begin
if(bps_sig) begin
num <= num + 1'b1;
case(num)
4'd1: rx_data_temp_r[0] <= data_rx; //锁存第0bit
4'd2: rx_data_temp_r[1] <= data_rx; //锁存第1bit
4'd3: rx_data_temp_r[2] <= data_rx; //锁存第2bit
4'd4: rx_data_temp_r[3] <= data_rx; //锁存第3bit
4'd5: rx_data_temp_r[4] <= data_rx; //锁存第4bit
4'd6: rx_data_temp_r[5] <= data_rx; //锁存第5bit
4'd7: rx_data_temp_r[6] <= data_rx; //锁存第6bit
4'd8: rx_data_temp_r[7] <= data_rx; //锁存第7bit
default: ;
endcase
end
else if(num == 4'd10)begin
rx_data_r <= rx_data_temp_r;
num <= 4'd0;
end
end
end
assign data_tx = rx_data_r;
endmodule
tb
module tb_uart_rx;
// Inputs
reg clk;
reg rst_n;
reg data_rx;
// Outputs
wire [7:0] data_tx;
wire rx_int;
// Instantiate the Unit Under Test (UUT)
uart_rx uut (
.clk(clk),
.rst_n(rst_n),
.data_rx(data_rx),
.data_tx(data_tx),
.rx_int(rx_int)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
data_rx = 0;
// Wait 20 ns for global reset to finish
#20
rst_n = 1;
data_rx = 1;
#104160
rst_n = 1;
data_rx = 0; //起始位:-1-0-
#104160 //传输11001011 (倒序)
rst_n = 1;
data_rx = 1;
#104160
rst_n = 1;
data_rx = 1;
#104160
rst_n = 1;
data_rx = 0;
#104160
rst_n = 1;
data_rx = 1;
#104160
rst_n = 1;
data_rx = 0;
#104160
rst_n = 1;
data_rx = 0;
#104160
rst_n = 1;
data_rx = 1;
#104160
rst_n = 1;
data_rx = 1;
#104160 //结束位 -0-1-
rst_n = 1;
data_rx = 0;
#104160
rst_n = 1;
data_rx = 1;
#104160 //复位
rst_n = 0;
data_rx = 0;
end
endmodule
ref
https://blog.csdn.net/qq_40789587/article/details/84073419?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-5&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-5
ref
https://blog.csdn.net/zjy900507/article/details/79789671
下一篇: leetcode最长公共前缀C++版