提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
本文章主要讲述了RS232串口原理,代码实现以及上板情况。
一、串口原理
1.串口简介
通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter,简称UART。
UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。包括RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。
2.串口通信方式
tx:发送数据端口线;
rx:接收数据端口线。
tx和rx可同时发送接收数据,实现全双工通信。
PC端通过tx发送8bit数据,FPGA通过rx一位一位进行接收,从最低位到最高位,最后在FPGA形成8位数据;从FPGA到PC端也是如此传输。
二、RS232串口设计
RS232串口示意图如图2所示。3个输入端,分别接收时钟信号、复位信号和接收信号;1个输出端,输出信号。
现分别对接收模块和发送模块进行设计。
如图3,分别为接收模块和发送模块。接收模块中,串行数据rx转换成并行数据数据Po_data[7:0],还生成了数据有效标志位Po_flag。而在发送模块,这些信号连同时钟信号和复位信号作为输入端,输出信号rx。
1.接收模块设计
如图4为接收模块时序设计图。3个输入端分别为时钟信号、复位信号和8位串行的传输信号,用橙色方块标记;红色方块是接收模块的输出信号,并行数据Po_data[7:0]和数据标志位Po_flag。
黄色方块Rx_reg1、Rx_reg2、Rx_reg3为3个寄存器,用来传递串行信号,这里用3个寄存器是为了减少亚稳态的危害。
Start_flag是起始标志信号。
Work_en为使能信号,确定传输范围。
baud_cnt:计数器。这里波特率为9600,因此周期数=1/9600*10^9ns/20ns=5208。
Bit_flag:数据提取的标志信号。
Bit_cnt:比特计数器。对Rx_reg3的8个数据位提取。
rx_data:对传输信号进行拼接。
rx_flag:拼接完成标志位。
2.接收模块Verilog HDL程序设计
现对图4的时序设计图编写Verilog HDL程序uart_rx。
module uart_rx
#(
parameter Uart_bps = 'd9600,
parameter Clk_freq = 'd50_000_000
) //设置参数(波特率9600,时钟晶振频率50MHz)
(
input wire clk,
input wire rst_n,
input wire rx,
output reg [7:0] po_data,
output reg po_flag
);
parameter Baud_cnt_max = Clk_freq / Uart_bps; //设置计数器参数
reg rx_reg1;
reg rx_reg2;
reg rx_reg3;
reg start_flag;
reg work_en;
reg [15:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg [7:0] rx_data;
reg rx_flag;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
start_flag <= 1'b0;
else if((rx_reg3 == 1'b1) && (rx_reg2 == 1'b0) && (work_en ==1'b0))
start_flag <= 1'b1;
else
start_flag <= 1'b0;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
work_en <= 1'b0;
else if(start_flag == 1'b1)
work_en <= 1'b1;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
work_en <= 1'b0;
else
work_en <= work_en;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
baud_cnt <= 16'd0;
else if((baud_cnt == Baud_cnt_max - 1) || (work_en == 1'b0))
baud_cnt <= 16'd0;
else
baud_cnt <= baud_cnt + 1'b1;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == Baud_cnt_max/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
bit_cnt <= 4'd0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
bit_cnt <= 4'd0;
else if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
rx_data <= 8'b0;
else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {rx_reg3,rx_data[7:1]};
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
rx_flag <= 1'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
po_data <= 1'b0;
else if(rx_flag == 1'b1)
po_data <= rx_data;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
endmodule
进行仿真验证。编写仿真文件uart_rx_tb
`timescale 1ns / 1ns
module uart_rx_tb();
reg clk;
reg rst_n;
reg rx;
wire [7:0] po_data;
wire po_flag;
initial
begin
clk = 1'b1;
rst_n <= 1'b0;
rx <= 1'b1;
#20
rst_n <= 1'b1;
end
initial
begin
#200
rx_bit(8'd0);
rx_bit(8'd1);
rx_bit(8'd2);
rx_bit(8'd3);
rx_bit(8'd4);
rx_bit(8'd5);
rx_bit(8'd6);
rx_bit(8'd7);
end
always #10 clk = ~clk;
task rx_bit
(
input [7:0] data
);
integer i;
for(i = 0; i < 10; i = i + 1)
begin
case(i)
0:rx <= 1'b0;
1:rx <= data[0];
2:rx <= data[1];
3:rx <= data[2];
4:rx <= data[3];
5:rx <= data[4];
6:rx <= data[5];
7:rx <= data[6];
8:rx <= data[7];
9:rx <= 1'b1;
endcase
#(5208*20);
end
endtask
uart_rx
#(
.Uart_bps (9600),
.Clk_freq (50_000_000)
)
uart_rx_inst
(
.clk (clk ),
.rst_n (rst_n),
.rx (rx),
.po_data (po_data),
.po_flag (po_flag)
);
endmodule
模拟接收0-7共8位数据,仿真图如下图所示:
3.发送模块设计
4.接收模块Verilog HDL程序设计
对图5的时序设计图编写Verilog HDL程序uart_tx。
module uart_tx
#(
parameter uart_bps = 9600,
parameter clk_freq = 50_000_000
)
(
input wire clk,
input wire rst_n,
input wire [7:0] Pi_data,
input wire Pi_flag,
output reg tx
);
parameter baud_max_cnt = clk_freq / uart_bps;
reg work_en;
reg [15:0] baud_cnt;
reg bit_flag;
reg [8:0] bit_cnt;
always@(posedge clk or negedge rst_n)
if(rst_n == 0)
work_en <= 0;
else if(Pi_flag == 1)
work_en <= 1;
else if((bit_flag == 1) && (bit_cnt == 9))
work_en <= 0;
always@(posedge clk or negedge rst_n)
if(rst_n == 0)
baud_cnt <= 0;
else if((work_en == 0)||(baud_cnt == baud_max_cnt - 1))
baud_cnt <= 0;
else if(work_en <= 1)
baud_cnt <= baud_cnt + 1;
always@(posedge clk or negedge rst_n)
if(rst_n == 0)
bit_flag <= 0;
else if(baud_cnt == 1)
bit_flag <= 1;
else
bit_flag <= 0;
always@(posedge clk or negedge rst_n)
if(rst_n == 0)
bit_cnt <= 0;
else if((bit_cnt == 9)&&(bit_flag == 1))
bit_cnt <= 0;
else if(bit_flag == 1)
bit_cnt <= bit_cnt + 1;
always@(posedge clk or negedge rst_n)
if(rst_n == 0)
tx <= 1;
else if(bit_flag == 1)
case(bit_cnt)
0 :tx <= 0;
1 :tx <= Pi_data[0];
2 :tx <= Pi_data[1];
3 :tx <= Pi_data[2];
4 :tx <= Pi_data[3];
5 :tx <= Pi_data[4];
6 :tx <= Pi_data[5];
7 :tx <= Pi_data[6];
8 :tx <= Pi_data[7];
9 :tx <= 1;
default: tx <= 1;
endcase
endmodule
进行仿真验证。编写仿真文件uart_rx_tb
`timescale 1ns / 1ns
module uart_tx_tb();
reg clk;
reg rst_n;
reg [7:0] Pi_data;
reg Pi_flag;
wire tx;
initial
begin
clk = 1;
rst_n <= 0;
#20
rst_n <= 1;
end
always #10 clk = ~clk;
initial
begin
Pi_data <= 0;
Pi_flag <= 0;
#200
//数据0
Pi_data <= 0;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据1
Pi_data <= 1;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据2
Pi_data <= 2;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据3
Pi_data <= 3;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据4
Pi_data <= 4;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据5
Pi_data <= 5;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据6
Pi_data <= 6;
Pi_flag <= 1;
#20
Pi_flag <= 0;
#(5208*10*20)
//数据7
Pi_data <= 7;
Pi_flag <= 1;
#20
Pi_flag <= 0;
end
uart_tx
#(
.uart_bps (9600),
.clk_freq (50_000_000)
)
uart_tx_inst
(
.clk(clk),
.rst_n(rst_n),
.Pi_data(Pi_data),
.Pi_flag(Pi_flag),
.tx(tx)
);
endmodule
模拟发送0-7共8位数据,仿真图如下图所示:
5.串口设计
设计好发送模块和接收模块后,对整体进行设计。
代码设计(对端口进行例化)
module rs232
(
input wire clk,
input wire rst_n,
input wire rx,
output wire tx
);
wire [7:0] rx_data;
wire rx_flag;
uart_rx
#(
.uart_bps (9600),
.clk_freq (50_000_000)
)
uart_rx_inst
(
.clk (clk ),
.rst_n (rst_n),
.rx (rx),
.po_data (rx_data),
.po_flag (rx_flag)
);
uart_tx
#(
.Uart_bps (9600),
.Clk_freq (50_000_000)
)
uart_tx_inst
(
.clk(clk),
.rst_n(rst_n),
.Pi_data(rx_data),
.Pi_flag(rx_flag),
.tx(tx)
);
endmodule
仿真验证:
`timescale 1ns/1ns
module rs232_tb();
reg clk;
reg rst_n;
reg rx;
wire tx;
initial
begin
clk = 1;
rst_n <= 0;
rx <= 1;
#20
rst_n <= 1;
end
always #10 clk = ~clk;
initial
begin
#200
rx_byte();
end
task rx_byte();
integer j;
for(j = 0; j < 8; j = j + 1)
rx_bit(j);
endtask
task rx_bit
(
input [7:0] data
);
integer i;
for(i = 0; i < 10; i = i + 1)
begin
case(i)
0:rx <= 1'b0;
1:rx <= data[0];
2:rx <= data[1];
3:rx <= data[2];
4:rx <= data[3];
5:rx <= data[4];
6:rx <= data[5];
7:rx <= data[6];
8:rx <= data[7];
9:rx <= 1'b1;
endcase
#(5208*20);
end
endtask
rs232 rs232_inst
(
.clk(clk),
.rst_n(rst_n),
.rx(rx),
.tx(tx)
);
endmodule