FPGA学习笔记_UART串口协议以及串口接收端设计

FPGA学习笔记

1. UART串口协议以及串口接收端设计

1 原理图
2 Verilog 代码
3 Modelsim仿真
4. FPGA板级验证

1.1 串口协议接收端设计

  • 目标:FPGA接收其他设备通过UART发送过来的数据。
  • 实验现象:在Quartusz II中调用in system sources and probes
    editor工具,查看UART接收模块接受到的数据,数据有pc机发出。
  • 知识点:
    1. uart的通信协议原理和工业环境下的数据接受实现
    2. in system sources and probes editor(ISSP)调试工具的使用

1. 原理图

在这里插入图片描述

  • 模块组成:
    1. 输入信号同步模块
    2. 起始数据检测模块
    3. 波特率产生模块
    4. 数据接受进程

(1). 发送端实验室数据采集时序图

在这里插入图片描述

  • 实验室环境:采样一个数据的中点

(2). 发送端工业数据采集时序图
在这里插入图片描述

  • 工业环境:工业环境的电磁干扰严重,噪音信号容易导致错误,只采样一次的数据不准确。
  • 方法:采每位数据采样的中间6次的结果,根据这6次结果的电平(0,1)出现的次数,判定最终结果。
    在这里插入图片描述

2. Verilog 代码

//----top---------------------------------------
module uart_rx_r0
(
	input 				clk,
	input 				rst_n,
	input 				rs232_rx,
	input [2:0]			baud_set,
	
	output 	reg		rx_done,
//	output 			rx_state,
	output reg  [7:0]   data_byte
);

	reg rs232_rx_reg1;
	reg rs232_rx_reg2;
	reg rs232_rx_syn1;
	reg rs232_rx_syn2;
	
	reg uart_state;
	reg [8:0] div_cnt;
	reg [8:0] cnt_max;//div_cnt max value
	reg baud_clk;
	reg [7:0] baud_cnt;
	reg [3:0] reg_data_byte [7:0];
	reg [2:0] START;
	reg [2:0] STOP;	
//	wire pos_edge;
	wire neg_edge;
	
//----asynch rs232_rx--> synch-----------
	always@(posedge clk or negedge rst_n)
		if(!rst_n)begin
			rs232_rx_syn1 <= 0;
			rs232_rx_syn2 <= 0;
		end else begin
			rs232_rx_syn1 <= rs232_rx;
			rs232_rx_syn2 <= rs232_rx_syn1;
		end
//----start bit detect------------------
	always@(posedge clk or negedge rst_n)
		if(!rst_n)begin
			rs232_rx_reg1 <= 0;
			rs232_rx_reg2 <= 0;
		end else begin
			rs232_rx_reg1 <= rs232_rx_syn2;
			rs232_rx_reg2 <= rs232_rx_reg1;
		end
//	assign pos_edge = (rs232_rx_reg1 & !rs232_rx_reg2);
	assign neg_edge = (!rs232_rx_reg1 & rs232_rx_reg2); 
	
//------baud_set------------------------
	always@(*)
		case(baud_set)
			3'd0: cnt_max <= 9'd325;
			3'd1: cnt_max <= 9'd163;
			3'd2: cnt_max <= 9'd81;
			3'd3: cnt_max <= 9'd54;
			3'd4: cnt_max <= 9'd27;
		default: cnt_max <= 9'd325;
		endcase
//----uart_state-------------------------
	always@(posedge clk or negedge rst_n)
		if(!rst_n)
			uart_state <= 0;
		else if(neg_edge)
			uart_state <= 1;
		else if(rx_done || (baud_cnt==12 && (START > 2))) 
			uart_state <= 0;
		else
			uart_state <= uart_state;
//----frequence div-->baud_clk-----------
	always@(posedge clk or negedge rst_n)
		if(!rst_n)
			div_cnt <= 0;
		else if(uart_state)begin
			if(div_cnt == cnt_max)
				div_cnt <= 0;
			else
				div_cnt <= div_cnt + 9'd1;
		end else
				div_cnt <= 0;
	always@(posedge clk or negedge rst_n)
		if(!rst_n)
			baud_clk <= 1'd0;
		else if(div_cnt == 9'd1)
			baud_clk <= 1'd1;
		else
			baud_clk <= 1'd0;
//----baud_cnt--------------------------
	always@(posedge clk or negedge rst_n)
		if(!rst_n)
			baud_cnt <= 0;
		else if(rx_done || (baud_cnt==12 && (START > 2)))
			baud_cnt <= 0;
		else if(baud_clk)
				baud_cnt <= baud_cnt + 8'd1;
		else
				baud_cnt <= baud_cnt;
			
//----rx_done----------------------------
	always@(posedge clk or negedge rst_n)
		if(!rst_n)
			rx_done <= 1'b0;
		else if(baud_cnt == 8'd159)
			rx_done <= 1'b1;
		else 
			rx_done <= 1'b0;
			
//----data_byte--------------------------
	always@(posedge clk or negedge rst_n) // asynchronos transimiter, reg_data_byte is used to keep input data stable 
		if(!rst_n)
			data_byte <= 8'd0;
		else if(baud_cnt == 8'd159)begin
			data_byte[0] <= reg_data_byte[0][2];
			data_byte[1] <= reg_data_byte[1][2];
			data_byte[2] <= reg_data_byte[2][2];
			data_byte[3] <= reg_data_byte[3][2];
			data_byte[4] <= reg_data_byte[4][2];
			data_byte[5] <= reg_data_byte[5][2];
			data_byte[6] <= reg_data_byte[6][2];
			data_byte[7] <= reg_data_byte[7][2];
		end

//----reg_data_byte----------------------
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			START <= 3'd0;
			reg_data_byte[0] <= 3'd0; 
			reg_data_byte[1] <= 3'd0;
			reg_data_byte[2] <= 3'd0;
			reg_data_byte[3] <= 3'd0;
			reg_data_byte[4] <= 3'd0;
			reg_data_byte[5] <= 3'd0;
			reg_data_byte[6] <= 3'd0;
			reg_data_byte[7] <= 3'd0;
			STOP <= 3'd0;
		end 
		else if(baud_clk)begin
			case(baud_cnt) //0:initial baud_cnt=0 --> after sending data, rs232_tx always be 0; 
				0: begin
						START <= 3'd0;
						reg_data_byte[0] <= 3'd0; 
						reg_data_byte[1] <= 3'd0;
						reg_data_byte[2] <= 3'd0;
						reg_data_byte[3] <= 3'd0;
						reg_data_byte[4] <= 3'd0;
						reg_data_byte[5] <= 3'd0;
						reg_data_byte[6] <= 3'd0;
						reg_data_byte[7] <= 3'd0;
						STOP <= 3'd0;
					end
				6,7,8,9,10,11: START <= START + rs232_rx_reg2;
				22,23,24,25,26,27:       reg_data_byte[0] <= reg_data_byte[0] + rs232_rx_reg2;
				38,39,40,41,42,43:       reg_data_byte[1] <= reg_data_byte[1] + rs232_rx_reg2;
			   54,55,56,57,58,59:       reg_data_byte[2] <= reg_data_byte[2] + rs232_rx_reg2;
				70,71,72,73,74,75:       reg_data_byte[3] <= reg_data_byte[3] + rs232_rx_reg2;
				86,87,88,89,90,91:       reg_data_byte[4] <= reg_data_byte[4] + rs232_rx_reg2;
				102,103,104,105,106,107: reg_data_byte[5] <= reg_data_byte[5] + rs232_rx_reg2;
				118,119,120,121,122,123: reg_data_byte[6] <= reg_data_byte[6] + rs232_rx_reg2;
				130,131,132,133,134,135: reg_data_byte[7] <= reg_data_byte[7] + rs232_rx_reg2;
				148,149,150,151,152,153: STOP <= STOP + rs232_rx_reg2;
				default begin
						START <= START;
						reg_data_byte[0] <= reg_data_byte[0]; 
						reg_data_byte[1] <= reg_data_byte[1];
						reg_data_byte[2] <= reg_data_byte[2];
						reg_data_byte[3] <= reg_data_byte[3];
						reg_data_byte[4] <= reg_data_byte[4];
						reg_data_byte[5] <= reg_data_byte[5];
						reg_data_byte[6] <= reg_data_byte[6];
						reg_data_byte[7] <= reg_data_byte[7];
						STOP <= STOP;				
				end
		endcase
	 end
	end	
	
endmodule
//------------------------------------------------
//----testbench-----------------------------------
`timescale 1ns/1ns
`define clk_period 20

module uart_rx_r0_tb;
	reg clk;
   reg rst_n;
	
	reg [7:0] data_byte_tx;
	wire rx_done;

	reg send_en;
	reg [2:0] baud_set;
	wire [7:0] data_byte_rx;
	wire	rs232_rx;
	wire	tx_done;
	//wire	uart_state; 
	wire led;
	
	uart_rx_r0	uut_rx(
		.clk(clk),
		.rst_n(rst_n),
		.rs232_rx(rs232_rx),
		.baud_set(baud_set),
		.rx_done(rx_done),
		.data_byte(data_byte_rx)
	);
						
		uart_tx_r0 uut_tx(
		.clk(clk),
		.rst_n(rst_n),
		.send_en(send_en),
		.baud_set(baud_set),
		.tx_done(tx_done),
		.data_byte(data_byte_tx),
		.uart_state(led),
		.rs232_tx(rs232_rx)
	);
	
	
		initial begin
		clk = 1;
		rst_n = 0;
		send_en = 0;
		baud_set = 3'd4;
		#(`clk_period*20+1);
		rst_n = 1;
		#(`clk_period*50);
		data_byte_tx = 8'haa;//8'b1010_1010
		send_en = 1;
		#`clk_period;
		send_en = 0;
		
		@(posedge tx_done)
		
		#(`clk_period*5000);
		data_byte_tx = 8'h55;//0101_0101
		send_en = 1;
		#`clk_period;
		send_en = 0;
		
		@(posedge tx_done)
		#(`clk_period*5000);
		$stop;
	end
	always begin #(`clk_period/2) clk = ~clk;end
	
endmodule


3. Modelsim仿真

在这里插入图片描述

4. FPGA板级验证
在这里插入图片描述

  • 串口助手发送数据,ISSP软件实时接收数据。

----学习内容来自小梅哥FPGA视频

【注】:个人学习笔记,如有错误,望不吝赐教,这厢有礼了~~~

猜你喜欢

转载自blog.csdn.net/weixin_50722839/article/details/109744919