FPGA学习笔记_UART串口协议_串口发送端设计1

FPGA学习笔记

1. UART串口协议以及串口发送端设计1

  • 发送单一字节

1. 串口协议发送端设计

(1). 概念:
UART: (Universal Asynchronous Receiver/Transmitter)通用异步收发器,是异步串行通信的总称。全双工,发送端:并转串;接收端:串转并。包括RS232,RS449,RS423,RS485(各种异步串行通信口的接口标准和总线标准,电气特性,传输速率,连接特性,接口的机械特性等)。

(2). 特点: 电路结构简单,成本低。

(3). 关键参数:

数据位(data bits): 单个UART数据传输在开始从停止期间发送的数据位数;可选择5, 6, 7, 8(默认)

波特率(baud): 从一个设备发到另一个设备,每秒可以通信的数据比特个数。典型的波特率有300,1200,2400,9600,19200,115200(1s:115200bits;1bit: 8.68us)等;通信设备两端应设置相同的波特率。

波特率与时钟周期的换算:
频率(f)=500MHz, 周期(T)=20ns;
波特率B(bps) >> 1s传送Bbit数据>> 1bit数据传输需要(1/B(bps))s=(1000000000/ B(bps))ns
波特率的分频计数值:(1000000000/ B(bps))ns/时钟周期

系统时钟计数值 = 波特率的分频计数值 - 1;
FPGA学习笔记_UART串口协议_串口发送端设计1_第1张图片
奇偶校验位: 验证数据的正确性。偶校验(even):传输的数位中1的个数位偶数;奇校验(odd):传送的数位中1的个数位奇数(含字符的各个数位和校验位)。

停止位数 : 在每个字节的数据位发送完成后,发送停止位,标志一次数据传输完成,同时可用来帮助接受信号方硬件同步。可选择位:1(默认),1.5,2位。

RS323:8N1(8个数据位,1个停止位,无奇偶校验位)

(4). 串口协议发送端整体框图

  • 整体框图:

FPGA学习笔记_UART串口协议_串口发送端设计1_第2张图片

  • 信号描述

FPGA学习笔记_UART串口协议_串口发送端设计1_第3张图片

  • 细节图

FPGA学习笔记_UART串口协议_串口发送端设计1_第4张图片

(5). 串口协议发送端时序图
FPGA学习笔记_UART串口协议_串口发送端设计1_第5张图片

  • START: 低电平
  • STOP:高电平
  • DATA:8bits

(6). Verilog 代码


module uart_tx_r0#(
	parameter START = 1'b0,
	parameter STOP = 1'b1
)
(
		input 		clk, //50MHz
		input 		rst_n,
		input			send_en,
		input [2:0] baud_set,
		input [7:0] data_byte,
		
		output reg	rs232_tx,
		output reg	tx_done,
		output reg	uart_state
);
//----define--------------------------------------
	reg [7:0]	reg_data_byte;
	reg [15:0] 	cnt;//divide frequence counter
	reg			baud_clk;
	reg [15:0]	cnt_max;//baud value
	reg [3:0]   cnt_bit;//
	
	
	
//----baud_set--------------------------------------
	always@(*)begin
		case(baud_set)
//			0:	cnt_max <= 16'd20832;
//			1: cnt_max <= 16'd10415;
			0: cnt_max <= 16'd5207;
			1: cnt_max <= 16'd2603;
			2: cnt_max <= 16'd1301;
			3: cnt_max <= 16'd867;
			4: cnt_max <= 16'd433;
//			7: cnt_max <= 16'd194;
			default: cnt_max <= 5207;
		endcase
	end

//----divide frequence counter-->baud clock------------------------	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			cnt <= 0;
		end 
		else if(uart_state)begin
			if(cnt == cnt_max) 
				cnt <= 0;
		else
				cnt <= cnt +16'd1;
		end else
			cnt <= 0;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			baud_clk <= 1'b0;
		else if(cnt == 16'd1)
			baud_clk <= 1'b1;
		else
			baud_clk <= 1'b0;
	end
//----cnt_bit----------------------------------------
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt_bit <= 0;
		else if(cnt_bit == 4'd11)
				cnt_bit <= 0;
		else if(baud_clk)
				cnt_bit <= cnt_bit + 4'd1;
		else
				cnt_bit <= cnt_bit;
	end
//----rs232_tx --------------------------------------	
	always@(posedge clk or negedge rst_n)begin // asynchronos transimiter, reg_data_byte is used to keep input data stable 
		if(!rst_n)
			reg_data_byte <= 8'd0;
		else if(send_en)
			reg_data_byte <= data_byte;
		else
			reg_data_byte <= reg_data_byte;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			rs232_tx <= 0;
		end begin
			case(cnt_bit) //0:initial cnt_bit=0 --> after sending data, rs232_tx always be 0; 
				0: rs232_tx <= 1'b1;
				1: rs232_tx <= START; //0
				2: rs232_tx <= reg_data_byte[0];
				3: rs232_tx <= reg_data_byte[1];
			    4: rs232_tx <= reg_data_byte[2];
				5: rs232_tx <= reg_data_byte[3];
				6: rs232_tx <= reg_data_byte[4];
				7: rs232_tx <= reg_data_byte[5];
				8: rs232_tx <= reg_data_byte[6];
				9: rs232_tx <= reg_data_byte[7];
				10: rs232_tx <= STOP; //1
				default: rs232_tx <= 1;
		endcase
	 end
	end
//----tx_done-----------------------------------------
		always@(posedge clk or negedge rst_n)begin
			if(!rst_n)
				tx_done <= 1'b0;
			else if(cnt_bit == 4'd11)
				tx_done <= 1'b1;
			else
				tx_done <= 1'b0;
		end
		
//----uart_state--------------------------------------
		always@(posedge clk or negedge rst_n)
			if(!rst_n)
				uart_state <= 1'b0;
			else if(send_en)
				uart_state <= 1'b1;
			else if(cnt_bit == 4'd11)
				uart_state <= 0;
			else
				uart_state <= uart_state;

endmodule

测试平台 Testbench

//------------------------------------------------
//----testbench-----------------------------------
`timescale 1ns/1ns
`define clk_period 20

module tb_uart_tx_r0;
	reg clk;
	reg rst_n;
	reg send_en;
	reg [2:0] baud_set;
	reg [7:0] data_byte;
	
	wire	rs232_tx;
	wire	tx_done;
	wire	uart_state; 
	
	uart_tx_r0 uut(
		.clk(clk),
		.rst_n(rst_n),
		.send_en(send_en),
		.baud_set(baud_set),
		.tx_done(tx_done),
		.data_byte(data_byte),
		.uart_state(uart_state),
		.rs232_tx(rs232_tx)
	);
	
	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 = 8'haa;//8'b1010_1010
		send_en = 1;
		#`clk_period;
		send_en = 0;
		
		@(posedge tx_done)
		
		#(`clk_period*5000);
		data_byte = 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

(7). Modelsim仿真

FPGA学习笔记_UART串口协议_串口发送端设计1_第6张图片
(8). FPGA板级验证

  • 板级验证顶层代码设置
module uart_tx_top(
	input clk,
	input rst_n,
	input key0,
	output led,
	output rs232_tx
);
	wire send_en;
	wire [7:0] data_byte;
	wire key_state0;
	wire key_flag0;
	
	uart_tx_r0 uut_uart(
		.clk(clk),
		.rst_n(rst_n),
		.send_en(send_en),
		.baud_set(3'd4),
		.tx_done(),
		.data_byte(data_byte),
		.uart_state(led),
		.rs232_tx(rs232_tx)
	);
	
	fsm_key_filter uut_key(
			.clk(clk),
			.rst_n(rst_n),
			.key(key0),
			.key_state(key_state0),
			.key_flag(key_flag0)
);
	dada_source uut_dada_source(
		.source(data_byte)  // sources.source
	);
	assign send_en = key_flag0 & ! key_state0;
	
endmodule
  • 按键消抖
module fsm_key_filter#(
	parameter IDLE = 4'b0001,
	parameter FILTER1 = 4'b0010,
	parameter DOWN = 4'b0100,
	parameter FILTER2 = 4'b1000
)
(
	input clk, //50MHz 20us
	input rst_n,
	input key,
	
	output  key_flag,
	output reg  key_state
);
	reg		cnt_en;
	reg		cnt_full;
	reg [19:0] cnt1;
	//reg [19:0] cnt2;
	reg [3:0] state;
	reg		key_syn1;
	reg		key_syn2;
	reg 		key_reg1;
	reg		key_reg2;
	wire 		pos_edge;
	wire 		neg_edge;
	
	always@(posedge clk or negedge rst_n)
		if(!rst_n)begin
			state <= IDLE;
			cnt_en <= 1'b0;
		end else 
		begin
			case(state)
				IDLE:
					begin
						if(neg_edge)begin
							state <= FILTER1;
							cnt_en <= 1'b1;
						end else
						begin
							state <=	IDLE;	
							cnt_en <= 1'b0;
						end
					end
					
				FILTER1:
					begin
							if(cnt_full)//20ms
							begin
								state <= DOWN;
								cnt_en <= 1'b0;
							end 
							else if(pos_edge)
							begin
								state <= IDLE;
								cnt_en <= 1'b0;
							end else
							begin
								state <= FILTER1;
								cnt_en <= cnt_en;
							end
					end
					
				DOWN:
					begin
						if(pos_edge)begin
							cnt_en <= 1'b1;
							state <= FILTER2;
						end else 
						begin
							cnt_en <= 1'b0;
							state <= DOWN;
						end
					end
					
				FILTER2:
					begin
							if(cnt_full)
								state <=	IDLE;	
							else if(neg_edge)begin
								cnt_en <= 1'b0;
								state <= DOWN;
							end 
							else
								state <= FILTER2;
					end
				default: begin
					state <= IDLE;
					cnt_en <= 1'b0;
				end
			endcase
		end
//----cnt--------------------------------------
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt1 <= 20'd0;
		else if(cnt_en)
			cnt1 <= cnt1 + 20'd1;
			else 
			cnt1 <= 20'd0;
	end
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt_full <= 1'b0;
		else if(cnt1 == 20'd999_999)
			cnt_full <= 1'b1;
			else 
			cnt_full <= 1'b0;
	end
//----asyn_key-->syn---------------------------------
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			key_syn1 <= 1'b0;
			key_syn2 <= 1'b0;
		end else
		begin
			key_syn1 <= key;
			key_syn2 <= key_syn1;
		end	
	end
//----key edge detect--------------------------------
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			key_reg1 <= 1'b0;
			key_reg2 <= 1'b0;
		end else
		begin
			key_reg1 <= key_syn2;
			key_reg2 <= key_reg1;
		end	
	end
	assign neg_edge = (!key_reg1) & key_reg2;
	assign pos_edge = key_reg1 & (!key_reg2);
	
//----key_flag---------------------------------------
//		always@(posedge clk or negedge rst_n)
//			if(!rst_n)
//				key_flag <= 0;
//			else if(cnt_full)
//				key_flag <= 1;
//			else
//				key_flag <= 0;
	assign key_flag = (cnt1 == 20'd999_999)? 1'b1:1'b0;
//----key_state--------------------------------------
		always@(posedge clk or negedge rst_n)
			if(!rst_n)
				key_state <= 1;
			else if(cnt_full)
				key_state <= ~key_state;
			else
				key_state <= key_state;	
	//assign key_state = (cnt1 == 20'd999_999)? (~key_state):1;	
endmodule
  • In system source and probes editor工具使用
    具体步骤参看:Quartus II_In system sources and probes editor(ISSP)调试工具的使用
    FPGA学习笔记_UART串口协议_串口发送端设计1_第7张图片

  • 板级验证操作步骤

  1. 将代码通过JTAG,下载到FPGA开发板上
    FPGA学习笔记_UART串口协议_串口发送端设计1_第8张图片
  2. 打开串口调试助手,设置串口,波特率,Hex格式接受,并点击开始,在开发板上按动按键,串口调试助手接受界面显示“00”。
    FPGA学习笔记_UART串口协议_串口发送端设计1_第9张图片
  3. 打开In system source and probes editor调试工具
    FPGA学习笔记_UART串口协议_串口发送端设计1_第10张图片
  4. 设置Hardware,更改Data为想测试的值,如“BB”
    FPGA学习笔记_UART串口协议_串口发送端设计1_第11张图片
  5. 按动FPGA开发板上的按键,则串口调试助手显示界面实时显示设置的值。
    FPGA学习笔记_UART串口协议_串口发送端设计1_第12张图片
    可多次更改验证
    FPGA学习笔记_UART串口协议_串口发送端设计1_第13张图片

----学习内容来自小梅哥FPGA视频
【注】:个人学习笔记,如有错误,望不吝赐教,这厢有礼了~~~


你可能感兴趣的:(FPGA学习笔记,#,接口协议,fpga,verilog,串口通信,uart)