当前位置: 首页 > news >正文

基于FPGA的UART回环设计

基于FPGA的UART回环设计

文章目录

  • 基于FPGA的UART回环设计
    • 一. UART介绍
    • 二. 协议原理
    • 三. rx接收设计
      • 1. 计数器设计
      • 2. 数据接收
    • 四. tx发送设计
    • 五. 实现效果
    • 六. 主要代码

一. UART介绍

UART(Universal Asynchronous Receiver/Transmitter),即通用异步收发传输器,它是一种通用的串行、异步通信总线,有两条数据线,可以实现全双工的发送与接收。

UART因为是异步通信,所以不包括同步信号中的时钟线,其两条数据数据线TX和rRX分别用来发送和接收数据。UART负责处理串行端口与发送接收数据总线见的串并转换,并规定了数据包格式,TX和RX采用相同的数据包格式和波特率就可以进行数据传输。

二. 协议原理

发送端UART将要发送的数据组装成一个数据包通过TX端口串行将数据发送出去,接收端UART通过其RX端口串行对数据包的数据进行采集,从数据包中恢复出发送端发送的数据。
在这里插入图片描述
UART的数据包包括1个开始位、5到9位数据(取决于UART设置),1个奇偶校验位,1或者2个停止位。

开始位:UART数据传输线空闲时保持高电平,开始发送数据时,发送端UART将数据线从高拉到低一个时钟周期(其长短根据波特率决定)。当接收端UART探测到下降沿时,开始根据波特率读取数据。

数据位:数据位中为要传输的真是数据,如果使用奇偶校验位,数据帧中可包含5到8位数据,如果不适用奇偶校验位的话,数据帧中最长则可包含9位数据。

奇偶校验位:奇偶校验位用于在接收端UART来显示传输过程中是否有数据被改变。在传输过程中,位数据可能因为电磁辐射、不匹配的波特率、或者长距离传输等原因被改变。当接收UART读取完数据帧后,计算其中1的个数,并且检查1的总个数是奇数还是偶数。如果奇偶校验位是0,则数据帧中1的个数应该是偶数个,如果奇偶校验位是1,则数据帧中1的个数则应该是奇数个。当奇偶校验位的值与数据位帧相匹配时,则UART认为传输中没有错误(具有局限)。但是如果奇偶校验位是0,数据帧的1的总个数是奇数;或者奇偶校验位是1,数据帧中1的共个数是偶数,则UART则会知道帧数据已经被改变。

停止位:为了表示传输包的结束,发送端UART将数据传输线从低电平拉到高电平至少1到2个数据位宽。

前面已经对数据包/数据发送时序进行了介绍,下面在对波特率以及与其容易混淆的比特率的基础知识进行补充。

波特率定义是:每秒钟传送的符号(码元)数量,单位波特(Baud、B,即symbol/s)。

比特率定义是:每秒钟传送的位(比特)数量,基本单位是比特每秒(bps,bit per second)。.

从两者的定义可以看出要想知道波特率和比特率的关系,就首先的弄清楚码元数量和比特数量的关系。此处引用参考资料2中的解释:把通信系统中码元类比为公共交通车辆,例如公交车、地铁、的士……。通信系统所传输的比特数量类比为出行的人,则比特率为出行人口流动速度,波特率就是发车率。就像例子中提及的公交车、地铁、的士可以搭乘不同数量的出行人员一样,不同码元也可以用不同位数的比特表示。码元所需要的比特位数,由码元支持的状态数量确定。

UART中每个码元就是一位数据,存在两种状态,也即波特率单位变为每秒位数,与比特率定义相同,可以理解为此处的波特率就是比特率,本次采用的波特率为9600bps、14400bps、38400bps、115200bps。

三. rx接收设计

本次采用可调节波特率设置

always @(*) begincase(sw)2'b00:      baud = 5208;          //9600波特率2'b01:      baud = 3472;          //14400波特率2'b10:      baud = 1302;          //38400波特率2'b11:      baud = 434;           //115200波特率default:    baud = 5208;           endcase
end

当sw开关拨动到不同位置时,可设置不同的波特率

1. 计数器设计

波特率计数器设计,为简单的基础计数器,最大数为可设置的调节数

always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt <= 0;end else if(add_cnt)beginif(end_cnt)begincnt <= 0;endelse begincnt <= cnt + 1;endendelse begincnt <= cnt;end
endassign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));always @(posedge clk or negedge rst_n)beginif(!rst_n)beginadd_cnt <= 1'b0;endelse if(rx_start)beginadd_cnt <= 1'b1;endelse if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))beginadd_cnt <= 1'b0;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_r <= 2'b00;endelse beginrx_r <= {rx_r[0],rx};end 
endassign rx_start = ~rx_r [0] & rx_r [1];

其中,add_cnt为波特率计数器开始计数的标志,当输入的数据起始位到来,即检测其下降沿rx_r,波特率计数器开始计数。

end_cnt为波特率计数器结束的标志,当计到最大的波特率时停止,或者在10bit即停止位时(本设计由于硬件问题,停止位为半个波特率的周期),计数器停止计数。

比特计数器设计

always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt ;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;

开始计数标志为波特率计数器结束,当计到最大bit数,即11个bit时停止。

2. 数据接收

always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data_r <= 11'b11111111111;endelse if(cnt == (baud - 1)>>2)beginrx_data_r[cnt_bit] <= rx; end
end

由于uart收发数据为串行数据,这里要将串行数据转为并行数据,设置数据寄存器rx_data_r,按照bit计数器将数据存入寄存器

assign rx_check = ~^ rx_data_r[8 :1];

奇偶校验位处理,本设计位奇校验rx_check为第十位奇偶校验位,对前8bit数据位进行按位异或运算,所得结果为0则代表数据位有偶数个,1则代表为奇数个,但本设计为奇校验,对所得数再取反,才使得所有数据加校验位为奇数个1。

always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data <= 8'hff;endelse if((cnt == (baud - 1)>>1)&&cnt_bit == 4'd9)beginif((rx_check == rx_data_r[9]))beginrx_data <= rx_data_r[8:1];endelse beginrx_data <=8'hff;endend
end

将数据位的8bit数据存入寄存器,方便传出和后续使用。

always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_done <= 1'b0;endelse if(end_cnt_bit)beginrx_done <= 1'b1;endelse beginrx_done <= 1'b0;end
end

rx停止信号,方便tx发数据以及后续的处理。

四. tx发送设计

波特率计数器与bit计数器与rx相同,这里不再赘述。

always @(posedge clk or negedge rst_n)beginif(!rst_n)begindata<=11'b11111111111;endelse if(tx_start)begindata<={1'b1,tx_check,tx_data,1'b0};      endelse begindata<=data;end
end

发出数据的处理,uart为低位先发,先发0起始位,后接所发的数据1-8bit的tx_data为rx数据寄存器传入的数据,后接校验位tx_check(与rx中同理,对tx_data按位取异或再取反),后接停止位1.

always @(posedge clk or negedge rst_n)beginif(!rst_n)begintx<=1'b1;endelse if(end_cnt)begintx<=data[cnt_bit];endelse if(end_cnt_bit)begintx<=1'b1;endelse begintx<=tx;end
end

uart数据为串行数据,将缓存的data数据挨个发出。

五. 实现效果

在这里插入图片描述
电脑向FPGA发送hello,FPGA接收到并返回电脑hello在这里插入图片描述
sw[0]与sw[1]都为1,同时led[2]与led[3]亮起,对应波特率为115200,数据传输正确。
在这里插入图片描述
在这里插入图片描述
sw[0]为0,sw[1]为1,led[2]量led[3]灭,对应波特率为38400,
数据传输正确。
在这里插入图片描述
在这里插入图片描述
电脑向FPGA发送“开”,led[0]亮起
在这里插入图片描述
在这里插入图片描述
发送关led[0]熄灭。

六. 主要代码

uart_rx.v

module uart_rx (input               clk,input               rst_n,input               rx,input      [1:0]    sw,output reg [7:0]    rx_data,output reg          rx_done
);wire                         rx_check;//比特率计数器
reg            [12:0]       cnt;
reg                         add_cnt;
wire                        end_cnt;   //bit计数器
reg            [3:0]        cnt_bit;  
wire                        add_cnt_bit;
wire                        end_cnt_bit;//下降沿检测
reg            [1:0]        rx_r;
wire                        rx_start;reg            [10:0]       rx_data_r;reg            [12:0]       baud;always @(*) begincase(sw)2'b00:      baud = 5208;          //9600波特率2'b01:      baud = 3472;          //14400波特率2'b10:      baud = 1302;          //38400波特率2'b11:      baud = 434;           //115200波特率default:    baud = 5208;           endcase
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt <= 0;end else if(add_cnt)beginif(end_cnt)begincnt <= 0;endelse begincnt <= cnt + 1;endendelse begincnt <= cnt;end
endassign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));always @(posedge clk or negedge rst_n)beginif(!rst_n)beginadd_cnt <= 1'b0;endelse if(rx_start)beginadd_cnt <= 1'b1;endelse if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))beginadd_cnt <= 1'b0;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_r <= 2'b00;endelse beginrx_r <= {rx_r[0],rx};end 
endassign rx_start = ~rx_r [0] & rx_r [1];always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt ;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data_r <= 11'b11111111111;endelse if(cnt == (baud - 1)>>2)beginrx_data_r[cnt_bit] <= rx; end
endassign rx_check = ~^ rx_data_r[8 :1];always @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data <= 8'hff;endelse if((cnt == (baud - 1)>>1)&&cnt_bit == 4'd9)beginif((rx_check == rx_data_r[9]))beginrx_data <= rx_data_r[8:1];endelse beginrx_data <=8'hff;endend
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginrx_done <= 1'b0;endelse if(end_cnt_bit)beginrx_done <= 1'b1;endelse beginrx_done <= 1'b0;end
end
endmodule

uart_tx.v

module uart_tx (input               clk,input               rst_n,input    [7:0]      tx_data,input               tx_start,input    [1:0]      sw,output   reg        tx,output              tx_done
);
//奇偶校验位
wire                  tx_check;
//波特率计数器参数
reg     [12:0]        cnt;
reg                   add_cnt;
wire                  end_cnt;//bit计数器参数
reg     [3:0]         cnt_bit;
wire                  add_cnt_bit;
wire                  end_cnt_bit;reg     [12:0]        baud;
reg     [10:0]         data;//数据寄存器always @(*) begincase(sw[1:0])2'b00:      baud = 5208;          //9600波特率2'b01:      baud = 3472;          //14400波特率2'b10:      baud = 1302;          //38400波特率2'b11:      baud = 434;           //115200波特率default:    baud = 5208;           endcase
end//波特率计数器
always @(posedge clk or negedge rst_n) begin if(!rst_n)begincnt <= 0;end else if(add_cnt)beginif(end_cnt)begincnt <= 0;endelse begincnt <= cnt + 1;endendelse begincnt <= cnt;end
endassign end_cnt = add_cnt && (cnt == baud-1)||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1));always @(posedge clk or negedge rst_n)beginif(!rst_n)beginadd_cnt <= 1'b0;endelse if(tx_start)beginadd_cnt <= 1'b1;endelse if(end_cnt_bit||(cnt_bit == 4'd10 && cnt == ((baud-1)>>1)))beginadd_cnt <= 1'b0;end
end//bit计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = end_cnt;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd11-1;assign tx_check = ~^tx_data;always @(posedge clk or negedge rst_n)beginif(!rst_n)begindata<=11'b11111111111;endelse if(tx_start)begindata<={1'b1,tx_check,tx_data,1'b0};           //数据加启示位endelse begindata<=data;end
endalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintx<=1'b1;endelse if(end_cnt)begintx<=data[cnt_bit];endelse if(end_cnt_bit)begintx<=1'b1;endelse begintx<=tx;end
endassign tx_done = end_cnt_bit;
endmodule

led.v

module led (input clk,input rst_n,input [1:0] sw,input [7:0] data,output reg [2:0] led
);
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginled[1:0] <= 2'b00;endelse begincase(sw)2'b00:      led[1:0] <= 2'b00;2'b10:      led[1:0] <= 2'b01;2'b01:      led[1:0] <= 2'b10;2'b11:      led[1:0] <= 2'b11;default:    led[1:0] <= 2'b00;endcaseendendalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginled[2] <= 1'b0;endelse if(data == 8'h80)beginled[2] <= 1'b1;endelse if(data == 8'hb3)beginled[2] <= 1'b0;endelse beginled[2] <= led[2];endendendmodule

uart_loop.v

module uart_loop (input              clk,input              rst_n,input              rx,input    [1:0]     sw,output   [2:0]     led,output             tx
);
wire      [7:0]          data;
wire                     tx_done;uart_rx inst_uart_rx (.clk(clk),.rst_n(rst_n),.rx(rx),.sw(sw),.rx_data(data),.rx_done(rx_done)
);uart_tx inst_uart_tx (.clk(clk),.rst_n(rst_n),.tx_data(data),.tx_start(rx_done),.sw(sw),.tx(tx),.tx_done(tx_done)
);
led inst_led (.clk(clk),.rst_n(rst_n),.sw(sw),.data(data),.led(led)
);endmodule
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dtcms.com/a/259268.html

相关文章:

  • Qt开发1--Qt概述,安装,创建第一个Qt项目
  • 在windows系统上安装Comfy UI
  • 内存条与CPU三级缓存之间的区别
  • Vue SPA 路由跳转无法回到顶部问题排查与解决
  • C++设计模式(GOF-23)——04 C++装饰器模式(Decorator)(一个类同时继承和组合另一个类)解决类爆炸问题、模板装饰器
  • iPhone越狱基本流程
  • 【CMake入门学习教程】bash语句示例注解
  • 60页PPT实战方案 | 大数据决策分析平台建设全流程路径图
  • Bright Data亮数据 MCP + N8N x AI 新闻编辑:基于亮数据,数据采集到观点摘要工作流自动化实践
  • “Ubuntu 18.04.6 LTS“ 配置网卡静态IP
  • 数据赋能(324)——安全与合规——所有权
  • 实战 X-AnyLabeling:构建高效自动标注系统的工程实践
  • 2012_2NOIP 国王游戏 [贪心+排序+高精]
  • java 对接ETH(以太坊) 交易相关资料
  • 跟着AI学习C#之项目实践Day7
  • 在uni-app build的index.html 中加入 <mate,和title 等标签内容 内容
  • 《大模型 Agent 应用实战指南》第2章:商业目标与 Agent 能力边界定义
  • 【评估指标】MAP@k (目标检测)
  • 探索解析C++ STL中的 list:双向链表的高效实现与迭代器
  • Linux学习笔记:PCIe内核篇(3):DPC服务
  • 浪潮存储单卷单LUN不能超过64T、128T解决方法
  • 领域驱动设计(DDD)【13】之重构中的坏味道:深入理解依恋特性(Feature Envy)与表意接口模式
  • 深入浅出:RocketMQ与Kafka的双剑合璧,实现高可用与高吞吐
  • 计算机网络-----详解HTTP协议
  • 用Python做一个手机镜头
  • GitHub中openmmlab和Detectron2的区别
  • 打造灵活强大的PDF解析管道:从文本提取到智能分块的全流程实战
  • Java编程中的单例模式
  • 微信点餐小程序—美食物
  • python学智能算法(十六)|机器学习支持向量机简单示例