• 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 is not a recommended coding practice.
    错误原因:阻塞赋值与非阻塞赋值同时使用
    2、锁存器警告,case语句需要写完整,一定要写”default“,负责就会产生不需要的锁存器,对后续的时序设计带来影响。定义的寄存器一定要给定初值,负责也会产生锁存器。

    2、实验结果

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

  • 相关阅读:
    汇编语言入门(一)
    弱鸡记录一道模拟题
    Pytest进阶使用
    Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单
    git 回退过程记录
    PIE-engine 教程 ——影像集合的使用for循环函数(北京市NDVI计算)
    数字图标的使用(阁瑞钛伦特软件-九耶实训)
    [PAT练级笔记] 19 Basic Level 1019 数字黑洞
    DP0001A高压差分探头具有哪些具体性能?
    七夕力扣刷不停,343. 整数拆分(剑指 Offer 14- I. 剪绳子、剑指 Offer 14- II. 剪绳子 II)
  • 原文地址:https://blog.csdn.net/m0_51390088/article/details/126228195