RS232串口通信(UART的接收部分)

RS232串口通信(UART的接收部分)

置顶

新人博主,创作不易,波形图手绘,请给个点赞关注吧,非常感谢!

串口简介

​ 串口作为常用的三大低速总线(UART、SPI、IIC)之一,在设计众多通信接口和调试 时占有重要地位。 其中UART是异步通信,通信双方是通过起始位停止位来实现信息同步的。SPI和IIC都是同步通信的,通信双方使用统一的时钟频率,在数据传输过程中时钟伴随着数据一起传输,发送方和接收方使用的时钟都是由主机提供的。

串口的优点

  1. 很多传感器芯片或 CPU 都带有串口功能,目的是在使用一些传感器或 CPU时可以通过串口进行调试,十分方便;
  2. 在较为复杂的高速数据接口和数据链路集合的系统中往往联合调试比较困难,可以先使用串口将数据链路部分验证后,再把串口换成高速数据接口。如在做以太网相关的项目时,可以在调试时先使用串口把整个数据链路调通,然后再把串口换成以太网的接口;
  3. 串口的数据线一共就两根,也没有时钟线,节省了大量的管脚资源。

UART

UAR通信只有两根线,一根是发送数据端口线叫tx(Transmitter),一根是接 收数据端口线叫rx(Receiver)。如图所示,对于 PC 来说它的 tx 要和对于 FPGA 来 说的 rx 连接,同样 PC 的 rx 要和 FPGA 的 tx 连接,所以rx和tx都是对于自身来说的。UART 可以实现全双工,即可以同时进行发送数据和接收数据。

image-20220329095033425

串口的通信协议

  1. rx的位宽1bit,从最低位到最高位依次接收

  2. tx的位宽1bit,从最低位到最高位发送

  3. RS232的最基本的帧结构

    image-20220329102936250

  4. 波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)

  5. 比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为“每秒比特数(bps)”

实验目标

​ 如图所示,做一个回环测试来验证设计模块的正确性,即发送端发送什么数据,接收端就收到什么数据。

image-20220329095738944

串口接收模块

信号说明

信号位宽类型功能描述
sys_clk1bitinput工作频率,系统时钟50Mhz
sys_rst_n1bitinput系统复位信号
rx1bitinput串口接收信号
po_data8bitoutput串口接收8位rx,转为8位数据
po_data_flag1bitoutput串口接收后转成的 8bit 数据有效标志信号

接收框图

image-20220329112811407

波形设计

起始下降沿

使用打三拍的方式是为了防止亚稳态的出现而导致数据传输错误。这里对亚稳态不做过多的介绍。

接收模块开始接收的信号是通过rx_reg2和rx_reg3产生的下降沿作为起始信号。具体如下波形图所示。

img

//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(!sys_rst_n)
        rx_reg1<=1'd1;
    else
        rx_reg1<=rx;        
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(!sys_rst_n)
        rx_reg2<=1'd1;//复位的是reg=1
    else
        rx_reg2<=rx_reg1;        
end

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;    
end

工作使能信号

​ 我们已经生成了一个开始的下降沿信号,通过这个信号我们可以生成一个工作使能信号,这个信号的作用就是,当数据在传输时为1,否则为0。

image-20220329113841308

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

波特率计数信号

​ 我们使用的是 9600bps 的波特率 和 PC 机进行串口通信,PC 机的串口调试助手要将发送数据波特率调整为 9600bps。而 FPGA 内部使用的系统时钟是 50MHz,前面也进行过计算,得出 1bit 需要的时间约为 5208 个(因为一帧只有 10bit,细微的近似计数差别不会产生数据错误,但是如果计数值差的过 大,则会产生接收数据的错误)系统时钟周期,那么我们就需要产生一个能计 5208 个数的 计数器来依次接收 10 个比特的数据,计数器每计 5208 个数就接收一个新比特的数据。

parameter UART_BPS = 'd9600; //串口波特率
parameter CLK_FREQ = 'd50_000_000; //时钟频率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;//定义波特率的计数值
//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)//当开始传输的时候波特率计数器开始计数
        baud_cnt <= baud_cnt + 1'b1;

位标志信号

​ band_cnt数据在0和5027这两个点的时候是不太稳定的,所以我采用的是中间的点作为bit_flag的高电平的开始。

image-20220329115520847

//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;     

数据bit传输个数

​ 如图数据帧中可以看出,需要传输8bit的数据,所以我们要设置一个信号来记录已经传输了的数据bit数,定义为bit_cnt信号,每次bit_flag信号置高的时候,bit_cnt +1。

image-20220329145123046

​ 对于bit_cnt什么时候清0是我们比较关注的,从上述中可以很轻易的知道其中一个条件是bit_cnt=8的时候,另一个条件是bit_flag为高(这个条件是确保第8位数据已经传输完毕)。

image-20220329150315966

bit_cnt清0相当于数据传输结束,所以此时work_en信号置低。

image-20220329151605412

//work_en:接收数据工作使能信号(work_en置低)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;
//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)//当开始传输的时候波特率计数器开始计数
        baud_cnt <= baud_cnt + 1'b1;

串转并输出

​ 在这里定义一个rx_flag 信号,当数据传输完毕的时候,这个信号拉高一个电平,并且在下一个时钟输出,即:Po_data_flag=rx_flag ,

并且通过移位的方式加rx输入信号转为并行信号po_data 输出。

image-20220329152527108

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;
//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;
解释一下为什么要定义rx_flag

​ 其实定义这个信号的原因是为了并行数据输出和输出标志信号同步,这里读者自行体会。

整体波形图

image-20220329152505594

总结

若有啥疑问和想要完整的工程文件请私聊我 QQ:330965825 ,谢谢!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值