232串口协议是通信接口中最简单的协议之一,使用两根线(一根为接收数据线,一根为发送数据线)就能完成数据传输。数据在这两根线上串行传输。
其协议原理如下:
空闲状态时传输线被置为高电平。开始传输数据的时候要先给一个开始标志,此时将传输线电平置低。然后由低位到高位依次传输数据。数据传输完成后,要给一个停止标志,此时传输线必须保持一段时间的高电平然后再进入空闲状态。
串口传输时需要设置波特率,波特率决定了数据传输的速度,波特率越高数据传的越快。一般常用的波特率有9600bps 19200bps 38400bps 115200bps等。如上图所示,在9600bps波特率情况下,每个状态要保持1/9600s。
串口接收代码:
// Company:
// Engineer: Abshdbeh
//
// Create Date:
// Design Name:
// Module Name:
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_rx
#(parameter CLK_FREQ='d100_000_000,
BAUD_FREQ='d9600)
(
input clk,
input rst,
input rxd,
output reg [7:0] rx_data,
output reg rx_done
);
parameter MAX = CLK_FREQ/BAUD_FREQ;
parameter IDLE = 4'd0 ,
START = 4'd1 ,
RD0 = 4'd2 ,
RD1 = 4'd3 ,
RD2 = 4'd4 ,
RD3 = 4'd5 ,
RD4 = 4'd6 ,
RD5 = 4'd7 ,
RD6 = 4'd8 ,
RD7 = 4'd9 ,
END = 4'd10 ;
reg rxd0 ;
reg rxd1 ;
reg [3:0] state ;
reg flag ;
reg [13:0] baud_cnt ;
reg rd_flag ;
reg clr_flag ;
initial
begin
state=IDLE;
end
always@(posedge clk or negedge rst)
if(!rst)
rxd0 <= 1'b1;
else
rxd0 <= rxd;
always@(posedge clk or negedge rst)
if(!rst)
rxd1 <= 1'b1;
else
rxd1 <= rxd0;
always@(posedge clk or negedge rst)
if(!rst)
flag <= 1'b0;
else if(baud_cnt==MAX-2)
flag <= 1'b1;
else
flag <= 1'b0;
always@(posedge clk or negedge rst)
if(!rst)
baud_cnt <= 14'd0;
else if(state==IDLE)
baud_cnt <= 14'd0;
else if(baud_cnt==MAX-1)
baud_cnt <= 14'd0;
else
baud_cnt <= baud_cnt+1'b1;
always@(posedge clk or negedge rst)
if(!rst)
state <= IDLE;
else
case(state)
IDLE:
if(!rxd1)
state <= START;
START:
if(flag)
state <= RD0;
RD0:
if(flag)
state <= RD1;
RD1:
if(flag)
state <= RD2;
RD2:
if(flag)
state <= RD3;
RD3:
if(flag)
state <= RD4;
RD4:
if(flag)
state <= RD5;
RD5:
if(flag)
state <= RD6;
RD6:
if(flag)
state <= RD7;
RD7:
if(flag)
state <= END;
END:
if(flag)
state <= IDLE;
default: state <= IDLE;
endcase
always@(posedge clk or negedge rst)
if(!rst)
rd_flag <= 1'b0;
else
case(state)
RD0,RD1,RD2,RD3,RD4,RD5,RD6,RD7:
if(baud_cnt==((MAX/2)-1))
rd_flag <= 1'b1;
else
rd_flag <= 1'b0;
default: rd_flag <= 1'b0;
endcase
always@(posedge clk or negedge rst)
if(!rst)
rx_data <= 8'd0;
else if(rd_flag)
case(state)
RD0: rx_data[0] <= rxd1;
RD1: rx_data[1] <= rxd1;
RD2: rx_data[2] <= rxd1;
RD3: rx_data[3] <= rxd1;
RD4: rx_data[4] <= rxd1;
RD5: rx_data[5] <= rxd1;
RD6: rx_data[6] <= rxd1;
RD7: rx_data[7] <= rxd1;
default: rx_data <= 8'd0;
endcase
else if(clr_flag)
rx_data <= 8'd0;
always@(posedge clk or negedge rst)
if(!rst)
rx_done <= 1'b0;
else if(state==END && baud_cnt==14'd1)
rx_done <= 1'b1;
else
rx_done <= 1'b0;
always@(posedge clk or negedge rst)
if(!rst)
clr_flag <= 1'b0;
else if(state==END && baud_cnt==((MAX/2)-1))
clr_flag <= 1'b1;
else
clr_flag <= 1'b0;
endmodule
串口发送代码:
// Company:
// Engineer: Abshdbeh
//
// Create Date:
// Design Name:
// Module Name:
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_tx
#(parameter CLK_FREQ='d100_000_000,
BAUD_RATE='d9600,
BIT_NUM='d64)
(
input clk,
input rst,
input [BIT_NUM-1:0] tx_data,
input tx_start,
output reg txd,
output reg tx_done
);
localparam BAUD_CNT_MAX=CLK_FREQ/BAUD_RATE,
BYTE_CNT_MAX=BIT_NUM/8;
localparam IDLE = 4'd0,
START = 4'd1,
TX0 = 4'd2,
TX1 = 4'd3,
TX2 = 4'd4,
TX3 = 4'd5,
TX4 = 4'd6,
TX5 = 4'd7,
TX6 = 4'd8,
TX7 = 4'd9,
END = 4'd10;
reg [13:0] baud_cnt;
reg [4:0] byte_cnt;
reg [3:0] state;
reg turn_flag;
reg byte_flag;
reg [7:0] date_temp;
initial
begin
state=IDLE;
date_temp=0;
txd=1;
byte_cnt=0;
end
always@(posedge clk or negedge rst)
if(!rst)
state <= IDLE;
else
case(state)
IDLE: if(tx_start)
state <= START;
START: if(turn_flag)
state <= TX0;
TX0: if(turn_flag)
state <= TX1;
TX1: if(turn_flag)
state <= TX2;
TX2: if(turn_flag)
state <= TX3;
TX3: if(turn_flag)
state <= TX4;
TX4: if(turn_flag)
state <= TX5;
TX5: if(turn_flag)
state <= TX6;
TX6: if(turn_flag)
state <= TX7;
TX7: if(turn_flag)
state <= END;
END: begin
if(tx_done)
state <= IDLE;
else if(turn_flag)
state <= START;
end
default: state <= IDLE;
endcase
always@(posedge clk or negedge rst)
if(!rst)
turn_flag <= 1'b0;
else
case(state)
START,TX0,TX1,TX2,TX3,TX4,TX5,TX6,TX7,END:
if(baud_cnt==BAUD_CNT_MAX-2)
turn_flag <= 1'b1;
else
turn_flag <= 1'b0;
default: turn_flag <= 1'b0;
endcase
always@(posedge clk or negedge rst)
if(!rst)
baud_cnt <= 14'd0;
else
case(state)
START,TX0,TX1,TX2,TX3,TX4,TX5,TX6,TX7,END:
if(baud_cnt==BAUD_CNT_MAX-1)
baud_cnt <= 14'd0;
else
baud_cnt <= baud_cnt+1'b1;
default: baud_cnt <= 14'd0;
endcase
always@(posedge clk or negedge rst)
if(!rst)
byte_flag <= 1'b0;
else
case(state)
END:
if(baud_cnt==BAUD_CNT_MAX-2)
byte_flag <= 1'b1;
else
byte_flag <= 1'b0;
default: byte_flag<= 1'b0;
endcase
always@(posedge clk or negedge rst)
if(!rst)
byte_cnt <= 5'd0;
else
case(state)
END:
if(tx_done)
byte_cnt <= 5'd0;
else if(byte_flag)
byte_cnt <= byte_cnt+1'b1;
default: byte_cnt <= byte_cnt;
endcase
always@(*)
date_temp <= tx_data[(byte_cnt*8)+:8];
always@(posedge clk or negedge rst)
if(!rst)
txd <= 1'b1;
else
case(state)
IDLE,END: txd <= 1'b1;
START: txd <= 1'b0;
TX0: txd <= date_temp[0];
TX1: txd <= date_temp[1];
TX2: txd <= date_temp[2];
TX3: txd <= date_temp[3];
TX4: txd <= date_temp[4];
TX5: txd <= date_temp[5];
TX6: txd <= date_temp[6];
TX7: txd <= date_temp[7];
default: txd <= 1'b1;
endcase
always@(posedge clk or negedge rst)
if(!rst)
tx_done <= 1'b0;
else if((state==END) && (byte_cnt==BYTE_CNT_MAX-1) && (baud_cnt==BAUD_CNT_MAX-2))
tx_done <= 1'b1;
else
tx_done <= 1'b0;
endmodule
串口环回顶层代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer: Abshdbeh
//
// Create Date:
// Design Name:
// Module Name: uart_loop
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_loop(
input clk,
input rst,
input rxd,
output txd
);
wire [7:0] rx_data;
wire rx_done;
uart_rx
#(.CLK_FREQ ('d100_000_000 ),
.BAUD_FREQ ('d9600 ))
uart_rx_inst
(
.clk (clk),
.rst (rst),
.rxd (rxd ),
.rx_data (rx_data),
.rx_done (rx_done)
);
wire tx_done;
reg [7:0] tx_data;
reg tx_start;
always@(posedge clk or negedge rst)
if(!rst)
tx_start <= 1'b0;
else if(rx_done==1'b1)
tx_start <= 1'b1;
else
tx_start <= 1'b0;
always@(posedge clk or negedge rst)
if(!rst)
tx_data <= 'd0;
else if(rx_done==1'b1)
tx_data <= rx_data;
uart_tx
#(.CLK_FREQ ('d100_000_000 ),
.BAUD_RATE ('d9600 ),
.BIT_NUM ('d8 ))
uart_tx_inst
(
.clk (clk),
.rst (rst),
.tx_data (tx_data),
.tx_start (tx_start),
.txd (txd),
.tx_done (tx_done)
);
endmodule
将程序下载到板子上,打开串口助手,进行测试,结果如下:
注意:要勾选十六进制显示和十六进制发送,否则不会显示结果