野火FPGA跟练(四)——串口RS232、亚稳态

2024-06-09 20:04

本文主要是介绍野火FPGA跟练(四)——串口RS232、亚稳态,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 简介
  • 接口与引脚
  • 通信协议
  • 亚稳态
  • RS232接收模块
    • 模块框图
    • 时序波形
    • RTL 代码
    • 易错点
    • Testbench 代码
    • 仿真
  • RS232发送模块
    • 模块框图
    • 时序波形
    • RTL 代码
    • Testbench 代码
    • 仿真


简介

  • UART:Universal Asynchronous Receiver/Transmitter,异步串行通信接口。发送数据时并行转串行,接收数据时串行转并行。
  • RS232:UART包括多种接口标准规范和总线标准规范,RS232为其一,还有RS499、RS423、RS422、RS485等。

接口与引脚

在这里插入图片描述在这里插入图片描述
重点关注RXD、TXD即可。

通信协议

  • 帧结构:包含8bit有效数据和起始位、停止位。空闲状态是高电平,起始位低电平,停止位高电平。低位数据先发,后发高位。
    在这里插入图片描述
    假设我要传输字符“1”,对应的ASCII码值是0x31,即“0011 0001” ,低位先发,那么我的这帧数据波形为 0 1000 1100 1

  • 波特率:假设波特率为9600Bps,传输一个码元(一个码元在这里代表一个二进制数,就是1bit数据)需要1/9600秒。假设系统时钟50MHz(周期为20ns),那么传输1bit数据所需的时间为 (1/9600)/(20*e-9) = 5208.33 ≈ 5208 个时钟周期。

亚稳态

  • 产生原因:
    触发器采集输入信号时需要一定的建立时间和保持时间,如果在这一期间输入信号发生变化,那么触发器将无法稳定输出,导致输出信号在高低电平间快速振荡,即进入亚稳态。

  • 不良影响:
    如果处于亚稳态的信号直接接入组合逻辑电路,会导致亚稳态在整个系统中传递,从而影响整个电路的运行,导致不稳定。

  • 解决方案:
    单比特信号可以采用“打两拍”,多比特信号可以采用异步FIFO、格雷码、握手。

RS232接收模块

模块框图

在这里插入图片描述
输入输出描述:

  1. 时钟:50MHz,每10ns翻转一次
  2. 复位:高电平异步复位
  3. RX:接收的串口数据
  4. DATA:处理后的数据,并行输出
  5. FLAG:置高时代表已处理好一帧数据,只维持一个时钟周期

时序波形

示意时序图,以9600波特率为例。

在这里插入图片描述

时序图分析:

  • RX_REG
    • RX_REG1:是将 RX 同步到时钟信号上。
    • RX_REG2、3:打两拍,避免亚稳态。
  • START_FLAG 标志数据接收的开始、WORK_EN 标志数据接收的状态
    • START_FLAG:这里使用的是组合方式赋值,通过 RX_REG2、3 检测 RX_REG 的下降沿。由于 RX_REG 的数据位中也有可能存在下降沿,所以 START_FLAG 置高的前提条件还应有 WORK_EN 为 0 。
    • WORK_EN:WORK_EN 通过判断 START_FLAG 的状态而置高,通过判断 BIT_CNT、BIT_FLAG 的状态而置低,其余时刻保持不变。
  • BAUD_CNT、BIT_FLAG
    • BAUD_CNT:BAUD_CNT 只在 WORK_EN 为高时计数
    • BIT_FLAG:在每比特数据传输时的中间时刻拉高
  • RX_DATA
    由于是低位数据先发,所以 RX_REG 的数据存在 RX_DATA 的高位,每次更新时 RX_DATA 右移,实现数据的串行转并行。RX_DATA 的值在 BAUD_CNT 为 1-8 并且 BIT_FLAG 为高时采集 RX_REG。

RTL 代码

module RS232_RX
#(parameter UART_BPS = 'd9600,parameter CLK_FREQ = 'd50_000_000
)
(input   wire         clk,input   wire         rst,input   wire         rx,output  reg   [7:0]  data,output  reg          flag);parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;reg rx_reg1;reg rx_reg2;reg rx_reg3;wire start_flag;reg work_en;reg [16:0] baud_cnt;reg bit_flag;reg [3:0] bit_cnt;reg [7:0] rx_data;reg data_flag;// 赋值rx_reg1always@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_reg1 <= 1'b1;elserx_reg1 <= rx;end// 赋值rx_reg2always@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_reg2 <= 1'b1;elserx_reg2 <= rx_reg1;end// 赋值rx_reg3always@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_reg3 <= 1'b1;elserx_reg3 <= rx_reg2;end// 赋值start_flagassign start_flag = ((rx_reg2 == 1'b0) && (rx_reg3 == 1'b1));// 赋值work_enalways@(posedge clk or posedge rst) beginif(rst == 1'b1)work_en <= 1'b0;else if(start_flag == 1'b1)work_en <= 1'b1;else if((bit_flag == 1'b1) && (bit_cnt == 4'd8))work_en <= 1'b0;elsework_en <= work_en;end// 赋值baud_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)baud_cnt <= 17'b0;else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))baud_cnt <= 17'b0;elsebaud_cnt <= baud_cnt + 1'b1;end // 赋值bit_flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_flag <= 1'b0;else if(baud_cnt == BAUD_CNT_MAX / 2 - 1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;end  // 赋值bit_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_cnt <= 4'b0;else if((bit_flag == 1'b1) && (bit_cnt == 4'd8))bit_cnt <= 4'b0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;end// 赋值rx_dataalways@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_data <= 8'b0;else if((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8) && (bit_flag == 1'b1))rx_data <= {rx_reg3,rx_data[7:1]};elserx_data <= rx_data;end// 赋值data_flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)data_flag <= 1'b0;else if((bit_flag == 1'b1) && (bit_cnt == 4'd8))data_flag <= 1'b1;elsedata_flag <= 1'b0;end// 赋值dataalways@(posedge clk or posedge rst) beginif(rst == 1'b1)data <= 8'd0;else if(data_flag == 1'b1)data <= rx_data;elsedata <= data;end// 赋值flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)flag <= 1'b0;elseflag <= data_flag;end
endmodule

易错点

如果数据是多位的,赋值时需要注意其位宽大小。如果一个多位宽的数据赋值为“1’b0”,这将只赋值给这个数据的最低位,其他位将保留原值,与本意相悖。

Testbench 代码

`timescale 1ns / 1ps
module tb_RS232_RX();reg     clk;reg     rst;reg     rx;wire [7:0]  data;wire    flag;// 初始化clk和rstinitial beginclk <= 1'b0;rst <= 1'b1;#60;rst <= 1'b0;end always #10 clk = ~clk;// 初始化rxinitial beginrx <= 1'b1;#100;rx_bit(8'b1001_0001);rx_bit(8'b0101_0010);rx_bit(8'b1110_0101);rx_bit(8'b0110_1000);rx_bit(8'b0110_1011);rx_bit(8'b0011_0110);rx_bit(8'b0001_1110);end// rx_bit函数,生成串行数据task rx_bit(input [7:0] rx_data);integer i;for(i = 0; i < 10; i = i + 1) begincase(i)0: rx <= 1'b0;1: rx <= rx_data[0];2: rx <= rx_data[1];3: rx <= rx_data[2];4: rx <= rx_data[3];5: rx <= rx_data[4];6: rx <= rx_data[5];7: rx <= rx_data[6];8: rx <= rx_data[7];9: rx <= 1'b1;endcase#(5208*20);   end         endtask// 实例化模块RS232_RX #(.UART_BPS(9600),.CLK_FREQ(50_000_000))tb_RS232_RX(.clk      (clk  ),.rst      (rst  ),.rx       (rx   ),.data     (data ),.flag     (flag ));
endmodule

仿真

在这里插入图片描述

RS232发送模块

模块框图

在这里插入图片描述
输入输出描述:

  1. 时钟:50MHz,每10ns翻转一次
  2. 复位:高电平异步复位
  3. DATA:待发送的并行数据
  4. FLAG:数据标志位
  5. TX:输出处理后的串行数据

时序波形

示意时序图,以9600波特率为例。

在这里插入图片描述

时序图分析:

  • WORK_EN:WORK_EN 通过判断 START_FLAG 的状态而置高,通过判断 BIT_CNT、BIT_FLAG 的状态而置低,其余时刻保持不变。
  • BAUD_CNT、BIT_FLAG
    • BAUD_CNT:BAUD_CNT 只在 WORK_EN 为高时计数
    • BIT_FLAG:在 BAUD_CNT 计数为1时刻拉高

RTL 代码

module RS232_TX
#(parameter UART_BPS = 'd9600,parameter CLK_FREQ = 'd50_000_000
)
(input wire clk,input wire rst,input wire [7:0] data,input wire flag,output reg tx);parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;reg work_en;reg [16:0] baud_cnt;reg bit_flag;reg [3:0] bit_cnt;// 赋值work_enalways@(posedge clk or posedge rst) beginif(rst == 1'b1)work_en <= 1'b0;else if(flag == 1'b1)work_en <= 1'b1;else if((bit_flag == 1'b1) && (bit_cnt == 4'd10))work_en <= 1'b0;elsework_en <= work_en;end// 赋值baud_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)baud_cnt <= 17'b0;else if((baud_cnt == BAUD_CNT_MAX - 1'b1) || (work_en == 1'b0))baud_cnt <= 17'b0;elsebaud_cnt <= baud_cnt + 1'b1;end// 赋值bit_flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_flag <= 1'b0;else if(baud_cnt == 17'b1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;end// 赋值bit_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_cnt <= 4'b0;else if((bit_flag == 1'b1) && (bit_cnt == 4'd10))bit_cnt <= 4'b0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;end// 赋值txalways@(posedge clk or posedge rst) beginif(rst == 1'b1)tx <= 1'b1;else begincase(bit_cnt)4'd1: tx <= 1'b0;4'd2: tx <= data[0];4'd3: tx <= data[1];4'd4: tx <= data[2];4'd5: tx <= data[3];4'd6: tx <= data[4];4'd7: tx <= data[5];4'd8: tx <= data[6];4'd9: tx <= data[7];4'd10: tx <= 1'b1;default: tx <= 1'b1;endcaseendend
endmodule

Testbench 代码

`timescale 1ns / 1ps
module tb_RS232_TX();reg clk;     reg rst;    reg [7:0] data;reg flag;wire tx;       // 初始化clk和rstinitial beginclk <= 1'b0;rst <= 1'b1;#60;rst <= 1'b0;end always #10 clk = ~clk;// 初始化data和flaginitial begindata <= 8'd0;flag <= 1'd0;#200;// 数据1data <= 8'b0010_1011;flag <= 1'd1;#20;flag <= 1'd0;#(5208*12*20);// 数据2data <= 8'b0111_0101;flag <= 1'd1;#20;flag <= 1'd0;#(5208*12*20);// 数据3data <= 8'b1010_0001;flag <= 1'd1;#20;flag <= 1'd0;#(5208*12*20);end// 实例化模块RS232_TX #(.UART_BPS(9600),.CLK_FREQ(50_000_000))tb_RS232_TX(.clk    (clk),       .rst    (rst),       .data   (data),.flag   (flag),      .tx     (tx)      );
endmodule

仿真

在这里插入图片描述

这篇关于野火FPGA跟练(四)——串口RS232、亚稳态的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1046160

相关文章

FPGA编译与部署方法全方位介绍

FPGA编译与部署是FPGA开发中的核心环节,涉及从代码编写、调试到将设计部署到FPGA硬件的全过程。这个流程需要经过创建项目、编写FPGA VI、模拟调试、编译生成比特流文件,最后将设计部署到硬件上运行。编译的特点在于并行执行能力、定制化硬件实现以及复杂的时钟管理。通过LabVIEW的FPGA模块和NI硬件,可以快速完成开发和部署,尤其适用于复杂控制与高性能数据处理系统。 1. FPG

野火霸天虎V2学习记录

文章目录 嵌入式开发常识汇总1、嵌入式Linux和stm32之间的区别和联系2、stm32程序下载方式3、Keil5安装芯片包4、芯片封装种类5、STM32命名6、数据手册和参考手册7、什么是寄存器、寄存器映射和内存映射8、芯片引脚顺序9、stm32芯片里有什么10、存储器空间的划分11、如何理解寄存器说明12、如何操作寄存器的某一位 STM32F407芯片学习1、stm32单片机启动流程s

FPGA开发:条件语句 × 循环语句

条件语句 if_else语句 if_else语句,用来判断是否满足所给定的条件,根据判断的结果(真或假)决定执行给出的两种操作之一。 if(表达式)语句; 例如: if(a>b) out1=int1; if(表达式)         语句1; else         语句2; 例如: if(a>b)out1=int1;elseout1=int2; if(表达式1) 语句1; els

安卓开发板_联发科MTK开发评估套件串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发,USB 串口适配器( USB 转串口 TTL 适配器的简称)对于检查系统启动日志非常有用,特别是在没有图形桌面显示的情况下。 1.选购适配器 常用的许多 USB 转串口的适配器,按芯片来分,有以下几种: CH340PL2303CP2104FT232 一般来说,采用 CH340 芯片的适配器,性能比较稳定,价

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口

FPGA开发:模块 × 实例化

模块的结构 对于C语言,其基本单元为函数。与此类似,Verilog的基本设计单元称之为"模块"(block)。对于整个项目的设计思想就是模块套模块。 一个模块由两个部分组成:一部分描述接口,一部分描述逻辑功能。 每个Verilog模块包含4个部分:端口定义、IO说明、内部信号声明、功能定义。且位于module和endmodule之间,如下: module block(a,b,c);inpu

LabVIEW环境中等待FPGA模块初始化完成

这个程序使用的是LabVIEW环境中的FPGA模块和I/O模块初始化功能,主要实现等待FAM(Field-Programmable Gate Array Module,FPGA模块)的初始化完成,并处理初始化过程中的错误。让我们逐步分析各部分的功能: 1. Wait for FAM Initialization框架 此程序框架用于等待I/O模块成功初始化。如果在5秒钟内模块没有完成配

FPGA随记——小说 可综合和不可综合

当然我在网络上找到了些可综合和不可综合的解释 感觉也很有参考价值: https://wenda.so.com/q/1378362174074040 综合就是把你写的rtl代码转换成对应的实际电路。 比如你写代码assign a=b&c; EDA综合工具就会去元件库里拿一个二输入与门出来,然后输入端分别接上b和c,输出端接上a 假如你写了很多这样的语句 assign a=b&c; assig

Xilinx系FPGA学习笔记(五)ROM的IP核学习

系列文章目录 文章目录 系列文章目录前言ROM IP分布式ROM生成ROM配置创建COE文件 块ROM生成如何快速生成Example Design 两种ROM对比 前言 最近在学习小梅哥的xilinx型FPGA开发板,一边学习一边记录,简化整理一下笔记 ROM IP 在 Memories &Storage Elements 下可以看到有两个与 ROM 相关的

基于FPGA的开源项目:FOC/SHA/USB/JPEG等

文章目录 [1. USB 1.1控制器](https://github.com/WangXuan95/FPGA-USB-Device)[2. FOC控制算法](https://github.com/WangXuan95/FPGA-FOC)[3. BSV高级硬件描述语言入门指南](https://github.com/WangXuan95/BSV_Tutorial_cn)[4. 基于XDMA的