FPGA - 串口发送多字节数据

1,任务要求

关于uart串口接收与发送可以参考我的这篇博客:《FPGA-UART串口》。这篇博客实现了单字节发送与接收。

接下来,来使用串口发送多字节数据到电脑。

2,模块框图以及简单时序分析图

串口发送多字节数据的思路是把写入的数据存到FIFO中,然后读FIFO完成发送。

框架图如下:

整个过程可以用状态机来实现,首先写入FIFO之前为IDLE状态,当FIFO中的empty信号(当FIFO为空时,empty为高电平1,当FIFO中写入数据时,empty拉低)拉低时进入SEND状态(发送数据),当FIFO中的数据发送结束时,uart_tx_done信号拉高进入END状态。

根据框架图分析简单时序图:

3,整体代码设计

3.1 FIFO配置

        

3.2 发送多字节逻辑代码

然后编写uart_send代码:

`timescale 1ns / 1ps
module uart_send(
	input            clk          ,
	input            reset        ,
	input            wr_en        ,
	input  [7:0]     wr_data      ,

	output reg       uart_tx_en   ,
	output reg [7:0] uart_tx_data ,
	input            uart_tx_busy
    );
	


	reg [1:0]  state       ;

	localparam IDLE = 2'b00;
	localparam SEND = 2'b01;
	localparam END  = 2'b10;

	reg  [7:0]    din  ;
	reg           wren ;
	reg           rd_en;
	wire [7:0]    dout ;
	wire          full ;
	wire          empty;

	reg           uart_tx_busy_d0;
	reg           uart_tx_busy_d1;

	always @(posedge clk ) begin 
		uart_tx_busy_d0  <= uart_tx_busy     ;
		uart_tx_busy_d1  <= uart_tx_busy_d0  ;
	end


	always @(posedge clk ) begin 
		din  <= wr_data;
		wren <= wr_en  ;
	end

	always @(posedge clk) begin 
		if(reset) begin
			state <= IDLE;
		end 
		else begin
			 case(state)
			 	IDLE: begin
			 		if(~empty)
			 			state <= SEND ;
			 		else
			 			state <= state;
			 	end
			 	SEND : begin
			 		if(~uart_tx_busy_d0 && uart_tx_busy_d1)
			 			state <= END  ;
			 		else
			 			state <= state;
			 	end
			 	END : begin
			 		state <= IDLE;
			 	end
			 	default : state <= IDLE;
			 endcase
		end
	end

	always @(posedge clk) begin 
		if(state == IDLE && ~empty) begin
			rd_en <= 1'b1;
		end 
		else 
			rd_en <= 1'b0;
	end

	always @(posedge clk or negedge reset) begin 
		if(reset) begin
			uart_tx_en   <= 1'b0;
			uart_tx_data <= 1'b0;
		end 
		else begin
			uart_tx_en   <= rd_en;
			uart_tx_data <= dout ;
		end
	end


	fifo_w8xd128 fifo_w8xd128 (
		.clk(clk),      // input wire clk
		.srst(reset),    // input wire srst
		.din(din),      // input wire [7 : 0] din
		.wr_en(wren),  // input wire wr_en
		.rd_en(rd_en),  // input wire rd_en
		.dout(dout),    // output wire [7 : 0] dout
		.full(full),    // output wire full
		.empty(empty)  // output wire empty
		);


endmodule

然后设计顶层模块,在顶层模块我们为了上板可以看到接收效果,我们定义一个计数器,在计数器器开始计数时wr_en拉高,在wr_en拉高期间,wr_data自加,计数器计数到49时wr_en拉低,看下图:

3.3 顶层模块

在顶层模块中,其中定义了一个时钟管理单元 (PLL实现)来管理时钟和复位信号(目的使其同源),我们利用ILA逻辑分析仪来控制vio_wr来实现对wr_en的控制。

顶层文件代码如下:

`timescale 1ns / 1ps

module top(
	input	wire		clkin_50m,
	output              uart_txd
    );
    
	wire              reset  ;
	wire              clk    ;
 
	parameter 		  CLK_FREQ   = 50000000;      
	parameter 		  BAUD_RATE  = 115200  ;      
	parameter         DATA_WIDTH = 8       ;      
	parameter         STOP_WIDTH = 1       ;      
	parameter         CHACK_TYPE = 0       ;  


	wire                  uart_tx_en  ;
	wire [7:0]            uart_tx_data;
	wire                  uart_tx_busy;   
	reg                   wr_en       ;
    reg  [7:0]            wr_data     ;
	
	wire                  vio_wr      ;
	reg                   vio_wr_d0   ;
	reg                   vio_wr_d1   ;
	reg                   vio_wr_d2   ;

	reg [7:0]             cnt         ;

	always @(posedge clk ) begin
		vio_wr_d0 <= vio_wr   ;
		vio_wr_d1 <= vio_wr_d0;
		vio_wr_d2 <= vio_wr_d1;
	end

	always @(posedge clk ) begin 
		if(wr_en && cnt == 'd49)
			wr_en <= 1'b0;
		else if(vio_wr_d1 && ~vio_wr_d2) begin
			wr_en <= 1'b1;
		end
	end

	always @(posedge clk) begin
		if(reset) begin
			cnt <= 1'b0;
		end else if(wr_en && cnt == 'd49) begin
			cnt <= 1'b0;
		end
		else if(wr_en) begin
			cnt <= cnt + 1'b1;
		end
	end

	always @(posedge clk ) begin 
		if(reset) begin
			wr_data <= 1'b0;
		end else if(wr_en) begin
			wr_data <= wr_data + 1;
		end
	end
		



	clock_and_reset clock_and_reset 
	(
		.clkin_50m (clkin_50m), 
		.clkout_50M(clk), 
		.reset     (reset)
	);

	uart_send uart_send
		(
			.clk          (clk),
			.reset        (reset),

			.wr_en        (wr_en),
			.wr_data      (wr_data),
			
			.uart_tx_en   (uart_tx_en),
			.uart_tx_data (uart_tx_data),
			.uart_tx_busy (uart_tx_busy)
		);


	uart_tx #(
			.CLK_FREQ(CLK_FREQ),
			.BAUD_RATE(BAUD_RATE),
			.DATA_WIDTH(DATA_WIDTH),
			.STOP_WIDTH(STOP_WIDTH),
			.CHACK_TYPE(CHACK_TYPE)
		) inst_uart_tx (
			.clk          (clk),
			.reset        (reset),

			.uart_txd     (uart_txd),

			.uart_tx_en   (uart_tx_en),
			.uart_tx_data (uart_tx_data),
			.uart_tx_busy (uart_tx_busy)
		);
		
		
    vio_0 vio_0 (
          .clk(clk),                // input wire clk
          .probe_out0(vio_wr)  // output wire [0 : 0] probe_out0
        );
endmodule

4,上板验证

        生成bitstream文件,板级验证,在Hardware Manager界面中添加VIO:

串口接收:

再次切换vio_wr 0/1

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值