串口通信虽然是一种比较早就问世的通讯方式,但是在工业控制中应用比较广泛,这里提供一种FPGA PL端的串口通信逻辑。
UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。
UART在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位。
其中,起始位标志着一帧数据的开始,必须要有;停止位标志着一帧数据的结束,可以选择,一般选择一位停止位;数据位是一帧数据中的有效数据,一般是八位;校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错,一般不用;停止位也是必须有的,发送结束后拉高电平。
串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bps(位/秒),常用的波特率有9600、19200、38400、57600以及115200 等。
这里的FPGA串口发送逻辑由三个基本模块构成:时钟模块、发送模块和接收模块。
时钟模块按照要求的通讯波特率,根据板载晶振,产生采样时钟,为串口收发提供时钟;接收模块在时钟模块的驱动下按照固定频率读取接收引脚的电平状态,当读到低电平,也就是起始位的时候,开始连续读八次,然后转入等待读取状态;发送模块在发送信号以后,从发送引脚发出起始位+8个数据位+一个停止位。
时钟模块:
module clk_div(
input sys_clk,
input sys_rst_n,
output clk_out
);
reg clk_out_reg;
parameter BAUD_RATE = 115200;
parameter CRYSTAL = 50_000_000;
parameter CLK_DIV = CRYSTAL/BAUD_RATE;
reg [15:0] counter;
always@(posedge sys_clk ,negedge sys_rst_n)begin
if(!sys_rst_n)
counter <= 16'd0;
else begin
if(counter == CLK_DIV) begin
counter <= 16'd0;
clk_out_reg <= 1'd1;
end
else begin
counter <= counter +1'd1;
clk_out_reg <= 1'd0;
end
end
end
assign clk_out = clk_out_reg;
endmodule
接收模块:
module uart_rx(
input uart_clk,
input sys_rst_n,
input uart_rx,
output receive_end,
output [7:0] receive_data
);
reg [7:0] data_i;
localparam S0 = 2'b00; //空闲状态
localparam S1 = 2'b01; //接收状态
localparam S2 = 2'b10; //接收完成
reg [1:0] cur_st,nxt_st;
reg [3:0] count;
always@(posedge uart_clk,negedge sys_rst_n)begin
if(!sys_rst_n)
cur_st <= S0;
else begin
cur_st <= nxt_st;
end
end
always@(*)begin
nxt_st = cur_st;
case(cur_st)
S0:if(!uart_rx)
nxt_st = S1;
S1:if(count == 7)
nxt_st = S2;
S2:
nxt_st = S0;
default:nxt_st = S0;
endcase
end
always@(posedge uart_clk,negedge sys_rst_n)begin
if(!sys_rst_n)
count <= 4'd0;
else begin
if(cur_st == S1)
count <= count +1'b1;
else
count <= 4'd0;
end
end
always@(posedge uart_clk,negedge sys_rst_n)begin
if(!sys_rst_n)
data_i <= 8'd0;
else
if(cur_st == S1)begin
data_i[6:0] <= data_i[7:1];
data_i[7] <= uart_rx;
end
end
assign receive_end = (cur_st == S2)?1:0;
assign receive_data = data_i;
endmodule
发送模块:
module uart_tx(
input uart_clk,
input sys_rst_n,
input [7:0] data_in,
input send_signal,
output uart_tx
);
reg [1:0] cur_st,nxt_st;
localparam S0 = 2'b00; //空闲状态
localparam S1 = 2'b01; //发送状态
localparam S2 = 2'b10; //发送完成
reg [3:0] count;
reg [7:0] data_temp;
reg uart_tx_reg;
always@(posedge uart_clk,negedge sys_rst_n)begin
if(!sys_rst_n)
cur_st <= S0;
else begin
cur_st <= nxt_st;
end
end
always@(*)begin
nxt_st = cur_st;
case(cur_st)
S0:if(send_signal)
nxt_st = S1;
S1:
nxt_st = S2;
S2:if(count == 7)
nxt_st = S0;
default:nxt_st = S0;
endcase
end
always@(posedge uart_clk,negedge sys_rst_n)begin
if(!sys_rst_n)
count <= 4'd0;
else begin
if(cur_st == S2)
count <= count +1'b1;
else
count <= 4'd0;
end
end
always@(posedge uart_clk,negedge sys_rst_n)begin
if(!sys_rst_n)
data_temp <= 8'd0;
else
if(cur_st == S1)begin
uart_tx_reg <= 1'b0;
data_temp <= data_in;
end
else if(cur_st == S2)begin
uart_tx_reg <= data_temp[0];
data_temp[6:0] <= data_temp[7:1];
end
else if(cur_st == S0)
uart_tx_reg <= 1'b1;
end
assign uart_tx = uart_tx_reg;
endmodule
最后是顶层模块:
module uart_top(
input sys_clk,
input sys_rst_n,
input uart_rx,
output uart_tx
);
wire uart_clk;
wire signal;
wire [7:0] data;
clk_div u_clk_div(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.clk_out (uart_clk)
);
uart_rx u_uart_rx(
.uart_clk (uart_clk),
.sys_rst_n (sys_rst_n),
.uart_rx (uart_rx),
.receive_end (signal),
.receive_data (data)
);
uart_tx u_uart_tx(
.uart_clk (uart_clk),
.sys_rst_n (sys_rst_n),
.data_in (data),
.send_signal (signal),
.uart_tx (uart_tx)
);
endmodule
补充状态转移图: