UART通信实现与验证(RS232)

前言

        UART(通用异步收发传输器)是一种串行通信协议,用于在电子设备之间进行数据传输。RS232是UART协议的一种常见实现标准,广泛应用于计算机和外围设备之间的通信。它定义了串行数据的传输格式和电气特性,以确保不同设备之间的兼容性和可靠性。RS232协议采用异步串行通信方式,通过数据线将并行数据转换为串行数据进行传输,接收端再将串行数据恢复为并行数据。RS232的标准包括信号电平、连接器类型和数据格式等规范,确保了设备之间的正确通信。它的常见应用包括计算机串口、调制解调器、打印机等设备的连接。

        在实际应用中,实现RS232通信需要配置合适的硬件和软件,并进行详细的验证测试。硬件方面,通常需要RS232接口电路和相应的连接线;软件方面,则需要配置数据传输参数(如波特率、数据位、停止位、校验位)和进行通信协议的编程。验证过程包括测试通信的稳定性、数据传输的准确性以及处理异常情况的能力。

        本篇内容将探讨RS232通信的实现方法和验证步骤,旨在提供对UART通信协议的深入理解和实践经验,以支持在实际应用中的有效部署和故障排除。

正文

一、UART通信实现与验证(RS232)

        1.项目需求

        基于FPGA,设计验证UART通信的RS232接口数据收发实验。

        2.技术介绍

        常用的三种低速数据总线:UART,SPI,IIC,
        UART 通用异步收发传输器,全双工通信,UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。包括RS232、 RS499、 RS423、 RS422和RS485等接口标准规范和总线标准规范。

        SPI 串行外围接口,是一种用于串行数据传输的协议,用于微控制器和外围设备之间的通信。是一种同步、全双工的串行通信协议,主要用于微控制器和外围设备之间的数据传输。它通过四条主要信号线(MOSI、MISO、SCK和SS)实现主从设备之间的通信,允许在全双工模式下进行高效的数据交换。SPI协议支持灵活的时钟极性和相位设置,广泛应用于各种电子设备中。

        IIC 是一种双线制的串行通信协议,通过数据线(SDA)和时钟线(SCL)实现主从设备之间的数据交换。它支持多个主设备和从设备,采用地址分配来选择从设备,适用于短距离、低速的数据传输,广泛应用于各种电子设备中。

        RS232传输距离近,速度慢,但是电子设备普遍留有该接口方便通信,使用两根信号线进行通信,节省资源。

        PC机通过串口调试助手向FPGA发送8Bit数据,数据串行发送(从低到高),在FPGA内部拼凑为8Bit数据,FPGA通过串口串行发送数据给串口(从低到高),完成串口通信。基于帧结构(数据包),起始位+8Bit数据+停止位;完成一个帧结构(一包数据)(10bit),tx与rx在空闲状态下拉高。(串口每次只发送1Bit数据)

这里介绍几个知识点,本实验会遇到与之相关问题:

        亚稳态:实际信号跳变不是立刻跳变,有个渐变过程,在时钟信号下进行检测,如果时钟检测时刻刚好在这个渐变过程,会产生毛刺或震荡,在下个检测时钟到来之前,震荡结束后信号01不定.消除方法:使用多级寄存器(打拍)。

        波特率:码元(携带数据系统的信号单元,在UART RS232中表示一个二进制数)传输速率即波特率,单位bps。
波特  :串口接收或发送1bit数据的时间,例9600bps对应1/9600s
比特率:每秒中通信信道传输的信息量,位传输速率简称比特率,单位bit/s。
比特率=波特率*每个调制状态对应的二进制数
例:9600bps的UART RS232对应比特率=9600bps*1=9600bit/s

        单比特信号从高速时钟域同步到低速时钟域,使用打拍方式(计数器)会有数据漏采,常使用脉冲同步或握手信号进行数据同步,多比特数据进行传输时先要进行格雷码转换,或使用fifo或ram进行数据同步。

        3.顶层架构

        4.端口描述

clk时钟接口(50Mhz)
rst_n复位按键(低电平有效)
rx数据接收接口
tx数据发送接口

二、代码验证

uart_rx模块:数据接收模块,进行数据读取和转化

module uart_rx(
	input 			clk		,
	input				rst_n 	,
	input				rx			,
	
	output reg[7:0]po_data	,	//接收到的数据
	output reg  	po_flag		//数据输出有效

);

parameter 	uart_btl ='d9600			;//串口波特率
parameter 	clk_shuj ='d50_000_000	;//时钟频率

parameter 	cnt_max  =clk_shuj/uart_btl;

reg 			reg1,reg2,reg3	;//打排延迟周期,消除亚稳态
reg 			flag				;//uart工作信号(uart工作时在工作状态切换后产生一个时钟周期高电平)
reg 			en					;//uart工作使能标志信号(uart工作时在工作状态切换后的下一个时钟周期开始一直拉高,直到检测到停止信号)
reg [15:0]	cnt				;//每比特数据持续时钟周期计数器
reg [3 :0]	bit_cnt			;//数据个数计数器
reg 			bit_flag			;//每bit数据接收有效信号
reg [7 :0]	rx_data			;//存储输入数据
reg			rx_flag			;//输入数据并位结束信号

always@(posedge clk,negedge rst_n)//数据打排1
begin
	if(rst_n == 0)
		reg1 <= 1'b1;
	else
		reg1 <= rx;
end	

always@(posedge clk,negedge rst_n)//数据打排2
begin
	if(rst_n == 0)
		reg2 <= 1'b1;
	else
		reg2 <= reg1;
end	

always@(posedge clk,negedge rst_n)//数据打排3
begin
	if(rst_n == 0)
		reg3 <= 1'b1;
	else
		reg3 <= reg2;
end	

always@(posedge clk,negedge rst_n)//uart工作信号赋值
begin
	if(rst_n == 0)
		flag <= 1'b0;
	else
		if((reg2 == 1'b0)&&(reg3 == 1'b1)&&(en == 1'b0))
			flag <= 1'b1;
		else
			flag <= 1'b0;
end

always@(posedge clk,negedge rst_n)//uart工作使能标志信号赋值
begin
	if(rst_n == 0)
		en <= 1'b0;
	else
		if(flag == 1'b1)
			en <= 1'b1;
		else
			if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
				en <= 1'b0;
			else
				en <= en;
end			

always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
	if(rst_n == 0)	
		cnt <= 16'd0;
	else 	
		if((cnt == cnt_max - 1)||(en == 1'b0))
			cnt <= 16'd0;
		else
			cnt = cnt + 1'b1;
end

always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
	if(rst_n == 0)	
		bit_flag <= 1'b0;
	else
		if(cnt == cnt_max/2 - 1)
			bit_flag <= 1'b1;
		else
			bit_flag <= 1'b0;
end

always@(posedge clk,negedge rst_n)//数据个数计数器驱动逻辑
begin
	if(rst_n == 0)	
		bit_cnt <= 4'd0;
	else	
		if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
			bit_cnt <= 4'd0;
		else 
			if(bit_flag == 1'b1)
				bit_cnt <= bit_cnt + 1'b1;
			else
				bit_cnt <= bit_cnt;
end

always@(posedge clk,negedge rst_n)//接收数据并位
begin
    if(rst_n == 0)
        rx_data <= 8'd0;
    else
        if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
            rx_data <= {reg3,rx_data[7:1]};
end

always@(posedge clk,negedge rst_n)//输入数据并位结束信号
begin
    if(rst_n == 0)
        rx_flag <= 1'b0;
    else 
        if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
            rx_flag <= 1'b1;
        else
            rx_flag <= 1'b0;
end

always@(posedge clk,negedge rst_n)//输出数据传递
begin
    if(rst_n == 0)
        po_data <= 8'd0;
    else 
        if(rx_flag == 1'b1)
            po_data <= rx_data;
        else
            po_data <= po_data;
end

always@(posedge clk,negedge rst_n)//输出使能
begin
    if(rst_n == 0)
        po_flag <= 1'b0;
    else 
        po_flag <= rx_flag;
end

endmodule

uart_tx模块:数据传出模块

module uart_tx(

    input       clk     ,
    input       rst_n   ,
    input [7:0] pi_data ,
    input       pi_flag ,
    
    output reg  tx
);

parameter 	uart_btl ='d9600			;//串口波特率
parameter 	clk_shuj ='d50_000_000	;//时钟频率

parameter 	cnt_max  =clk_shuj/uart_btl;

reg         en      ;
reg [15:0]  cnt     ;//每bit数据传输完成计数器
reg         flag    ;//
reg [3 :0]  bit_cnt ;//bit计数器

always@(posedge clk,negedge rst_n)
begin 
    if(rst_n == 0)
        en <= 1'b0;
    else
        if(pi_flag == 1'b1)
            en <= 1'b1;
        else
            if((bit_cnt == 4'd9)&&(flag == 1'b1))
                en <= 1'b0;
            else 
                en <= en;          
end

always@(posedge clk,negedge rst_n)
begin
    if(rst_n == 0)
        cnt <= 16'd0;
    else
        if((en == 1'b0)||(cnt == cnt_max - 1'b1))
            cnt <= 16'd0;
        else
            if(en == 1'b1)
                cnt <= cnt + 1'b1;
            else
                cnt <= cnt;
end

always@(posedge clk,negedge rst_n)
begin
    if(rst_n == 0)
        flag <= 1'b0;
    else 
        if(cnt == 16'd1)
           flag <= 1'b1; 
        else
           flag <= 1'b0;
end

always@(posedge clk,negedge rst_n)
begin
    if(rst_n == 0)
        bit_cnt <= 4'd0;
    else
        if((bit_cnt == 4'd9)&&(flag == 1'b1))
            bit_cnt <= 4'd0;
        else
            if((en == 1'b1)&&(flag == 1'b1))
                bit_cnt <= bit_cnt + 1'b1;
            else
                bit_cnt <= bit_cnt;
end                
            
always@(posedge clk,negedge rst_n)
begin
    if(rst_n == 0)
        tx <= 1'b1;
    else
        if(flag == 1'b1)
            case(bit_cnt)
                0:  tx <= 1'b0;
                1:  tx <= pi_data[0];
                2:  tx <= pi_data[1];
                3:  tx <= pi_data[2];
                4:  tx <= pi_data[3];
                5:  tx <= pi_data[4];
                6:  tx <= pi_data[5];
                7:  tx <= pi_data[6];
                8:  tx <= pi_data[7];
                9:  tx <= 1'b1;
                default :tx <= 1'b1;
            endcase    
end

endmodule

顶层连线

module uart_rs232(

	 input       clk     ,
    input       rst_n   ,
    input       rx 		,
    
    output 		 tx		
);

wire [7:0]	pi_data	;
wire 			pi_flag	;

uart_tx uart_tx_inst(

    .clk    (clk    ) ,
    .rst_n  (rst_n  ) ,
    .pi_data(pi_data) ,
    .pi_flag(pi_flag) ,
 
    .tx		(tx		)		
);

uart_rx uart_rx_inst(
	.clk		(clk		),
	.rst_n 	(rst_n 	),
	.rx		(rx		),
	         
	.po_data	(pi_data ),	//接收到的数据
	.po_flag	(pi_flag )//数据输出有效

);

endmodule

仿真代码

`timescale 1ns/1ps

module uart_rs232_tb;
	
	reg	clk	;
	reg	rst_n	;
	reg	rx		;

	wire 	tx		;
	
	
uart_rs232 uart_rs232_inst(

	 .clk    (clk		),
    .rst_n  (rst_n	),
    .rx 		(rx		),
             
    .tx		(tx		)
);

initial clk = 1'b1;
always #10 clk = ~clk;

initial begin
	rst_n = 1'b0;
	rx = 1'b1;
	#40
	rst_n = 1'b1;
	#200
	re_byte();
	#1000000
	$stop;
end
//赋值函数
		task	rx_bit(

			input [7:0]data
			
		);

		integer i;

		for(i = 0;i < 10; i = i + 1)//循环9次
				begin
					case(i)
						0:  rx <= 1'b0;
						1:  rx <= data[0];
						2:  rx <= data[1];
						3:  rx <= data[2];
						4:  rx <= data[3];
						5:  rx <= data[4];
						6:  rx <= data[5];
						7:  rx <= data[6];
						8:  rx <= data[7];
						9:  rx <= 1'b1;
					endcase
				#(5208*20);//每次延时
				end
		endtask
	
		task re_byte();
			integer j;
				for(j = 0;j < 8;j = j + 1)
					rx_bit(j);//传递0——7,8个数据
		endtask
	
endmodule

三、仿真验证

运行仿真,对信号进行分组,调出中间变量进行分析:

如上图,数据可以正常传出,放大波形图进行数据验证:传入数据01234567,第一个flag信号(接收数据有效信号)即data[0],下图中黄线位置,

具体数据可以观察下图位置,选中flag,选中如图图标。

第一个flag信号(接收数据有效信号)即data[0]=0,上图中黄线位置.第2个flag信号(接收数据有效信号)即data[1]=1,下图中黄线位置。

第八个flag信号(接收数据有效信号)即data[7]=7下图中黄线位置

可以看到数据传入正常,验证数据输出,可以在仿真文件中调用两个顶层文件,在代码基础上将tx接入代第二个顶层代码的rx位置,观察在第二个中数据是否正确被解析。这里不做验证。

参考资料

RS-232

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张明阳.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值