一、通信特点
异步、串行、全双工
一般描述某种通信的特点为:同步/异步 , 串行/并行 , 半双工/全双工
同步:要求一个芯片控制另一芯片的时序,一般,两者之间至少采一 个总线连接以控制时钟(“时钟线”), 其中主机主动控制时钟线(通过时钟线输出),从机被动接受时钟线(通过时钟线输入)。
异步:双方不会通过总线连接时钟,异步通信要求双方使用独立的时钟生成装置(波特率发生器),生成相 同的通信速度。
串行:在每个数据方向上仅有一根数据线。每次仅传输一位数据。 并行:在每个数据方向上有多根数据线。每次可以传输数据的多个位(一般是8/16位)
半双工:数据线仅有一组,同一时刻,只有一方控制数据线的发送,另一方接收数据(即双方不能同时发送 数据)
全双工:数据线有两组或以上,同一时刻,通信双方都可以给对方发送数据。
二、uart协议介绍
uart:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种通用串行数据总线,用于异步通信。uart能实现双向通信,在嵌入式设计中,它常用于主机与辅助设备通信。uart包括RS232、RS449、RS432、RS422和RS485等接口标准规范和总线标准规范,既uart是异步串行通信口的的总称。而RS232、RS449、RS432、RS422和RS485等是对应各种异步串行通信口的接口标准和总线标准,它规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容,实际上属于通信网络中的物理层(最底层)的概念,与通信协议没有直接关系。(内容来源于《搭建你的数字积木-数字电路与逻辑设计》)
三、本篇博文主要介绍RS232接口标准的代码实现
使用工具:ISE Design Suite 14.7
FPGA开发板: Basys2
实现的功能:
指令一:PC端发送I like FPGA,开发板回应 I like FPGA, too. 在PC端显示。
指令二:PC端发送I like Verilog,开发板回应 I like Verilog, too. 在PC端显示。
RTL视图如下:
本篇博文主要记录代码实现,后续有时间会详细整理,欢迎各位大佬交流指点
顶层设计代码
module top_uart_tx_rx(
input clk,
input rst_n,
input rs232_rx,
output rs232_tx
);
//uart_rx
wire [7:0] rx_data;
wire rx_done;
//uart_tx
wire tx_finish;
wire send_en;
wire [7:0] data_tx;
//rx_datadeal
wire tx_en1;
wire tx_en2;
//tx_datadeal
uart_rx uart_rx(
.clk (clk) ,
.rst_n (rst_n) ,
.rs232_rx(rs232_rx) ,
.rx_data (rx_data) ,
.rx_done (rx_done)
);
uart_tx uart_tx(
.clk (clk),
.rst_n (rst_n),
.send_en (send_en),
.data_tx (data_tx),
.rs232_tx (rs232_tx),
.tx_finish (tx_finish)
);
tx_datadeal tx_datadeal(
.clk (clk ),
.rst_n (rst_n ),
.tx_en1 (tx_en1),
.tx_en2 (tx_en2),
.tx_finish (tx_finish),
.send_en (send_en),
.data_tx (data_tx)
);
rx_datadeal rx_datadeal(
.clk (clk) ,
.rst_n (rst_n) ,
.rx_data (rx_data),
.rx_done (rx_done),
.tx_en1 (tx_en1),
.tx_en2 (tx_en2)
);
endmodule
接收数据处理代码
module rx_datadeal(
input clk ,
input rst_n ,
input [7:0] rx_data ,
input rx_done ,
output reg tx_en1 ,
output reg tx_en2
);
reg [3:0] data_cnt;
reg [3:0] flag1;
reg [3:0] flag2;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
data_cnt <= 4'd0;
else if(flag1 == 4'd11 || flag2 == 4'd14 )
data_cnt <= 4'd0;
else if(rx_done)
data_cnt <= data_cnt + 1'b1;
end
//给tx的使能信号
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
tx_en1 <= 1'b0;
tx_en2 <= 1'b0;
end
else if(flag1 == 4'd11)
tx_en1 <= 1'b1;
else if(flag2 == 4'd14)
tx_en2 <= 1'b1;
else
begin
tx_en1 <= 1'b0;
tx_en2 <= 1'b0;
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
flag1 <= 4'd0;
flag2 <= 4'd0;
end
else if(flag1 == 4'd11)
flag1 <= 4'd0;
else if(flag2 == 4'd14)
flag2 <= 4'd0;
else
begin
case(data_cnt)
4'd0 : begin
if(rx_data == "I")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd1 : begin
if(rx_data == " ")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd2 : begin
if(rx_data == "L")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd3 : begin
if(rx_data == "i")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd4 : begin
if(rx_data == "k")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd5 : begin
if(rx_data == "e")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd6 : begin
if(rx_data == " ")
begin
flag1 <= flag1 + 1'b1;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd7 : begin
if(rx_data == "F")
begin
flag1 <= flag1 + 1'b1;
flag2 <= 4'd0;
end
else if(rx_data == "V")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd8 : begin
if(rx_data == "P")
begin
flag1 <= flag1 + 1'b1;
flag2 <= 4'd0;
end
else if(rx_data == "e")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd9 : begin
if(rx_data == "G")
begin
flag1 <= flag1 + 1'b1;
flag2 <= 4'd0;
end
else if(rx_data == "r")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd10 : begin
if(rx_data == "A")
begin
flag1 <= flag1 + 1'b1;
flag2 <= 4'd0;
end
else if(rx_data == "i")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd11 : begin
if(rx_data == "l")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd12 : begin
if(rx_data == "o")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
4'd13 : begin
if(rx_data == "g")
begin
flag1 <= 4'd0;
flag2 <= flag2 + 1'b1;
end
else
begin
flag1 <= flag1;
flag2 <= flag2;
end
end
default : begin
flag1 <= 4'd0;
flag2 <= 4'd0;
end
endcase
end
end
endmodule
接收代码
module uart_rx(clk,rst_n,rs232_rx,rx_data,rx_done);
input clk;
input rst_n;
input rs232_rx;
output reg [7:0] rx_data;
output reg rx_done;
reg [1:0]s_rs232_rx;//消除亚稳态
reg [1:0]tmp_rs232_rx;//数据寄存器
wire nedge;
reg [15:0]div_cnt;//分频计数器
reg bps_clk;
reg uart_state;
reg [7:0]bps_cnt;
reg [2:0]start_bit;
reg [2:0]stop_bit;
reg [2:0]rx_data_t [7:0];
// 地址宽度
always@(posedge clk or negedge rst_n)
if(!rst_n)
s_rs232_rx <= 0;
else begin
s_rs232_rx[0] <= rs232_rx;
s_rs232_rx[1] <= s_rs232_rx[0];
end
always@(posedge clk or negedge rst_n)
if(!rst_n)
tmp_rs232_rx <= 0;
else begin
tmp_rs232_rx[0] <= s_rs232_rx[1];
tmp_rs232_rx[1] <= tmp_rs232_rx[0];
end
assign nedge = tmp_rs232_rx[1] & ~tmp_rs232_rx[0];
always@(posedge clk or negedge rst_n)
if(!rst_n)
div_cnt <= 16'd0;
else if(uart_state == 1) begin
if(div_cnt == 325)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1;
end
else
div_cnt <= 0;
always@(posedge clk or negedge rst_n)
if(!rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'b1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
always@(posedge clk or negedge rst_n)
if(!rst_n)
uart_state <= 1'b0;
else if(nedge)
uart_state <= 1'b1;
else if(rx_done || (bps_cnt == 8'd12 && start_bit > 2))
uart_state <= 1'b0;
else
uart_state <= uart_state;
always@(posedge clk or negedge rst_n)
if(!rst_n)
bps_cnt <= 0;
else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && start_bit > 2))
bps_cnt <= 0;
else if(bps_clk == 1)
bps_cnt <= bps_cnt + 1;
else
bps_cnt <= bps_cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
rx_done <= 1'b0;
end
else if(bps_cnt == 8'd159) begin
rx_done <= 1'b1;
end
else begin
rx_done <= 1'b0;
end
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
start_bit <= 3'd0;
rx_data_t[0] <= 3'd0;
rx_data_t[1] <= 3'd0;
rx_data_t[2] <= 3'd0;
rx_data_t[3] <= 3'd0;
rx_data_t[4] <= 3'd0;
rx_data_t[5] <= 3'd0;
rx_data_t[6] <= 3'd0;
rx_data_t[7] <= 3'd0;
stop_bit <= 3'd0;
end
else if(bps_clk == 1) begin
case(bps_cnt)
0:begin
start_bit <= 3'd0;
rx_data_t[0] <= 3'd0;
rx_data_t[1] <= 3'd0;
rx_data_t[2] <= 3'd0;
rx_data_t[3] <= 3'd0;
rx_data_t[4] <= 3'd0;
rx_data_t[5] <= 3'd0;
rx_data_t[6] <= 3'd0;
rx_data_t[7] <= 3'd0;
stop_bit <= 3'd0;
end
5,6,7,8,9,10: start_bit <= start_bit + s_rs232_rx[1];
21,22,23,24,25,26: rx_data_t[0] <= rx_data_t[0] + s_rs232_rx[1];
37,38,39,40,41,42: rx_data_t[1] <= rx_data_t[1] + s_rs232_rx[1];
53,54,55,56,57,58: rx_data_t[2] <= rx_data_t[2] + s_rs232_rx[1];
69,70,71,72,73,74: rx_data_t[3] <= rx_data_t[3] + s_rs232_rx[1];
85,86,87,88,89,90: rx_data_t[4] <= rx_data_t[4] + s_rs232_rx[1];
101,102,103,104,105,106: rx_data_t[5] <= rx_data_t[5] + s_rs232_rx[1];
117,119,120,121,122,123: rx_data_t[6] <= rx_data_t[6] + s_rs232_rx[1];
133,135,136,137,138,139: rx_data_t[7] <= rx_data_t[7] + s_rs232_rx[1];
149,151,152,153,154,155: stop_bit <= stop_bit + s_rs232_rx[1];
endcase
end
always@(posedge clk or negedge rst_n)
if(!rst_n)
rx_data <= 8'd0;
else if(bps_cnt == 159) begin
rx_data[0] <= rx_data_t[0][2];
rx_data[1] <= rx_data_t[1][2];
rx_data[2] <= rx_data_t[2][2];
rx_data[3] <= rx_data_t[3][2];
rx_data[4] <= rx_data_t[4][2];
rx_data[5] <= rx_data_t[5][2];
rx_data[6] <= rx_data_t[6][2];
rx_data[7] <= rx_data_t[7][2];
end
endmodule
发送数据处理代码
module tx_datadeal(
input clk,
input rst_n,
input tx_finish,
input tx_en1,
input tx_en2,
output reg send_en,
output reg [7:0] data_tx
);
//define
reg [4:0] data_cnt;
reg tx_state1;
reg tx_state2;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
tx_state1 <= 1'b0;
tx_state2 <= 1'b0;
end
else if(tx_en1)
tx_state1 <= 1'b1;
else if(tx_en2)
tx_state2 <= 1'b1;
else if(data_cnt == 5'd15)
tx_state1 <= 1'b0;
else if(data_cnt == 5'd18)
tx_state2 <= 1'b0;
else
begin
tx_state1 <= tx_state1;
tx_state2 <= tx_state2;
end
end
//data_cnt
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
data_cnt <= 5'd0;
else if(tx_state1 == 1'b1 && data_cnt == 5'd15)
data_cnt <= 5'd0;
else if(tx_state2 == 1'b1 && data_cnt == 5'd18)
data_cnt <= 5'd0;
else if(tx_finish)
data_cnt <= data_cnt + 1'b1;
else
data_cnt <= data_cnt;
end
//send_en
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
send_en <= 1'b0;
else if(tx_en1 || tx_en2)
send_en <= 1'b1;
else if(tx_finish)
begin
if(tx_state1)
begin
if(data_cnt < 5'd14)
send_en <= 1'b1;
else
send_en <= 1'b0;
end
else if(tx_state2)
begin
if(data_cnt < 5'd17)
send_en <= 1'b1;
else
send_en <= 1'b0;
end
end
else
send_en <= 1'b0;
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
data_tx <= 8'd0;
else if(tx_state1)
begin
case(data_cnt)
5'd0 : data_tx <= "I";
5'd1 : data_tx <= " ";
5'd2 : data_tx <= "L";
5'd3 : data_tx <= "i";
5'd4 : data_tx <= "k";
5'd5 : data_tx <= "e";
5'd6 : data_tx <= " ";
5'd7 : data_tx <= "F";
5'd8 : data_tx <= "P";
5'd9 : data_tx <= "G";
5'd10 : data_tx <= "A";
5'd11 : data_tx <= ",";
5'd12 : data_tx <= "t";
5'd13 : data_tx <= "o";
5'd14 : data_tx <= "o";
default : data_tx <= 8'd0;
endcase
end
else if(tx_state2)
begin
case(data_cnt)
5'd0 : data_tx <= "I";
5'd1 : data_tx <= " ";
5'd2 : data_tx <= "L";
5'd3 : data_tx <= "i";
5'd4 : data_tx <= "k";
5'd5 : data_tx <= "e";
5'd6 : data_tx <= " ";
5'd7 : data_tx <= "V";
5'd8 : data_tx <= "e";
5'd9 : data_tx <= "r";
5'd10 : data_tx <= "i";
5'd11 : data_tx <= "l";
5'd12 : data_tx <= "o";
5'd13 : data_tx <= "g";
5'd14 : data_tx <= ",";
5'd15 : data_tx <= "t";
5'd16 : data_tx <= "o";
5'd17 : data_tx <= "o";
default : data_tx <= 8'd0;
endcase
end
else
data_tx <= 8'd0;
end
endmodule
发送代码
module uart_tx(clk,rst_n,tx_finish,send_en,rs232_tx,uart_state,data_tx);
input clk ;
input rst_n ;
input send_en ;//发送使能信号
input [ 7:0] data_tx ;//发送字节
output reg rs232_tx ;
output reg tx_finish ;
output reg uart_state ;
reg bps_clk ;//波特率时钟
reg [ 15:0] div_cnt ;//分频计数器
reg [ 15:0] bps_DR = 5207 ;//分频计数最大值
reg [ 3:0] bps_cnt ;//波特率计数时钟
reg [ 7:0] r_data_tx ;
localparam START_BYTE = 1'b0 ;
localparam STOP_BYTE = 1'b1 ;
//控制信号
always@(posedge clk or negedge rst_n)
if(!rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(bps_cnt == 4'd11)
uart_state <= 1'b0;
else
uart_state <= uart_state;
//寄存器存储发送信息
always@(posedge clk or negedge rst_n)
if(!rst_n)
r_data_tx <= 1'b0;
else if(uart_state)
r_data_tx <= data_tx;
else
r_data_tx <= r_data_tx;
//counter
always@(posedge clk or negedge rst_n)
if(!rst_n)
div_cnt <= 16'd0;
else if(uart_state)
begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
//产生bps_clk
always@(posedge clk or negedge rst_n)
if(!rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'd1;
else
bps_clk <= 1'b0;
always@(posedge clk or negedge rst_n)
if(!rst_n)
bps_cnt <= 4'd0;
else if(bps_cnt == 4'd11)
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
//产生发送完成信号
always@(posedge clk or negedge rst_n)
if(!rst_n)
tx_finish <= 1'b0;
else if(bps_cnt == 4'd11)
tx_finish <= 1'b1;
else
tx_finish <= 1'b0;
//发送模块
always@(posedge clk or negedge rst_n)
if(!rst_n)
rs232_tx <= 1'b1;
else
begin
case(bps_cnt)
0:rs232_tx <= 1'b1;
1:rs232_tx <= START_BYTE; //0
2:rs232_tx <= r_data_tx[0];
3:rs232_tx <= r_data_tx[1];
4:rs232_tx <= r_data_tx[2];
5:rs232_tx <= r_data_tx[3];
6:rs232_tx <= r_data_tx[4];
7:rs232_tx <= r_data_tx[5];
8:rs232_tx <= r_data_tx[6];
9:rs232_tx <= r_data_tx[7];
10:rs232_tx <= STOP_BYTE; //1
default:rs232_tx <= 1'b1;
endcase
end
endmodule
TESTBENCH
module tb_uart_tx_rx;
// Inputs
reg clk;
reg rst_n;
reg rs232_rx;
// Outputs
wire rs232_tx;
// Instantiate the Unit Under Test (UUT)
top_uart_tx_rx uut (
.clk(clk),
.rst_n(rst_n),
.rs232_rx(rs232_rx),
.rs232_tx(rs232_tx)
);
parameter period=20,
RST_ING=1'b0,
BPS_9600=32'd104_167;
initial begin
// Initialize Inputs
clk = 0;
forever
#(period/2)clk=~clk;
end
reg [4:0] cnt;
reg [7:0] data_tx;
initial begin
rst_n = RST_ING;
#5000;
rst_n = ~RST_ING;
rs232_rx = 1'b1;
//I Like FPGA
#1000_000;
data_tx = "I";
tx_task(data_tx);
#1000_000;
data_tx = " ";
tx_task(data_tx);
#1000_000;
data_tx = "L";
tx_task(data_tx);
#1000_000;
data_tx = "i";
tx_task(data_tx);
#1000_000;
data_tx = "k";
tx_task(data_tx);
#1000_000;
data_tx = "e";
tx_task(data_tx);
#1000_000;
data_tx = " ";
tx_task(data_tx);
#1000_000;
data_tx = "F";
tx_task(data_tx);
#1000_000;
data_tx = "P";
tx_task(data_tx);
#1000_000;
data_tx = "G";
tx_task(data_tx);
#1000_000;
data_tx = "A";
tx_task(data_tx);
//I Like Verilog
#100_000_000;
data_tx = "I";
tx_task(data_tx);
#1000_000;
data_tx = " ";
tx_task(data_tx);
#1000_000;
data_tx = "L";
tx_task(data_tx);
#1000_000;
data_tx = "i";
tx_task(data_tx);
#1000_000;
data_tx = "k";
tx_task(data_tx);
#1000_000;
data_tx = "e";
tx_task(data_tx);
#1000_000;
data_tx = " ";
tx_task(data_tx);
#1000_000;
data_tx = "V";
tx_task(data_tx);
#1000_000;
data_tx = "e";
tx_task(data_tx);
#1000_000;
data_tx = "r";
tx_task(data_tx);
#1000_000;
data_tx = "i";
tx_task(data_tx);
#1000_000;
data_tx = "l";
tx_task(data_tx);
#1000_000;
data_tx = "o";
tx_task(data_tx);
#1000_000;
data_tx = "g";
tx_task(data_tx);
//I Like FPGA
#100_000_000;
data_tx = "I";
tx_task(data_tx);
#1000_000;
data_tx = " ";
tx_task(data_tx);
#1000_000;
data_tx = "L";
tx_task(data_tx);
#1000_000;
data_tx = "i";
tx_task(data_tx);
#1000_000;
data_tx = "k";
tx_task(data_tx);
#1000_000;
data_tx = "e";
tx_task(data_tx);
#1000_000;
data_tx = " ";
tx_task(data_tx);
#1000_000;
data_tx = "F";
tx_task(data_tx);
#1000_000;
data_tx = "P";
tx_task(data_tx);
#1000_000;
data_tx = "G";
tx_task(data_tx);
#1000_000;
data_tx = "A";
tx_task(data_tx);
end
task tx_task;
input [7:0] rs232_tx;
integer i;
begin
rs232_rx = 0;
#BPS_9600;
for(i = 0;i < 8;i = i+1) begin
rs232_rx = data_tx[i];
#BPS_9600;
end
rs232_rx = 1;
#BPS_9600;
end
endtask
endmodule
仿真图如下:
本篇博文为记录学习所用,如有转载请注明出处。