1,串口通信
异步通信的主要通讯方式,一根数据线即可完成传输,通过约定速率实现数据串并解析,按位顺序进行传输。通过电气标准分为RS-232,RS-422,RS-485。
2,串口通信时时序
一次串口传输主要有4类数据传输,具体如图所示.
空闲位 : 持续的高电平为空闲位
起始位 :1个低电平表示起始位
数据位 : 数据位一般有8位,一次传输一个字节的数据,从低到高以此传输。
校验位 : 校验位可配置为奇校验,偶校验,无效验。
停止位 : 1个高电平表示起始位。
3,FPGA实现串口通信
FPGA实现串口通信接口时序,可以通过参数配置系统时钟,波特率,传输数据位,校验,停止位。
顶层代码如下:
//
// Company:
// Engineer: xy
//
// Create Date: 2018.12.12
// Design Name:
// Module Name: uart_ip
// Project Name: uart_design
// Target Device:
// Tool versions:
// Description:
//
// Dependencies:
//
//
module uart_ip #(
parameter [31:0] SYSCLKFREQ = 50000000, //system clk
parameter [31:0] UARTATE = 115200, //uart bps
parameter [0:0] PARITY_EN = 0, //papity en?
parameter [0:0] PARITY = 0, //papity sel 0-even,1-odd
parameter [1:0] STOP_BIT = 0 //stopbit number 1 2
)(
input rst,
input clk,
output [7:0] po_rxdata,
output po_rxen,
output [7:0] po_error_cnt,
input [7:0] pi_txdata,
input pi_txen,
output po_txover,
input pi_uart_rx,
output po_uart_tx
);
uart_tx_module #(
.SYSCLKFREQ (SYSCLKFREQ),
.UARTATE (UARTATE),
.PARITY_EN (PARITY_EN),
.PARITY (PARITY),
.STOP_BIT (STOP_BIT)
)
uart_tx_inst(
.clk (clk),
.rst (rst),
.tx_data (pi_txdata),
.tx_en (pi_txen),
.rs232_tx (po_uart_tx),
.tx_over (po_txover)
);
uart_rx_module#(
.SYSCLKFREQ (SYSCLKFREQ),
.UARTATE (UARTATE),
.PARITY_EN (PARITY_EN),
.PARITY (PARITY),
.STOP_BIT (STOP_BIT)
)
uart_rx_inst(
.clk (clk),
.rst (rst),
.rs232_rx (pi_uart_rx),
.rx_data (po_rxdata),
.rx_en (po_rxen),
.error_cnt (po_error_cnt)
);
endmodule
接收端代码 :
//RX UART data
module uart_rx_module #(
parameter [31:0] SYSCLKFREQ = 50000000, //system clk
parameter [31:0] UARTATE = 115200, //uart bps 8
parameter [0:0] PARITY_EN = 0, //papity en?
parameter [0:0] PARITY = 0, //papity sel 0-even,1-odd
parameter [1:0] STOP_BIT = 0 //stop bit number 1 2
)(
input clk, // 50MHz
input rst, //rst -1
input rs232_rx, // uart signal -rx
output [7:0] rx_data, //receive data
output rx_en, //receive data en;if rx_en is 1,rx_data is vaild
output reg [7:0] error_cnt //papity is fail ,count for error data.
);
localparam [31:0] BPS_PARA = SYSCLKFREQ/UARTATE; //bps count
localparam [31:0] BPS_PARA_2 = BPS_PARA/2; //half is bps count
localparam [4:0] T_DATA_CNT = 1 + 8 + PARITY_EN + STOP_BIT; //start + 8bit + papity_bit + stop_bit;
//************************generate** uart clk***************************************
wire clk_bps; //clock bps
wire bps_start; //start generate bps signal
reg [31:0] clk_cnt; //count for bps clock
reg clk_bps_r; //clock bps reg /the rising edge of the clock with the data center
always @ (posedge clk) begin
if(rst)
clk_cnt <= 16'd0;
else if((clk_cnt == BPS_PARA) || !bps_start)
clk_cnt <= 16'd0;
else
clk_cnt <= clk_cnt + 16'd1;
end
always @ (posedge clk) begin
if(rst)
clk_bps_r <= 1'b0;
else if(clk_cnt == BPS_PARA_2)
clk_bps_r <= 1'b1;
else
clk_bps_r <= 1'b0;
end
assign clk_bps = clk_bps_r;
//---------------------------------------------------------------------------
//**************************************************Falling edge***********************************
reg rs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;
wire neg_rs232_rx;
always @ (posedge clk) begin
rs232_rx0 <= rs232_rx;
rs232_rx1 <= rs232_rx0;
rs232_rx2 <= rs232_rx1;
rs232_rx3 <= rs232_rx2;
end
assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0; //1 clock
//--------------------------------------------------------------------------------------------------
//******************************bps start*******************************************
wire num_flag;
reg bps;
reg bps_r;
wire rx_int;
always @ (*) begin
if(rst)
bps = 0;
else if(neg_rs232_rx)
bps = 1;
else if(num_flag)
bps = 0;
else
bps = bps_r;
end
always @ (posedge clk) begin
bps_r <= bps;
end
assign bps_start = bps_r;
assign rx_int = bps_r && clk_bps;
//--------------------------------------------------------------------------------------------------
//*****************************receiver count**************************************
(*keep = "true"*) reg [3:0] num;
(*keep = "true"*) reg [3:0] num_r;
always @ (*) begin
if(rst)
num = 0;
else if(rx_int)
num = num_r + 1;
else if(num_flag)
num = 0;
else
num = num_r;
end
always @ (posedge clk) begin
num_r <= num;
end
assign num_flag = (num_r == T_DATA_CNT) ? 1 : 0;
//-----------------------------------------------------------------------------
//********************************receive data**************************************
(*keep = "true"*) reg [7:0] rx_temp_data;
reg [7:0] rx_data_r;
reg rx_en_r;
reg parity_data;
reg parity_r;
wire parity_data_o;
assign parity_data_o = (PARITY == 1) ? ~parity_data : parity_data;
always @ (posedge clk) begin
if(rst) begin
rx_temp_data <= 8'd0;
parity_r <= 0;
parity_data <= 0;
end
else if(rx_int)
case (num)
4'd2 : begin rx_temp_data[0] <= rs232_rx; parity_data <= 0 ^rs232_rx; end
4'd3 : begin rx_temp_data[1] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd4 : begin rx_temp_data[2] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd5 : begin rx_temp_data[3] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd6 : begin rx_temp_data[4] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd7 : begin rx_temp_data[5] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd8 : begin rx_temp_data[6] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd9 : begin rx_temp_data[7] <= rs232_rx; parity_data <= parity_data ^rs232_rx; end
4'd10 : if(PARITY_EN == 1) parity_r <= rs232_rx;
default: ;
endcase
else
rx_temp_data <= rx_temp_data;
end
always @ (posedge clk) begin
if(rst) begin
rx_data_r <= 0;
rx_en_r <= 0;
end
else if(num_flag) begin
rx_data_r <= rx_temp_data;
rx_en_r <= 1;
end
else
rx_en_r <= 0;
end
assign rx_data = rx_data_r;
assign rx_en = rx_en_r;
always @ (posedge clk) begin
if(rst)
error_cnt <= 0;
else if(num_flag && PARITY_EN == 1) begin
if(parity_data_o == parity_r )
error_cnt <= error_cnt;
else
error_cnt <= error_cnt + 1;
end
else
error_cnt <= error_cnt;
end
//-----------------------------------------------------------------------------
endmodule
发送端代码:
//TX data
module uart_tx_module #(
parameter [31:0] SYSCLKFREQ = 50000000, //system clk
parameter [31:0] UARTATE = 115200, //uart bps
parameter [0:0] PARITY_EN = 0, //papity en?
parameter [0:0] PARITY = 0, //papity sel 0-even,1-odd
parameter [1:0] STOP_BIT = 0 //stop bit number 1 2
)(
input clk, // 50MHz
input rst, //rst -1
input [7:0] tx_data, //send data
input tx_en, //Send data en .if tx_en is 1. tx_data is vaild
output rs232_tx, // uart signal -tx
output tx_over //send data finish. one clock
);
localparam [31:0] BPS_PARA = SYSCLKFREQ/UARTATE; //bps count
localparam [31:0] BPS_PARA_2 = BPS_PARA/2; //half is bps count
localparam [4:0] T_DATA_CNT = 1 + 8 + PARITY_EN + STOP_BIT; //start + 8bit + papity_bit + stop_bit;
//************************generate** uart clk***************************************
wire clk_bps; //clock bps
wire bps_start; //start generate bps signal
reg [31:0] clk_cnt; //count for bps clock
reg clk_bps_r; //clock bps reg /the rising edge of the clock with the data center
always @ (posedge clk) begin
if(rst)
clk_cnt <= 16'd0;
else if((clk_cnt == BPS_PARA) || !bps_start)
clk_cnt <= 16'd0; //count ->0
else
clk_cnt <= clk_cnt + 16'd1; //count + 1
end
always @ (posedge clk) begin
if(rst)
clk_bps_r <= 1'b0;
else if(clk_cnt == BPS_PARA_2)
clk_bps_r <= 1'b1;
else
clk_bps_r <= 1'b0;
end
assign clk_bps = clk_bps_r;
//---------------------------------------------------------------------------
//*******************************start bps*******************************************
wire num_flag;
reg bps;
reg bps_r;
wire tx_int;
always @ (*) begin
if(rst)
bps = 0;
else if(tx_en)
bps = 1;
else if(num_flag)
bps = 0;
else
bps = bps_r;
end
always @ (posedge clk) begin
bps_r <= bps;
end
assign bps_start = bps_r;
assign tx_int = bps_r && clk_bps;
//--------------------------------------------------------------------------------------------------
//*****************************data count **************************************
(*keep = "true"*) reg [3:0] num;
(*keep = "true"*) reg [3:0] num_r;
always @ (*) begin
if(rst)
num = 0;
else if(tx_int)
num = num_r + 1;
else if(num_r == T_DATA_CNT)
num = 0;
else
num = num_r;
end
always @ (posedge clk) begin
num_r <= num;
end
assign num_flag = (num_r == T_DATA_CNT) ? 1 : 0;
//-----------------------------------------------------------------------------
//**************************************TX data***********************************
wire parity_data;
wire parity_data_o;
reg rs232_t;
assign parity_data = tx_data[0] ^ tx_data[1] ^ tx_data[2] ^ tx_data[3] ^ tx_data[4] ^ tx_data[5] ^ tx_data[6] ^ tx_data[7];
assign parity_data_o = (PARITY == 1) ? ~parity_data : parity_data;
always @ (posedge clk) begin
if(rst)
rs232_t <= 1;
else case(num)
4'd0 : rs232_t <= 1'b1;
4'd1 : rs232_t <= 1'b0;
4'd2 : rs232_t <= tx_data[0]; //bit1
4'd3 : rs232_t <= tx_data[1]; //bit2
4'd4 : rs232_t <= tx_data[2]; //bit3
4'd5 : rs232_t <= tx_data[3]; //bit4
4'd6 : rs232_t <= tx_data[4]; //bit5
4'd7 : rs232_t <= tx_data[5]; //bit6
4'd8 : rs232_t <= tx_data[6]; //bit7
4'd9 : rs232_t <= tx_data[7]; //bit7
4'd10 : if(PARITY_EN == 1) rs232_t <= parity_data_o;
else rs232_t <= 1'b1;
4'd11: rs232_t <= 1'b1;
4'd12: rs232_t <= 1'b1;
default: rs232_t <= 1'b1;
endcase
end
//---------------------------------------------------------------------------------
assign rs232_tx = rs232_t;
assign tx_over = num_flag;
endmodule