FPGA学习笔记02——串口通信

串行通信

串行通信是将数据分成一位一位的形式在一条传输线上逐个传输。根据发送方和接受方是否由同一时钟控制分为同步通信和异步通信。
常见的串行通信接口有:
在这里插入图片描述

串口通信

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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值