UART串口通信协议的FPGA实现

本文介绍了UART串口通信协议的基本概念,包括波特率、起始位、数据位、校验位、停止位和空闲位,并通过时序图详细阐述其工作原理。接着,给出了Verilog语言实现UART传输发送(uart_tx.v)、接收(uart_rx.v)以及仿真测试模块(uart_tb.v)的代码,帮助理解UART在FPGA中的实际应用。

想深耕嵌入式?这个专辑值得收藏

MCU、FPGA、工控、传感器一站式学习,实战项目直接抄

引言

      UART串口通信协议,全称叫做通用异步收发器(Universal Asynchronous Receiver/Transmitter),通常称作UART。UART是异步通信,它只需要一根线就可以进行数据的通信。

1、基本概念

  • 波特率:指每秒传输的bit位数(bit)。一般波特率配置为4800,9600,115200等;
  • 起始位:先发出一个逻辑”0”信号,表示传输字符的开始;
  • 数据位:可以是5~8位逻辑”0”或”1”。一般情况下都选择8位而不选择7,因为这样能尽可能避免数据的丢失或者混乱。从数据低位开始传输(即从LSB端开始发送数据,而且是串行发送);
  • 校验位:当数据位加上校验位后,当1”的位数为偶数时,叫做偶校验,为奇数时,叫做奇校验,主要用于差错控制;
  • 停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平;
  • 空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。

   具体的时序图如下图所示:

在这里插入图片描述

2、Verilog实现代码

  • uart_tx.v
module uart_tx #(
parameter BAUD_DIV = 14'd10416     //波特率时钟,9600bps,100Mhz/9600=10416,波特率可调,意为一位数据占用多少时钟周期
)
(
input clk,                   //100M时钟
input [7:0] uart_tx_data_i,    //待发送数据
input uart_tx_en_i,            //发送发送使能信号
output uart_tx_o,               //串口输出
output uart_tx_done         // 串口发送结束信号 1:正在发送 0:发送完成
    );
    
parameter BAUD_DIV_CAP = BAUD_DIV / 2;      //波特率时钟中间采样点,100Mhz/9600/2=5208,波特率可调
 
 //-------------------------串口计数,中值采样----------------------  
reg [13:0] baud_div=0;              //波特率设置计数器
reg baud_bps=0;                     //数据发送点信号,高有效
reg uart_send_flag=0;             //数据发送标志位
    
always@(posedge clk)
begin
    if(baud_div==BAUD_DIV_CAP)    //当波特率计数器计数到数据发送中点时,产生采样信号baud_bps,用来发送数据
        begin
            baud_bps<=1'b1;
            baud_div<=baud_div+1'b1;
        end
    else if(baud_div<BAUD_DIV && uart_send_flag)//数据发送标志位有效期间,波特率计数器累加,以产生波特率时钟
        begin
            baud_div<=baud_div+1'b1;
            baud_bps<=0;    
        end
    else
        begin
            baud_bps<=0;
            baud_div<=0;
        end
end

//------------------------生成串口发送标志位,串口数据加载与卸载--------------------------
reg [9:0] send_data=10'b1111111111;//待发送数据寄存器,1bit起始信号+8bit有效信号+1bit结束信号
reg [3:0] bit_num=0;    //发送数据个数计数器
reg uart_tx_o_r=1;        //发送数据寄存器,初始状态位高 
    
always@(posedge clk)
begin
    if(uart_tx_en_i)    //接收数据发送使能信号时,产生数据发送标志信号
        begin
            uart_send_flag<=1'b1;
            send_data<={1'b1,uart_tx_data_i,1'b0};//待发送数据寄存器装填,1bit起始信号0+8bit有效信号+1bit结束信号
        end
    else if(bit_num==4'd10)    //发送结束时候,清楚发送标志信号,并清除待发送数据寄存器内部信号
        begin
            uart_send_flag<=1'b0;
            send_data<=10'b1111_1111_11;
        end
end
 
 //-------------------------串口数据发送----------------------------   
always@(posedge clk)
begin
    if(uart_send_flag)    //发送有效时候
        begin
            if(baud_bps)//检测发送点信号
                begin
                    if(bit_num<=4'd9)
                        begin
                            uart_tx_o_r<=send_data[bit_num];    //发送待发送寄存器内数据,从低位到高位
                            bit_num<=bit_num+1'b1;
                        end
                end
            else if(bit_num==4'd10)
                bit_num<=4'd0;
        end
    else
        begin
            uart_tx_o_r<=1'b1;    //空闲状态时,保持发送端位高电平,以备发送时候产生低电平信号
            bit_num<=0;
        end
end

assign uart_tx_o=uart_tx_o_r;
assign uart_tx_done = uart_send_flag;
endmodule

  • uart_rx.v 
module uart_rx #(
parameter [13:0] BAUD_DIV     = 14'd10416 //波特率时钟,9600bps,100Mhz/9600=10416
)
(
	input clk,
	input uart_rx_i,
	
	output [7:0] uart_rx_data_o,
	output uart_rx_done
 );
  
parameter  BAUD_DIV_CAP = BAUD_DIV /2;//波特率时钟中间采样点,100Mhz/9600/2=5208


//------------------------计数控制-------------------------------
reg [13:0] baud_div=0;				            //波特率设置计数器
reg baud_bps=0;					                //数据采样点信号
reg bps_start=0;					            //波特率启动标志
always@(posedge clk)
begin
	if(baud_div==BAUD_DIV_CAP)		            //当波特率计数器计数到采样点时,产生采样信号baud_bps
		begin
			baud_bps<=1'b1;
			baud_div<=baud_div+1'b1;
		end
	else if(baud_div<BAUD_DIV && bps_start)    //当波特率计数器启动时,计数器累加
		begin
			baud_div<=baud_div+1'b1;
			baud_bps<=0;
		end
	else
		begin
			baud_bps<=0;
			baud_div<=0;
		end
end

//--------------------通过低电平判断是否有数据输入---------------------
reg [4:0] uart_rx_i_r=5'b11111;			      //数据接收缓存器
always@(posedge clk)
begin
	uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i};
end
//数据接收缓存器,当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号
wire uart_rx_int=uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];


//------------------------数据接收----------------------------------------
reg [3:0] bit_num=0;	//接收数据个数计数器
reg uart_rx_done_r=0;	//数据接收完成寄存器
reg state=1'b0;

reg [7:0] uart_rx_data_o_r0=0;//数据接收过程中,数据缓存器
reg [7:0] uart_rx_data_o_r1=0;//数据接收完成,数据寄存器

always@(posedge clk)
begin
	uart_rx_done_r<=1'b0;
	case(state)
		1'b0 : 
			if(!uart_rx_int)//当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号,启动波特率时钟
				begin
					bps_start<=1'b1;
					state<=1'b1;
				end
		1'b1 :			
			if(baud_bps)	//每次等待波特率采样中心时,接收数据,放入数据缓存器中
				begin
					bit_num<=bit_num+1'b1;
					if(bit_num<4'd9)	//接收1bit起始信号,8bit有效信号,1bit结束信号
						uart_rx_data_o_r0[bit_num-1]<=uart_rx_i;
				end
			else if(bit_num==4'd10) //接收完成时候,接收数据个数计数器清零,产生接收完成标志位,并将数据写入数据寄存器,关闭波特率时候
				begin
					bit_num<=0;
					uart_rx_done_r<=1'b1;
					uart_rx_data_o_r1<=uart_rx_data_o_r0;
					state<=1'b0;//进入状态0,再次循环检测
					bps_start<=0;
				end
		default:;
	endcase
end
assign uart_rx_data_o=uart_rx_data_o_r1;		
assign uart_rx_done=uart_rx_done_r;
endmodule

  •  uart_tb.v
`timescale 1ns/1ps
module uart_tb ();
 
reg clk;                  //100M时钟
reg [7:0] uart_tx_data_i;   //待发送数据
reg uart_tx_en_i;            //发送发送使能信号
wire uart_tx_o;              //串口输出
wire uart_tx_done;        // 串口发送结束信号 1:正在发送 0:发送结束

wire [7:0] uart_rx_data_o;//接受并行数据
wire uart_rx_done;//接收结束信号

reg [8:0] tx_data_r[0:39];//待发送数据寄存数组
reg [5:0] tx_icount;//发送计数
initial
begin
	clk = 1'b1;
	//寄存器组赋初值
	$readmemh("F:/PID/uart/tx_data.txt",tx_data_r);
	tx_icount = 6'd0;
end

always #5 clk = ~clk;
//输入数据
always @ (posedge clk) begin
	if(!(uart_tx_done) && (!uart_tx_en_i))begin//控制上一数据传输完成然后输入下一数据
		uart_tx_en_i <= 1'b1;
		uart_tx_data_i <= tx_data_r[tx_icount];
		tx_icount <= tx_icount + 1'd1;
		if(tx_icount == 6'd40)
			tx_icount <= 6'd0;
	end
	else 
		uart_tx_en_i <= 1'b0;
end
uart_tx tx_demo(
	.clk(clk),
	.uart_tx_data_i(uart_tx_data_i),
	.uart_tx_en_i(uart_tx_en_i),
	.uart_tx_o(uart_tx_o),
	.uart_tx_done(uart_tx_done)
	);

uart_rx rx_demo(
	.clk(clk),
	.uart_rx_i(uart_tx_o),
	.uart_rx_data_o(uart_rx_data_o),
	.uart_rx_done(uart_rx_done)
	);

endmodule
  • 仿真波形

想深耕嵌入式?这个专辑值得收藏

MCU、FPGA、工控、传感器一站式学习,实战项目直接抄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值