1. 串口简介
通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter,简称UART
UART
UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的数据转换成并行数据
UART是异步串行通信口,SPI是同步的,双方约定好使用频率一致的时钟,主机发送时钟
UART中,数据的收发都有独立的端口,所以它可以实现全双工通信
RS232传输距离比较近,传输速率也比较慢,数据线只有两条,可以节省IO口
2. 串口RS232接口
是UART串口的一种,没有时钟线只有两个数据线
注意:两个设备间的TXD和RXD应交叉相连
串口数据的发送和接收都是基于帧结构的,起始位+数据位+停止位,共10bit,空闲状态下rx和tx都保持高电平
波特率:来波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud),Bps
比特率:比特率是每秒传输的比特数。单位为比特(bps位/秒)
波特率源与比特率的关系为:比特率=波特率 * 单个调制状态对应的二进制位数
串口常用波特率有4800、9600、115200
9600Bps的串口比特率就是9600*1bps,串口发送或接收1bit数据的时间称为1个波特,也就是1/9600s。clk = 50MHz,T = 20ns,9600Bps下,传输一个波特需要的时钟个数,cnt = (1 * 10^9)ns / 9600 / 20ns 约等于5208,相当于每个bit的传输间隔需要50MHz时钟下的5208个clk
3. 代码实现
实现50MHz下9600波特率传输
波特率为9600Baud
一秒传输9600个波特
每个波特10bit数据,1bit起始位 + 8bit数据位 + 1bit结束位
每个码元传输需要的clk数 = ((1/9600)*10^9ns) / 20ns≈ 5208
串口传输,每个码元也就是一个bit,也可以理解为传递每个bit需要的clk数
UART接收模块
将上位机发送过来的串行数据进行接收,转换成并行数据
module uart_rx
#(
parameter UART_BPS = '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 = 5208; // 9600Bps (1/9600s)/(1/50M) = F/Bps = 50M / 9600
localparam 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 ; // 计数1个码元需要的clk,0~8207
reg bit_flag ; // 每bit数据稳定点拉高一拍
reg [ 3: 0] bit_cnt ; // 10bit数据计数器
reg [ 7: 0] rx_data ;
reg rx_flag ;
always @ (posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
begin
rx_reg1 <= 1'b1; // 空闲状态为高
rx_reg2 <= 1'b1;
rx_reg3 <= 1'b1;
end
else
begin
rx_reg1 <= rx;
rx_reg2 <= rx_reg1;
rx_reg3 <= rx_reg2;
end
// start_flag:检测下降沿
always @ (posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
start_flag <= 1'b0;
else if (~rx_reg2 && rx_reg3 && work_en == 1'b0) // 检测下降沿,并且现在不在数据采集范围内
start_flag <= 1'b1;
else
start_flag <= 1'b0;
// work_en
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;
// baud_cnt
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'b1) ||(work_en == 1'b0) )
baud_cnt <= 160'd0;
else
baud_cnt <= baud_cnt + 1'b1;
// bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,拉高一个标志信号表示数据可以被采样
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'b1) // 数据稳定的最中间位置
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
// bit_cnt
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;
else
bit_cnt <= bit_cnt;
// rx_data
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]};
else
rx_data <= rx_data;
// rx_flag
always @ (posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if ((bit_flag == 1'b1) && (bit_cnt == 4'd8))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
// po_data
always @ (posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
po_data <= 8'b0;
else if (rx_flag == 1'b1)
po_data <= rx_data;
// po_flag
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
`timescale 1ns/1ns
module uart_rx_tb ();
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
wire [ 7: 0] po_data ;
wire po_flag ;
uart_rx
#(
.UART_BPS ('d9600 ),
.CLK_FREQ ('d50_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 )
);
always # 10 sys_clk = ~sys_clk;
initial begin
sys_clk = 1;
sys_rst_n = 0;
rx = 1;
#10
sys_rst_n = 1;
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);
rx_bit(8'b1100_1010);
end
// 任务函数
task rx_bit ;
input [ 7: 0] data;
integer i;
begin
for (i = 0; i < 10 ; i = i + 1) begin // 这个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
end
endtask
endmodule
UART发送模块
将FPGA内部的数据以固定的波特率,并转串,包装成数据帧,发送给上位机
module uart_tx
#(
parameter UART_BPS = '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
);
localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;
reg work_en ;
reg [15: 0] baud_cnt ;
reg bit_flag ;
reg [ 3: 0] bit_cnt ;
// work_en
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;
// baud_cnt
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'b1))
baud_cnt <= 16'd0;
else if (work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
// bit_flag
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周期,相邻脉冲间隔为一个波特
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
// bit_cnt
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 ((work_en == 1'b1) && (bit_flag == 1'b1))
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
// tx
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
顶层模块
模拟接收串行数据8’b1100_1010,随后逐比特发送