1、串口简介:
通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter , 简称UART。
UART是一种通用的数据通信协议,也是异步串行通信(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
包括RS232,RS499,RS423,RS422和RS485等接口标准规范和总线标准规范。
2、串口通信方式:(全双工通信)
缺点:传输距离近,传输速度慢;
优点:应用广泛,适配传感器
3、串口RS232接口:![](https://img-blog.csdnimg.cn/direct/5b45cc07c144428bb9f66a64347242d8.png)
补充:波特率:每秒中通过的码元个数,称为码元的传输速率,也称作波特率;
符号:Band 单位:Bps
比特率:每秒传输的比特数; 单位: bps
比特率 = 波特率 * 每个调制状态对应的二进制数
串口接收和发送1bit数据的时间,称之为一个波特,比如波特率为9600.那么其波特率为:
(1 / 9600) s,如果系统时钟是50Mhz, 那么其系统时钟是20ns,那么在9600的波特率下,一个波特等于 5208个系统时钟周期;
4、实践:在FPGA内部实现一个数据收发模块,在pc端用串口助手做一个串口收发回环检测;
4.1 模块设计
顶层:
接收子模块:(将接收的串行数据转为并行数据发给发送子模块)
发送子模块:(将接收的并行数据转为串行数据输出)
整体系统框图:
4.2 波形设计
4.2.1 补充知识:亚稳态
亚稳态形成原因:由于信号输入到寄存器的时候,建立时间和保持时间不满足我们的条件,会导致亚稳态的产生;
建立时间:Tsu :时钟信号上升沿到来之前,信号维持一定时间的稳定状态,这个信号稳定不变的最小时间称为建立时间;
保持时间:Th:触发器时钟上升沿到达以后,信号维持一定时间的稳定状态,这个信号稳定不变的最小时间称为保持时间;
寄存器延迟时间:Tco: 信号在寄存器内部传输的时间;
决断时间:Tmet:当信号震荡需要做出0/1判决的时间
亚稳态的消除:使用寄存器,因为每经过一个寄存器,决断时间就会缩短,多打几拍(经历多级寄存器),单bit信号一般为了减少亚稳态打两拍即可;
4.2.2 接收模块
4.2.3 发送模块
4.3 程序设计
4.3.1 接收模块
module uart_rx
#(
parameter UART_BAUD = 'd9600 ,
parameter CLK_FREQ = 'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output reg [7:0] po_data ,
output reg po_flag
);
parameter BAUD_CNT_MAX = CLK_FREQ / UART_BAUD; //baud-rate:9600; sys_clk:50MHz;(1/9600)/(1/50MHz)
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 ;
//Tapping the input signal:rx
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_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 sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_data <= 8'd0;
else if(rx_flag == 1'b1)
po_data <= rx_data;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
endmodule
仿真部分:tb_uart_rx:
`timescale 1ns/1ns
module tb_uart_rx();
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
wire [7:0] po_data ;
wire po_flag ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
rx <= 1'b1;
#20
sys_rst_n <= 1'b1;
end
//Analog sends data 8 times, respectively 0~7
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 sys_clk = ~sys_clk;
//use the task function to assign values to rx
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_BAUD (9600 ),
.CLK_FREQ (50_000_000)
)
uart_rx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.po_data (po_data ),
.po_flag (po_flag )
);
endmodule
4.3.2 发送模块
module uart_tx
#(
parameter UART_BAUD = 'd9600 ,
parameter CLK_FREQ = 'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data ,
input wire pi_flag ,
output reg tx
);
parameter BAUD_CNT_MAX = CLK_FREQ / UART_BAUD; //baud-rate:9600;
reg work_en ;
reg [15:0] baud_cnt ;
reg bit_flag ;
reg [3:0] bit_cnt ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(bit_cnt == 4'd9 && bit_flag == 1'b1)
work_en <= 1'b0;
else if(pi_flag == 1'b1)
work_en <= 1'b1;
else
work_en <= work_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 16'd0;
else if(work_en == 1'b0 || baud_cnt == BAUD_CNT_MAX - 1)
baud_cnt <= 16'd0;
else if(work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == 16'd1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'd0;
else if(bit_cnt == 4'd9 && bit_flag == 1'b1)
bit_cnt <= 4'd0;
else if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tx <= 1'b1;
else if(bit_flag == 1'b1)
case(bit_cnt)
0:tx <= 1'b0;
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'b1;
default:tx <= 1'b1;
endcase
endmodule
仿真部分:tb_uart_tx:
`timescale 1ns/1ns
module tb_uart_tx();
reg sys_clk ;
reg sys_rst_n ;
reg [7:0] pi_data ;
reg pi_flag ;
wire tx ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~ sys_clk;
initial
begin
pi_data <= 8'd0;
pi_flag <= 1'b0;
#200
//data 0
pi_data <= 8'd0;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 1
pi_data <= 8'd1;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 2
pi_data <= 8'd2;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 3
pi_data <= 8'd3;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 4
pi_data <= 8'd4;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 5
pi_data <= 8'd5;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 6
pi_data <= 8'd6;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208 * 10 * 20)
//data 7
pi_data <= 8'd7;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
end
uart_tx
#(
.UART_BPS(9600 ),
.CLK_FREQ(50_000_000)
)uart_tx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.pi_data (pi_data ),
.pi_flag (pi_flag ),
.tx (tx )
);
endmodule
4.3.3 顶层模块
module rs232
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output wire tx
);
parameter UART_BPS = 14'd9600; //比特率
parameter CLK_FREQ = 26'd50_000_000; //时钟频率
wire [7:0] rx_data;
wire rx_flag;
uart_tx
#(
.UART_BPS (UART_BPS),
.CLK_FREQ (CLK_FREQ)
)
uart_tx_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_data (rx_data),
.pi_flag (rx_flag),
.tx (tx)
);
uart_rx
#(
.UART_BAUD (UART_BPS),
.CLK_FREQ (CLK_FREQ)
)
uart_rx_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rx (rx),
.po_data (rx_data),
.po_flag (rx_flag)
);
endmodule
总体仿真
`timescale 1ns/1ns
module tb_rs232();
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
wire tx ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
rx <= 1'b1;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
initial
begin
#200 //idle time
rx_byte();
end
task rx_byte();
integer j;
for(j = 0; j < 8; j = j + 1)
rx_bit(j);
endtask
//use the task function to assign values to rx
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
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rx (rx),
.tx (tx)
);
endmodule