原创 18.我与FPGA的恋爱之Uart发送数据

2016-4-20 19:07 906 20 20 分类: FPGA/CPLD 文集: 我与FPGA的恋爱

UART使用的是 异步,串行通信。
    串行通信是指利用一条传输线将资料一位位地顺序传送。特点是通信线路简单,利用简单的线缆就可实现通信,降低成本,适用于远距离通信,但传输速度慢的应用场合。
    异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的时间间隔是固定的。 
    数据传送速率用波特率来表示,即每秒钟传送的二进制位数。例如数据传送速率为120字符/秒,而每一个字符为10位(1个起始位,7个数据位,1个校验位,1个结束位),则其传送的波特率为10×120=1200字符/秒=1200波特。

UART发送一个字节时序图:

  2016-04-16_164624.jpg               

 

串口发送模块包含两个主要组件:

1、  发送波特率生成模块

2、  数据发送模块

 

串口发送模块整体结构体

2016-04-16_164643.jpg

 

串口发送模块详细结构图

2016-04-16_164701.jpg

 

波特率计算:

系统时钟周期为System_clk_period

 

baud_set

波特率

波特率周期

波特率分频计数值

System_clk_period = 20计数值

0

9600

104167ns

104167/ System_clk_period

5208-1

1

19200

52083ns

52083/ System_clk_period

2604-1

2

38400

26041ns

26041/ System_clk_period

1302-1

3

57600

17361ns

17361/ System_clk_period

868-1

4

115200

8680ns

8680/ System_clk_period

434-1

 

 代码:

module uart_tx(
					clk,
					rst_n,
					data_byte,
					send_en,
					baud_set,
					rs232_tx,
					tx_done,
					uart_state
					);
	
	input clk,rst_n;
	input [7:0]data_byte; //发送字节
	input send_en;//发送控制
	input [3:0]baud_set;//波特率选择
	
	output reg rs232_tx;//数据发送
	output reg tx_done;//发送完成
	output reg uart_state;//是否处于空闲状态
	
	//查找表--比特率选择
	reg [15:0]bps_max;//分频计数最大值
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_max <= 16'd5207;
	else begin
		case (baud_set)
			0:	bps_max <= 16'd5207;
			1:	bps_max <= 16'd2603;
			2:	bps_max <= 16'd1301;
			3:	bps_max <= 16'd867;
			4:	bps_max <= 16'd433;
			default:bps_max <= 16'd5207;	
		endcase
	end
	
	reg [15:0]div_cnt;
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		div_cnt <= 16'd0;
	else if(uart_state)begin
		if(div_cnt == bps_max)
			div_cnt <= 16'd0;
		else
			div_cnt <= div_cnt +1'b1;
	end
	else
		div_cnt <= 16'd0;
	
	reg bps_clk;//波特率时钟
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_clk <= 1'b0;
	else if(div_cnt == 16'd1)
		bps_clk <= 1'b1;
	else
		bps_clk <= 1'b0;
		
	reg [3:0]bps_cnt;  //计数到11,波特率时钟计数器
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_cnt <= 4'd0;
	else if(bps_clk)
		bps_cnt <= bps_cnt + 1'b1;
	else if(tx_done)
		bps_cnt <= 4'd0;
	else
		bps_cnt <= bps_cnt;
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		tx_done <= 1'b0;
	else if(bps_cnt == 4'd11)
		tx_done <= 1'b1;
	else
		tx_done <= 1'b0;
		
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		uart_state <= 1'b0;
	else if(send_en)
		uart_state <= 1'b1;
	else if(tx_done)
		uart_state <= 1'b0;
	else
		uart_state <= uart_state;
	
	reg [7:0]r_data_byte;
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		r_data_byte <= 8'd0;
	else if(send_en)
		r_data_byte <= data_byte;
	else
		r_data_byte <= r_data_byte;
	
	localparam START_BIT = 1'b0;
	localparam STOP_BIT = 1'b1;
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		rs232_tx <= 1'b1;
	else begin
		case(bps_cnt)
			0	:	rs232_tx <= 1'b1;
			1	:	rs232_tx <= START_BIT;
			2	:	rs232_tx <= r_data_byte[0];
			3	:	rs232_tx <= r_data_byte[1];
			4	:	rs232_tx <= r_data_byte[2];
			5	:	rs232_tx <= r_data_byte[3];
			6	:	rs232_tx <= r_data_byte[4];
			7	:	rs232_tx <= r_data_byte[5];
			8	:	rs232_tx <= r_data_byte[6];
			9	:	rs232_tx <= r_data_byte[7];
			10	:	rs232_tx <= STOP_BIT;
			default:rs232_tx <= 1'b1;
		endcase
	end	
	
endmodule


`timescale 1ns/1ns
`define clk_period 20

module uart_tx_tb;

	reg clk;
	reg rst_n;
	reg [7:0]data_byte;
	reg send_en;
	reg [3:0]baud_set;
	
	wire rs232_tx;
	wire tx_done;
	wire uart_state;
	
	uart_tx uart_tx(
		.clk(clk),
		.rst_n(rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(baud_set),
		
		.rs232_tx(rs232_tx),
		.tx_done(tx_done),
		.uart_state(uart_state)
	);
	
	initial clk = 1;
	always#(`clk_period/2)clk = ~clk;
	
	initial begin
		rst_n = 1'b0;
		data_byte = 8'd0;
		send_en = 1'd0;
		baud_set = 3'd4;
		#(`clk_period*20 + 1 )
		rst_n = 1'b1;
		#(`clk_period*50);
		data_byte = 8'haa;
		send_en = 1'd1;
		#`clk_period;
		send_en = 1'd0;
		
		@(posedge tx_done)
		
		#(`clk_period*5000);
		data_byte = 8'h55;
		send_en = 1'd1;
		#`clk_period;
		send_en = 1'd0;
		
		@(posedge tx_done)
		#(`clk_period*5000);
		$stop;	
	end

endmodule

2016-04-16_203055.jpg

 

2016-04-16_203621.jpg

 

更多资料请参考:

                    发烧友小梅哥视频教程

文章评论0条评论)

登录后参与讨论
我要评论
0
20
关闭 站长推荐上一条 /2 下一条