FPGA学习专栏-串口通信(xinlinx)

FPGA学习专栏-串口通信

本系列文章基于开发板黑金A309,FPGA芯片为Xilinx公司的spartan6,本系列文章记录FPGA学习历程。



一、串口通信原理

通用异步收发传输器,通常称为UART。本文采用的是RS232接口标准。串口通信原理在网上很容易搜到,串口通信是双机通信中最先学到的通信方式,是异步串行通信。
串口通信的连接图如下所示:
在这里插入图片描述
串行通信中消息桢组成为:
在这里插入图片描述
本实验传输不用校验位。
串行通信中,波特率非常重要,是数据能否正确传输的重要保障,波特率即一秒钟传输多少字。本文选用波特率为115200。
波特率相当于异步串行通信中的时基单元,所以非常重要。


二、硬件设计

硬件上,AX309采用了USB转串口芯片CP2102。
在这里插入图片描述

三、verilog代码编写

1.发送模块

采用状态转移图方式编写代码,发送模块的状态转换图如下所示:
在这里插入图片描述

发送模块的方框图如下:
在这里插入图片描述

module uart_tx
#(
		parameter	CLK_FRE		= 50,
		parameter	BAUD_RATE	= 115200
)
(
	input		clk,
	input		rst,
	input[7:0]	tx_data,//发送数据
	input		tx_data_valid,//发送数据有效标志
	
	output		tx_pin,
	output	reg	tx_data_ready
);
localparam		CYCLE = CLK_FRE * 1000000/BAUD_RATE;

localparam		S_IDLE		= 1;
localparam		S_START		= 2;
localparam		S_SEND_BYTE	= 3;
localparam		S_STOP		= 4;

reg[2:0]		state;
reg[2:0]		next_state;
reg[15:0]		cycle_cnt;
reg[2:0]		bit_cnt;
reg[7:0]		tx_data_latch;
reg				tx_reg;

assign	tx_pin = tx_reg;

always@(posedge clk or negedge rst)
begin
	if(rst == 1'b0)
		state <= S_IDLE;
	else
		state <= next_state;
end
//状态转移
always@(*)
begin
	case(state)
		S_IDLE:
			if(tx_data_valid == 1'b1)
				next_state <= S_START;
			else
				next_state <= S_IDLE;
		
		S_START:
			if(cycle_cnt == CYCLE-1)
				next_state <= S_SEND_BYTE;
			else
				next_state <= S_START;
		S_SEND_BYTE:
			if(cycle_cnt == CYCLE-1 && bit_cnt == 3'd7)
				next_state <= S_STOP;
			else
				next_state <= S_SEND_BYTE;
		S_STOP:
			if(cycle_cnt == CYCLE-1)
				next_state <= S_IDLE;
			else
				next_state <= S_STOP;
		default:next_state <= S_IDLE;
	endcase
end
//发送标志位
always@(posedge clk or negedge rst)
begin
	if(rst == 1'b0)
		tx_data_ready <= 1'b0;
	else if(state == S_IDLE)
		if(tx_data_valid == 1'b1)
			tx_data_ready <= 1'b0;
		else
			tx_data_ready <= 1'b1;
	else if(state == S_STOP && cycle_cnt == CYCLE-1)
		tx_data_ready <= 1'b1;
end

always@(posedge clk or negedge rst)
begin
	if(rst == 1'b0)
		tx_data_latch <= 8'd0;
	else if(state == S_IDLE && tx_data_valid == 1'b1)
		tx_data_latch <= tx_data;
end


//数据输出
always@(posedge clk or negedge rst)
begin
	if(rst == 1'b0)
		tx_reg <= 1'b0;
	else 
		case(state)
			S_IDLE,S_STOP:
					tx_reg <= 1'b1;
			S_START:
					tx_reg <= 1'b0;
			S_SEND_BYTE:
					tx_reg <= tx_data_latch[bit_cnt];
			default:
				tx_reg <= 1'b1;
		endcase
end
//比特位计数
always@(posedge clk or negedge rst)
begin 
if(rst == 1'b0)
	bit_cnt <= 3'd0;
else if(state == S_SEND_BYTE)
	if(cycle_cnt == CYCLE-1)
		bit_cnt <= bit_cnt +1'b1;
	else
		bit_cnt <= bit_cnt;
else
	bit_cnt <= 3'd0;
end
//波特率计数
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
	cycle_cnt <= 16'd0;
else if((state == S_SEND_BYTE && cycle_cnt == CYCLE-1)|| next_state != state)
	cycle_cnt <= 16'd0;
else
	cycle_cnt <= cycle_cnt + 16'd1;
end

endmodule

modelsim仿真

发送模块仿真波形图,如下所示:
在这里插入图片描述

2.接收模块

接收模块的状态转换图:
在这里插入图片描述

接收模块的方框图:
在这里插入图片描述

module uart_rx
#(
	parameter	CLK_FRE	=	50,
	parameter	BAUD_RATE = 115200
)
(
	input				clk,
	input				rst,
	input				rx_pin,
	input				rx_ready,
	
	output	reg		rx_valid,
	output	reg[7:0]	rx_data
	);
	
localparam	CYCLE = (CLK_FRE * 1000000)/BAUD_RATE;

localparam	S_IDLE 		= 0;
localparam	S_START		= 1;
localparam	S_RX_CYCLE 	= 2;
localparam	S_DATA		= 3;
localparam	S_STOP 		= 4;

reg[2:0]		state;
reg[2:0]		next_state;
reg			rx_d0;
reg			rx_d1;
wire			rx_negedge;
reg[7:0]		rx_bits;
reg[2:0]		bit_cnt;
reg[15:0]	cycle_cnt;

assign	rx_negedge = ~rx_d0 && rx_d1;
//消息桢起始位,下降沿
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
	begin
	rx_d0 <= 1'b0;
	rx_d1 <= 1'b0;
	end
else 
	begin
	rx_d0 <= rx_pin;
	rx_d1 <= rx_d0;
	end
end

always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
	state <= S_IDLE;
else
	state <= next_state;
end 
//状态转移
always@(*)
begin
	case(state)
		S_IDLE:
			if(rx_negedge)
				next_state <= S_START;
			else
				next_state <= S_IDLE;
		S_START:
			if(cycle_cnt == CYCLE-1)
				next_state <= S_RX_CYCLE;
			else
				next_state <= S_START;
		S_RX_CYCLE:
			if(cycle_cnt == CYCLE-1 && bit_cnt == 3'd7)
				next_state <= S_STOP;
			else
				next_state <= S_RX_CYCLE;
		S_STOP:
			if(cycle_cnt == CYCLE/2-1)
				next_state <= S_DATA;
			else
				next_state <= S_STOP;
		S_DATA:
			if(rx_ready)
				next_state <= S_IDLE;
			else
				next_state <= S_DATA;
		default:
			next_state <= S_IDLE;
	endcase
end
//波特率计数器	
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
	cycle_cnt <= 16'd0;
else if((state == S_RX_CYCLE && cycle_cnt == CYCLE-1)||next_state != state)
	cycle_cnt <= 16'd0;
else
	cycle_cnt <= cycle_cnt + 16'd1;
end
//比特位计数器
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
	bit_cnt <= 3'd0;
else if(state == S_RX_CYCLE )
	if(cycle_cnt == CYCLE-1)
		bit_cnt <= bit_cnt + 3'd1;
	else
		bit_cnt <= bit_cnt;
else
	bit_cnt <= 3'd0;
end
//串行数据转为并行数据
always@(posedge clk or negedge rst)
begin 
if(rst == 1'b0)
	rx_bits <= 8'd0;
else if(state == S_RX_CYCLE && cycle_cnt == CYCLE/2-1)
	rx_bits[bit_cnt] <= rx_pin;
else
	rx_bits <= rx_bits;
end
//数据接收有效标志位
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
	rx_valid <= 1'b0;
else if(state == S_STOP && next_state != state)
	rx_valid <= 1'b1;
else if(state == S_DATA && rx_ready)
	rx_valid <= 1'b0;
end
//数据接收
always@(posedge clk or negedge rst)
begin 
if(rst == 1'b0)
	rx_data <= 8'd0;
else if(state == S_STOP && next_state != state)
	rx_data <= rx_bits;
else
	rx_data <= rx_data;
end
		
endmodule

modelsim仿真

模块整体波形图如下所示
在这里插入图片描述
采用两个寄存器,利用非阻塞赋值,对输入数据进行打拍,进而获得消息桢的下降沿。如下图所示:
在这里插入图片描述
内部状态转移和寄存器的波形图如下所示:
在这里插入图片描述

3.顶层模块

设计一个顶层程序,让FPGA每隔1秒发送一段字符串,并在等待时间里可接收计算机发送的数据,并将接收数据发送至PC端。
在这里插入图片描述
结构方框图如下所示:
在这里插入图片描述
FPGA每接收到一次数据就对LED进行电平翻转,所以增加了LED输出口,控制LED0。
代码如下:

module uart_test
#(
	parameter		CLK_FRE = 50,
	parameter		BAUD_RATE = 115200
)
(
	input		clk,
	input		rst,
	input		rx,
	output	tx,
	output reg	led
	);
	
localparam		IDLE = 0;
localparam		SEND = 1;
localparam		WAIT = 2;	

reg[1:0]		state;
reg[1:0]		next_state;

reg[7:0]		tx_data;
reg[7:0]		tx_str;
reg				tx_data_valid;
wire			tx_data_ready;

wire				rx_ready;
wire[7:0]		rx_data;
wire			rx_valid;	

reg[3:0]		tx_cnt;
reg[31:0]		wait_cnt;			

assign rx_ready = 1'b1;

always@(posedge clk or negedge rst)
begin
	if(rst == 1'b0)
		begin
			wait_cnt <= 32'd0;
			tx_cnt <= 8'd0;
			state <= IDLE;
			tx_data <= 8'd0;
			tx_data_valid <= 1'b0;
			led <= 1'b0;
		end
	else
		case(state)
			IDLE:
				state <= SEND;
			SEND:
				begin
					wait_cnt <= 32'd0;
					tx_data <= tx_str;
					if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < 4'd12)//发送字符串"I LOVE YOU"
					begin
						tx_cnt <= tx_cnt + 4'd1;
					end
					else if(tx_data_valid && tx_data_ready)//等待最后一个字节发送完
					begin
						tx_cnt <= 8'd0;
						tx_data_valid <= 1'b0;
						state <= WAIT;
					end
					else if(~tx_data_valid)
					begin
						tx_data_valid <= 1'b1;
					end
				end
			WAIT://等待1s的间隔,等待时间里可接收字符串,并送至发送模块
			begin
				wait_cnt <= wait_cnt +1'b1;
				if(rx_valid == 1'b1)
				begin
					tx_data_valid <= 1'b1;
					tx_data <= rx_data;
					led <= ~led;
				end
				else if(tx_data_valid && tx_data_ready)
				begin
					tx_data_valid <= 1'b0;
				end
				else if(wait_cnt >= CLK_FRE * 1000000)
				begin
					state <= SEND;
				end
			end
			default:
					state <= IDLE;
		endcase
	end
//要发送的字符串	
always@(*)
begin
	case(tx_cnt)
	4'd0:tx_str <= "I";
	4'd1:tx_str <= " ";
	4'd2:tx_str <= "L";
	4'd3:tx_str <= "O";
	4'd4:tx_str <= "V";
	4'd5:tx_str <= "E";
	4'd6:tx_str <= " ";
	4'd7:tx_str <= "Y";
	4'd8:tx_str <= "O";
	4'd9:tx_str <= "U";
	4'd10:tx_str <= "\r";
	4'd11:tx_str <= "\n";
	default:tx_str <= 8'd0;
	endcase
end

uart_tx 
#(
		.CLK_FRE	(CLK_FRE),
		.BAUD_RATE	(BAUD_RATE)
)uart_tx
(
	.clk(clk),
	.rst(rst),
	.tx_data(tx_data),
	.tx_data_valid(tx_data_valid),
	
	.tx_pin(tx),
	.tx_data_ready(tx_data_ready)
);

uart_rx 
#(
	.CLK_FRE	(CLK_FRE),
	.BAUD_RATE 	(BAUD_RATE)
)uart_rx
(
	.clk(clk),
	.rst(rst),
	.rx_pin(rx),
	.rx_ready(rx_ready),
	
	.rx_valid(rx_valid),
	.rx_data(rx_data)
	);
	
endmodule

四、实验结果

使用UCF文件对FPGA端口进行定义:

NET "clk" LOC = T8 | TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 50000 kHz;

##
NET "rst"                  LOC = L3 | IOSTANDARD = "LVCMOS33";         ## reset pushbutton

##################################################################################
#USB Serial RS232 Pin define
##################################################################################
NET "rx"                LOC = C11 | IOSTANDARD = "LVCMOS33";   	## Uart RXD:U4_TXD
NET "tx"                LOC = D12 | IOSTANDARD = "LVCMOS33"; 	## Uart TXD:U4_RXD
##################################################################################
#LED Pin define
##################################################################################
NET "led"               LOC = P4 | IOSTANDARD = "LVCMOS33";       ## LED1

1、代码调试

1、ERROR:HDLCompiler:1511 - “C:\Users\HP\Desktop\FPGA_demo\04_uart\rtl\uart_rx.v” Line 29: Mix of blocking and non-blocking assignments to variable <rx_bits> is not a recommended coding practice.
错误原因:阻塞赋值与非阻塞赋值同时使用
2、锁存器警告,case语句需要写完整,一定要写”default“,负责就会产生不需要的锁存器,对后续的时序设计带来影响。定义的寄存器一定要给定初值,负责也会产生锁存器。

2、实验结果

串口结果如图所示:
在这里插入图片描述

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数字通信同步技术是实现高速、可靠数字通信的关键技术之一。而在数字通信系统中,数字通信同步技术的MATLAB与FPGA实现,特别是基于Xilinx/VHDL进行开发的方式,具有重要的意义。 首先,MATLAB作为一个强大的数学计算工具,可以方便地进行算法的设计与验证。在数字通信同步技术中,使用MATLAB进行算法设计和仿真可以更加直观地观察到系统的性能和特性,并且可以快速地进行系统性能指标的评估和优化。同时,MATLAB还提供了丰富的工具箱和函数库,包括通信系统工具箱、信号处理工具箱等,可以方便地调用和实现数字通信同步技术中的各种功能模块。 其次,FPGA作为一种可编程逻辑器件,可以实现数字通信同步技术中的各种算法和功能模块。基于Xilinx平台和VHDL语言进行FPGA开发,具有较高的灵活性和可扩展性。可以通过编写VHDL代码,将MATLAB中设计好的算法直接转换为硬件逻辑,在FPGA芯片上进行运行。这样可以实现更高的运行速度和更低的系统延迟,并且可以大大提高系统的实时性和并行计算能力。 通过将数字通信同步技术的MATLAB与FPGA实现进行结合,可以充分发挥两者的优势,提高数字通信系统的运行速度和性能。实现MATLAB算法到FPGA芯片的转化需要使用HDL Coder工具。该工具可以将MATLAB中的算法转换为HDL代码(如VHDL),并为FPGA开发提供数学功能密集型模块。与传统的软件实现相比,基于FPGA的实现可以更好地满足高速、实时和并行计算的需求,并且能够方便地进行系统的调试和优化。 综上所述,数字通信同步技术的MATLAB与FPGA实现,尤其是基于Xilinx/VHDL的开发方式,可以极大地提高数字通信系统的性能和可靠性,对数字通信领域的研究和应用具有重要的意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值