串行通信
串行通信是将数据分成一位一位的形式在一条传输线上逐个传输。根据发送方和接受方是否由同一时钟控制分为同步通信和异步通信。
常见的串行通信接口有:
串口通信
UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的传输器。在发送数据时将并行数据转换成串行数据来传输,在接收数据时将串行数据转换为并行数据接收,主要功能就是实现数据的串并转换。
当检测到电平拉低时标记为起始位,接着是8个(5、6、7个也行)数据位和校验位(奇校验保证1的个数为奇数,偶校验反之),到停止位时电平又被拉高,进入空闲状态。
串口通信的传输速率用波特率表示,即每秒传输的二进制数据的位数,单位bps,常用的波特率有9600、19200、38400、57600及115200。
串口通信的接口标准如下:
串口接收
输入信号有系统时钟、复位、uart_rxd,输出有uart_done、uart_data,其余为中间变量。
start_flag:检测uart_rxd的下降沿,标志起始位的到来
rx_flag:接收使能,为高电平时执行接收任务
clk_cnt:时钟计数,计到波特率周期CLK_SYS/BPS
rx_cnt:接收计数器,每个数据比特对应一个计数值
//串口发送模块
//设时钟频率为50MHz,波特率为115200,8位数据,无校验位
module uart_rx(
input clk,
input rst_n,
input uart_rxd,
output reg uart_done,
output reg [7:0] uart_data
);
parameter BPS = 115200;
parameter CLK_SYS = 50_000_000;
parameter BPS_CNT = CLK_SYS/BPS;
wire start_flag;
reg uart_rxd_d0;
reg uart_rxd_d1;
reg rx_flag;
reg [3:0] rx_cnt;
reg [15:0] clk_cnt;
reg [7:0] uart_data_reg;
//检测输入数据uart_rxd的下降沿,标志起始位的到来
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
//打两拍,防亚稳态,同步时钟
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uart_rxd_d0 <= 1;
uart_rxd_d1 <= 1;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//数据发送旗帜,高电平时进行数据发送
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rx_flag <= 0;
else if(start_flag)
rx_flag <= 1;
else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2 - 1'b1))
rx_flag <= 0;
else
rx_flag <= rx_flag;
end
//计数器,将时钟周期计数为波特率周期
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
clk_cnt <= 0;
else if(rx_flag) begin
if(clk_cnt < BPS_CNT - 1'b1)
clk_cnt <= clk_cnt + 1;
else
clk_cnt <= 0;
end
else
clk_cnt <= 0;
end
//波特率计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rx_cnt <= 0;
else if((clk_cnt == BPS_CNT - 1'b1) && (rx_flag))
rx_cnt <= rx_cnt + 1;
else if((rx_cnt == 9) && (clk_cnt == BPS/2))
rx_cnt <= 0;
else
rx_cnt <= rx_cnt;
end
//对输入数据uart_rxd寄存,并实现串转并
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
uart_data_reg <= 0;
else if((rx_flag) && (clk_cnt == BPS/2)) begin
/*
uart_data_reg <= {uart_data_reg[6:0],uart_rxd_d1}
*/
case(rx_cnt)
1:uart_data_reg[0] <= uart_rxd_d1;
2:uart_data_reg[1] <= uart_rxd_d1;
3:uart_data_reg[2] <= uart_rxd_d1;
4:uart_data_reg[3] <= uart_rxd_d1;
5:uart_data_reg[4] <= uart_rxd_d1;
6:uart_data_reg[5] <= uart_rxd_d1;
7:uart_data_reg[6] <= uart_rxd_d1;
8:uart_data_reg[7] <= uart_rxd_d1;
default:uart_data_reg <= uart_data_reg;
endcase
end
end
//输出模块,将寄存器中的数据输出
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uart_done <= 0;
uart_data <= 0;
end
else if(rx_cnt == 9) begin
uart_done <= 1;
uart_data <= uart_data_reg;
end
else begin
uart_done <= 0;
uart_data <= 0;
end
end
endmodule
串口发送
//串口接收模块
module uart_tx(
input clk,
input rst_n,
input uart_en,
input uart_din,
output reg uart_txd
);
parameter BPS = 115200;
parameter CLK_SYS = 50_000_000;
parameter BPS_CNT = CLK_SYS/BPS;
wire en_flag;
reg uart_en_d0;
reg uart_en_d1;
reg tx_flag;
reg [3:0] tx_cnt;
reg [15:0] clk_cnt;
reg [7:0] uart_din_reg;
assign en_flag = (~uart_en_d1) && uart_en_d0;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uart_en_d0 <= 0;
uart_en_d1 <= 0;
end
else begin
uart_en_d0 <= uart_en;
uart_en_d1 <= uart_en_d0;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
uart_din_reg <= 0;
else if(en_flag)
uart_din_reg <= uart_din;
else
uart_din_reg <= uart_din_reg;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
tx_flag <= 0;
else if(en_flag)
tx_flag <= 1;
else if((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT - BPS_CNT/10))
tx_flag <= 0;
else
tx_flag <= tx_flag;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
clk_cnt <= 0;
else if(tx_flag) begin
if(clk_cnt < BPS_CNT - 1'b1)
clk_cnt <= clk_cnt + 1;
else
clk_cnt <= 0;
end
else
clk_cnt <= 0;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
tx_cnt <= 0;
else if((clk_cnt == BPS_CNT - 1'b1) && (tx_flag))
tx_cnt <= tx_cnt + 1;
else if((tx_cnt == 9) && (clk_cnt == BPS_CNT - BPS_CNT/10))
tx_cnt <= 0;
else
tx_cnt <= tx_cnt;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
uart_txd <= 1;
else if(tx_flag) begin
case(tx_cnt)
0:uart_txd <= 0;
1:uart_txd <= uart_din_reg[0];
2:uart_txd <= uart_din_reg[1];
3:uart_txd <= uart_din_reg[2];
4:uart_txd <= uart_din_reg[3];
5:uart_txd <= uart_din_reg[4];
6:uart_txd <= uart_din_reg[5];
7:uart_txd <= uart_din_reg[6];
8:uart_txd <= uart_din_reg[7];
9:uart_txd <= 1;
endcase
end
end
endmodule