Java教程

ZYNQ&FPGA 串口通信实验

本文主要是介绍ZYNQ&FPGA 串口通信实验,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

实验任务

 上位机通过串口将数据发送给开发板,开发板通过串口把数据送回上位机。

TX→RX为串行通信,在FPGA内部接收到发送为并行数据。

协议层:

数据位为8位,停止位为1位,无校验位

波特率为115200bps

 目的:将上图中数据转换为并行数据并给出标志信号。

串口接收过程示意图:

 uart_rxd接收完成会得到uart_done的8位并行信号;

接收完成后uart_done会持续一个波特率周期的高电平,表示下方的并行信号为有效数据;

start_flag为串口接收过程的起始信号,检测到uart_rxd下降沿的时候会给出一个系统时钟周期的高电平。

rx_flag标志整个接收过程。

每一个数据传输需要的时间:1/115200(波特率)

clk_cnt对系统时钟周期计数,计数到一个波特率周期时,就清零,再次开始计数。一个波特率周期计数一次,表示传输一个数据。

rx_cnt在clk_cnt计数满一个波特率周期后+1,标识出对应的各个位。串并转换或称中,根据该值,把对应的各个位放到并行数据对应的位上面去。

串口接收代码:

//串口接收模块
`timescale 1ns / 1ps

module uart_recv(
	input 				sys_clk,
	input 				sys_rst_n,
	
	input 				uart_rxd,//串口接收端
	output reg [7:0] 	uart_data,//串转并
	output reg			uart_done//代表一帧串口数据接收完成,接收到的并行数据uart_data为有效数据
    );

parameter CLK_FREQ = 50_000_000;
parameter UART_BPS = 115200;
parameter BPS_CNT  = CLK_FREQ/UART_BPS;  //clk_cnt需要计数的最大值

reg urat_rxd_d0;
reg urat_rxd_d1;
reg rx_flag;//串口接收过程
reg [3:0] rx_cnt;//计数0-9,4位
reg [15:0]clk_cnt;//波特率除系统时钟 50000000/115200=434左右,会计数434次左右,9位,考虑不同波特率时会溢出,给大一点
reg [7:0] rx_data;//

wire start_flag;

assign start_flag = (~urat_rxd_d0)&urat_rxd_d1; //检测串口接收端的下降沿  

always@(posedge sys_clk or negedge sys_rst_n) begin//异步信号同步处理
	if(!sys_rst_n) begin
		urat_rxd_d0 <= 1'b1;//串口数据端在默认情况下高电平
		urat_rxd_d1 <= 1'b1;
	end
	else begin//异步数据同步到系统时钟下,不同步可能会产生亚稳态,还能实现下降沿检测功能
		urat_rxd_d0 <= uart_rxd;
		urat_rxd_d1 <= urat_rxd_d0;
	end
end

always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rx_flag <= 1'b0;
	else if(start_flag)//检测到串口接收过程的起始信号
		rx_flag <= 1'b1;
	else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2 - 1'b1))//串口接收过程结束,rx_flag重新拉低
		rx_flag <= 1'b0;
end
	
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		clk_cnt <= 16'd0;
	else if(rx_flag) begin//clk_cnt只在rx_flag位高电平时开始计数
		if(clk_cnt < BPS_CNT - 1'b1)//只会计数到BPS_CNT - 1'b1
			 clk_cnt <= clk_cnt + 1'b1;
		else 
			 clk_cnt <= 16'd0;
	end
	else
		clk_cnt <= 16'd0;
end
		
always@(posedge sys_clk or negedge sys_rst_n) begin//传输数据计数,标记数据对应位
	if(!sys_rst_n)
		rx_cnt <= 4'd0;
	else if(rx_flag) begin
		if(clk_cnt == BPS_CNT - 1'b1)
			rx_cnt <= rx_cnt + 1'b1;
		else
			rx_cnt <= rx_cnt;
	end
	else
			rx_cnt <= 4'd0;	 
end    

always@(posedge sys_clk or negedge sys_rst_n) begin//实现串转并
	if(!sys_rst_n)
		rx_data <= 8'd0;
	else if(rx_flag && (clk_cnt == BPS_CNT/2 )) begin//在计数中间数据最稳定的时候进行寄存
		 case(rx_cnt)
		 	4'd1: rx_data[0] <=  urat_rxd_d1;//0位是起始标志位
		 	4'd2: rx_data[1] <=  urat_rxd_d1;
		 	4'd3: rx_data[2] <=  urat_rxd_d1;
		 	4'd4: rx_data[3] <=  urat_rxd_d1;
		 	4'd5: rx_data[4] <=  urat_rxd_d1;
		 	4'd6: rx_data[5] <=  urat_rxd_d1;
		 	4'd7: rx_data[6] <=  urat_rxd_d1;
		 	4'd8: rx_data[7] <=  urat_rxd_d1;
		 endcase
	end
end

always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		uart_done <= 1'b0;
		uart_data <= 8'd0;
	end
	else if(rx_cnt == 4'd9 ) begin
		uart_done <= 1'b1;
		uart_data <= rx_data;
	end
	else
		uart_done <= 1'b0;
		uart_data <= 8'd0;	
end
endmodule

串口发送模块示意图:

 uart_en只拉高没用,必须检测到上升沿;

代码:

//串口发送模块
`timescale 1ns / 1ps

module uart_send(
	input 				sys_clk,
	input 				sys_rst_n,
	input				uart_en,//

	input  [7:0] 		uart_din,//串转并

	output reg			uart_txd//串口发送端
    );

parameter CLK_FREQ = 50_000_000;
parameter UART_BPS = 115200;
parameter BPS_CNT  = CLK_FREQ/UART_BPS;  //clk_cnt需要计数的最大值

reg uart_en_d0;
reg uart_en_d1;
reg tx_flag;//串口发送过程
reg [3:0] tx_cnt;//计数0-9,4位
reg [15:0]clk_cnt;//波特率除系统时钟 50000000/115200=434左右,会计数434次左右,9位,考虑不同波特率时会溢出,给大一点
reg [7:0] tx_data;//en_flag一拉高立刻开始寄存uart_din信号

wire en_flag;

assign en_flag = uart_en_d0 & (~uart_en_d1); //检测串口发送端的上升沿  

always@(posedge sys_clk or negedge sys_rst_n) begin//异步信号同步处理
	if(!sys_rst_n) begin
		uart_en_d0 <= 1'b0;//串口数据端在默认情况下低电平
		uart_en_d1 <= 1'b0;
	end
	else begin//异步数据同步到系统时钟下,不同步可能会产生亚稳态,还能实现下降沿检测功能
		uart_en_d0 <= uart_en;
		uart_en_d1 <= uart_en_d0;
	end
end

always@(posedge sys_clk or negedge sys_rst_n) begin//把数据寄存到tx_data里面
	if(!sys_rst_n) 
		tx_data <= 8'b0;
	else if(en_flag)
		tx_data <= uart_din;
	else
		tx_data <= tx_data;	
end

always@(posedge sys_clk or negedge sys_rst_n) begin//在串口发送过程中tx_flag一直保持高电平
	if(!sys_rst_n)
		tx_flag <= 1'b0;
	else if(en_flag)//检测到串口发送过程的起始信号
		tx_flag <= 1'b1;
	else if((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2 - 1'b1))//串口发送过程结束,tx_flag
		tx_flag <= 1'b0;
end
	
always@(posedge sys_clk or negedge sys_rst_n) begin//clk_cnt对系统时钟进行计数,计数到波特率周期时清0
	if(!sys_rst_n)
		clk_cnt <= 16'd0;
	else if(tx_flag) begin//clk_cnt只在tx_flag位高电平时开始计数
		if(clk_cnt < BPS_CNT - 1'b1)//只会计数到BPS_CNT - 1'b1
			 clk_cnt <= clk_cnt + 1'b1;
		else 
			 clk_cnt <= 16'd0;
	end
	else
		clk_cnt <= 16'd0;
end
		
always@(posedge sys_clk or negedge sys_rst_n) begin//传输数据计数,标记数据对应位
	if(!sys_rst_n)
		tx_cnt <= 4'd0;
	else if(tx_flag) begin
		if(clk_cnt == BPS_CNT - 1'b1)
			tx_cnt <= tx_cnt + 1'b1;
		else
			tx_cnt <= tx_cnt;
	end
	else
			tx_cnt <= 4'd0;	 
end    

always@(posedge sys_clk or negedge sys_rst_n) begin//实现并转串
	if(!sys_rst_n)
		uart_txd <= 1'b1;//串口没有发送数据时为高电平
	else if(tx_flag && (clk_cnt == 16'd0 )) begin//
		 case(tx_cnt)
		 	4'd0: uart_txd <=  1'b0;//0位是起始标志位
		 	4'd1: uart_txd <=  tx_data[0];
		 	4'd2: uart_txd <=  tx_data[1];
		 	4'd3: uart_txd <=  tx_data[2];
		 	4'd4: uart_txd <=  tx_data[3];
		 	4'd5: uart_txd <=  tx_data[4];
		 	4'd6: uart_txd <=  tx_data[5];
		 	4'd7: uart_txd <=  tx_data[6];
		 	4'd8: uart_txd <=  tx_data[7];
		 	4'd9: uart_txd <=  1;
		 endcase
	end
end
endmodule

顶层:

module top_uart(
	input 				sys_clk,
	input 				sys_rst_n,
	
	input				uart_rxd,
	output				uart_txd
    );
 
 wire [7:0]				uart_data;
 wire					uart_done;
 
 uart_recv uart_recv_u(   
.sys_clk			(sys_clk),
.sys_rst_n			(sys_rst_n),

.uart_rxd			(uart_rxd),
.uart_data			(uart_data),
.uart_done			(uart_done)
 );
 
 uart_send uart_send_u(
.sys_clk				(sys_clk),
.sys_rst_n				(sys_rst_n),
.uart_en				(uart_done),
                      
.uart_din				(uart_data),
                       
.uart_txd               (uart_txd)
     );
 
 
endmodule

这篇关于ZYNQ&FPGA 串口通信实验的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!