当前位置:   article > 正文

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

四、实验结果

使用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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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、实验结果

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/464999?site
推荐阅读
相关标签
  

闽ICP备14008679号