基于Robei EDA--实现串口通信

一、串口简介

串口作为常用的三大低速总线(UART、SPI、IIC)之一,在设计众多通信接口和调试时占有重要地位。但UART和SPI、IIC不同的是,它是异步通信接口,异步通信中的接收方并不知道数据什么时候会到达,所以双方收发端都要有各自的时钟,在数据传输过程中是不需要时钟的,发送方发送的时间间隔可以不均匀,接受 方是在数据的起始位和停止位的帮助下实现信息同步的。而SPI、IIC是同步通信接口(后面的章节会做详细介绍),同步通信中双方使用频率一致的时钟,在数据传输过程中时钟伴随着数据一起传输,发送方和接收方使用的时钟都是由主机提供的。

UART通信只有两根信号线,一根是发送数据端口线叫tx(Transmitter),一根是接收数据端口线叫rx(Receiver)。可以实现全双工,即可以同时进行发送数据和接收数据。

RS232通信协议简介

串口的通信协议有很多,如RS232、RS499、RS423、RS422和RS485等接口,但是这里我们就使用最常见的通信协议RS232。

下面是数据帧结构

rx和tx的位宽都是1bit。串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。

每一帧数据共10bit,起始位为0,停止位为1,中间包含8bit的数据位。在不发送或者不接收数据的情况下,rx和tx处于空闲状 态,此时rx和tx线都保持高电平,即为空闲状态。

几个重要概念

波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是1bit进行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率。串口常见的波特率有4800、9600、115200等。

由计算得串口发送或者接收1bit数据的时间为一个波特,即1/9600秒,如果用50MHz(周期为20ns)的系统时钟来计数,需要计数的个数为cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208个系统时钟周期,即每个bit数据之间的间隔要在50MHz的时钟频率下计数5208次。

二、串口接收模块

先设计串口接收模块,该模块的功能是接收通过PC机上的串口调试助手发送的固定波特率的数据,串口接收模块按照串口的协议准确接收串行数据,解析提取有用数据后需将其转化为并行数据,因为并行数据在FPGA内部传输的效率更高,转化为并行数据后同时产生一个数据有效信号标志信号伴随着并行的有效数据一同输出。

采用WPS绘制框图--串口接收模块

  • rx:1bit  通过该模块将串行数据转换成8bit并行数据
  • 8bit并行数据有效的标志信号 po_flag
  • po_data 8bit数据并行输出
波形设计
思路1:减少亚稳态的影响对rx打两拍,两级寄存器
{signal: [{name: 'clk', wave: 'p.........|............'},{name: 'rerset', wave: 'lh......|..............'},{name: 'rx', wave: 'h......lhlhlhlhlh..'},{name: 'rx_reg1', wave: 'h.......lhlhlhlhlh..'},{name: 'rx_reg2', wave: 'h........lhlhlhlhlh..'},
],config: { hscale: 2,skin: "narrow"}}


思路二:采集下降沿

经过两级寄存器后rx_reg2相对稳定,rx_reg1是不稳定的,所以打算再打一拍,rx_reg3和rx_reg2用于采集下降沿

采集好下降沿后让start_negedge维持一个时钟周期

思路三:工作使能信号

为了使在过程中的下降沿不会影响串口的接收,我们设置一个工作使能信号,在work_en信号为高的时候,可以判断出现的start_negedge是不是我们想要的串口一帧的起始位下降沿,从而不影响后续操作

思路四:正式接收数据

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

思路五:读取数据

我们选择当baud_cnt计数器计数到2603,即中间位置时取数最稳定(其实只要b aud_cnt计数器在计数值不是在0和5207这两个最不稳定的时刻取数都可以,更为准确的是多次取值取概率最大的情况)。

思路六:rx何时拉高

串口的数据是基于帧的,所以每接收完一帧数据rx信号都要被拉高,即恢复到空闲状态重新判断串口帧起始下降沿,以等待下一帧数据的接收,且一帧数据中还包括了起始位和停止位这种无用的数据,而对我们有价值的数据只是中间的8bit数据,也就是说我们需要准确的知道我们此时此刻接收的是第几比特,当接收够10bit数据 后,我们就停止继续接收数据,等rx信号被拉高待恢复到空闲状态后再等待接收下一帧的数据。

bit_cnt用于记录目前接收到第几bit数据

可以利用我们已经产生的bit_flag取数标志信号,对该信号进行计数既可以知道此时我们接收的数据是第几个比 特了。这里我们只让bit_cnt计数器的计数值为8时再清零,虽然bit_cnt计数器的计数值从0计数到8只有9个bit,但这9个bit中已经包含的我们所需要的8bit有用的数据,最后的1bit停止位没有用,可以不用再进行计数了。

思路七:work_en何时拉低

前面讲了work_en什么时候拉高,下面思考什么时候拉低,当接受完第八位数据时:①bit_flag = 1

②bit_cnt = 8;如果只计数到8没有bit_flag = 1的条件的话会错失第8bit有小数据,读取位置偏前

思考八:串转并

串并转换就需要做移位,我们要考虑清楚什么时候开始移位,不能提前也不能推后,否则会将无用的 数据也移位进来,所以我们需要卡准时间。注意移位的条件,要在bit_cnt计数器的计数值为1到8区间内且bit_flag取数标志信号同时为高时才能移位,也就是移动7次即可,接收最后1bit有用数据时就不需要再进行移位了。当移位7次后1bit的串行数据已经变为8 bit的并行数据了,此时产生一个移位完成标志信号rx_flag。

思考九:输出po_data

程序编写

Robei EDA框图

//reg define
reg rx_reg1;
reg rx_reg2;
reg rx_reg3;
reg work_en;	//工作使能信号
reg start_negedge;//下降沿检测
reg [12:0]baud_cnt;	//比特计数器
reg bit_flag;
reg [3:0]bit_cnt;
reg [7:0]rx_data;
reg rx_flag;
parameter baud_cnt_MAX = 13'd5208;//三拍寄存器
always@(posedge clk or negedge reset)if(!reset)beginrx_reg1 <= 1'b0;
endelse beginrx_reg1 <= rx;
endalways@(posedge clk or negedge reset)if(!reset)beginrx_reg2 <= 1'b0;
endelse beginrx_reg2 <= rx_reg1;
endalways@(posedge clk or negedge reset)if(!reset)beginrx_reg3 <= 1'b0;
endelse beginrx_reg3 <= rx_reg2;
end//start_negedge
always@(posedge clk or negedge reset)
if(!reset)beginstart_negedge <= 1'b0;
end
else if((~rx_reg2) && (rx_reg3))
start_negedge <= 1'b1;
else
start_negedge <= 1'b0;//work_en
always@(posedge clk or negedge reset)
if(!reset)work_en <= 1'b0;
else if(start_negedge == 1'b1)
work_en <= 1'b1;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
work_en <= 1'b0;
else work_en <= work_en;//baud_cnt
always@(posedge clk or negedge reset)
if(!reset)baud_cnt<= 0;
else if(work_en == 1'b1)beginif(baud_cnt >= baud_cnt_MAX - 1'b1)baud_cnt <= 0;else baud_cnt <= baud_cnt + 1'b1;
end
elsebaud_cnt <= 0;//bit_flag
always@(posedge clk or negedge reset)
if(!reset)bit_flag <= 0;
else if(work_en == 1'b1)beginif(baud_cnt == baud_cnt_MAX/2)bit_flag <= 1;else bit_flag <= 0;
end
else bit_flag <= 0;//bit_cnt;
always@(posedge clk or negedge reset)
if(!reset)bit_cnt <= 4'd0;else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))bit_cnt <= 4'b0;else if(bit_flag ==1'b1)bit_cnt <= bit_cnt + 1'b1;//rx_data:输入数据进行移位always@(posedge clk or negedge reset)if(reset == 1'b0)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]};//rx_flag
always@(posedge clk or negedge reset)
if(!reset)rx_flag <= 1'b0;
else if(bit_cnt == 4'd8 &&(bit_flag == 1'b1))rx_flag <= 1'b1;
elserx_flag <= 1'b0;//po_data
always@(posedge clk or negedge reset)
if(!reset)po_data <= 8'b0;
else if(rx_flag)po_data <= rx_data;//po_flag
always@(posedge clk or negedge reset)
if(!reset)po_flag <= 1'b0;
else if(rx_flag)po_flag <= 1'b1;
elsepo_flag <= 1'b0;

仿真文件

 //初始化系统时钟、全局复位和输入信号initial beginclk = 1'b1;reset <= 1'b0;rx <= 1'b1;#20;reset <= 1'b1;
#1000_000_00;
$finish;end//模拟发送8次数据,分别为0~7initial begin#200rx_bit(8'd0); //任务的调用,任务名+括号中要传递进任务的参数rx_bit(8'd1);rx_bit(8'd2);rx_bit(8'd3);rx_bit(8'd4);rx_bit(8'd5);rx_bit(8'd6);rx_bit(8'd7);end//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号always #10 clk = ~clk;//定义一个名为rx_bit的任务,每次发送的数据有10位//data的值分别为0~7由j的值传递进来//任务以task开头,后面紧跟着的是任务名,调用时使用task rx_bit(//传递到任务中的参数,调用任务的时候从外部传进来一个8位的值input [7:0] data);integer i; //定义一个常量//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1//不可以写成C语言i=i++的形式for(i=0; i<10; i=i+1) begincase(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); //每发送1位数据延时5208个时钟周期endendtask //任务以endtask结束

仿真文件里Verilog语法学习

task 任务名(
//此处写传递到任务里的参数
input xxx,
output xxx,
inout xxx
);endtask //任务以endtask结束

仿真波形

经过仿真,慢慢改正,成功完成。

三、串口发送模块

  • clk:时钟
  • reset:复位信号,低电平有效
  • pi_data:并行数据输入
  • pi_flag:并行数据输入有效信号
  • tx:发送端


波形设计

波形分析一:由于pi_flag的高电平只维持1个时钟周期,所以我们产生一个work_en工作使能信号

work_en=1 的条件下baud_cnt开始计数

波形分析二:选择发送数据的位置点

baund_cnt = 1

波形分析三:bit_cnt

一帧数据包括起始位、8位有效数据、停止位,需要一个4位的bit_cnt用于记录发了多少

波形分析四:

当bit_cnt计数到9就可以不再计数,因为停止位和空闲状态均为高电平


程序编写

//reg define
reg [12:0] baud_cnt;
reg [3:0] bit_cnt;
reg bit_flag;
reg work_en;parameter Baud_cnt_Max = 13'd5208;
//工作使能信号
always@(posedge clk or negedge reset)
if(!reset)work_en <= 1'b0;
else if(pi_flag == 1'b1)work_en <= 1'b1;
else if(bit_cnt == 4'd9 && bit_flag == 1)work_en <= 1'b0;
else work_en <= work_en;always@(posedge clk or negedge reset)
if(!reset)baud_cnt <= 13'b0;
else if(baud_cnt >= Baud_cnt_Max)baud_cnt <= 13'b0;
else if(work_en)baud_cnt <= baud_cnt + 1'b1;
else baud_cnt <= 13'b0;always@(posedge clk or negedge reset)
if(!reset)bit_flag <= 0;
else if(baud_cnt == 13'd1)bit_flag <= 1'b1;
else bit_flag <= 1'b0;always@(posedge clk or negedge reset)
if(!reset)bit_cnt <= 4'd0;
else if(bit_flag&&work_en)bit_cnt <= bit_cnt + 1'b1;
else if((work_en == 1'b0 )||(bit_cnt == 4'd9 && bit_flag ))bit_cnt <= 4'd0;
else bit_cnt <= bit_cnt;always@(posedge clk or negedge reset)
if(!reset)							tx <= 1'b1;//空闲状态为高电平
else if(bit_flag)
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
else tx <= tx;

四、数据回环

写在最后--串口学习(参考)可以应用在蓝牙模块以及一系列串口通信的外设,继续努力学习。

UART那么好用,为什么单片机还需要I2C和SPI?​​​​​​​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2775675.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

深度优先搜索(DFS)与广度优先搜索(BFS):探索图与树的算法

一、引言 在图论和树形结构中&#xff0c;搜索算法是寻找从起点到终点的路径的关键。其中&#xff0c;深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09;是最常用且最基础的两种搜索算法。本文将详细介绍广度优先搜索&#xff08;BFS&#xf…

C#上位机与三菱PLC的通信03--MC协议之A-1E报文解析

1、MC协议帧 MC协议可以在串口通信&#xff0c;也可以在以太网通信&#xff0c;有A-1E和Qna-3E两种模式&#xff0c;这两种都是三菱PLC通信协议中比较常用的两种&#xff0c;一般我们使用比较多的是以太网通信&#xff0c;对于FX5U系列/Q系列/Qna系列/L系列的PLC&#xff0c;…

目标检测 | 卷积神经网络(CNN)详细介绍及其原理详解

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种深度学习模型&#xff0c;主要用于图像识别和计算机视觉任务。它的设计灵感来自于生物学中视觉皮层的工作原理。CNN的核心思想是通…

Stable Diffusion教程——使用TensorRT GPU加速提升Stable Diffusion出图速度

概述 Diffusion 模型在生成图像时最大的瓶颈是速度过慢的问题。为了解决这个问题&#xff0c;Stable Diffusion 采用了多种方式来加速图像生成&#xff0c;使得实时图像生成成为可能。最核心的加速是Stable Diffusion 使用了编码器将图像从原始的 3512512 大小转换为更小的 46…

Leetcode刷题笔记题解(C++):面试题 08.07. 无重复字符串的排列组合

思路&#xff1a;因为字符之间互不相同&#xff0c;故使用全排列的方式去解题&#xff1b; 字符串长度为n&#xff0c;将第一个字母分别与后面每一个字母进行交换&#xff0c;生成n种不同的全排列&#xff1b;再用第二个元素与后面每一个元素进行交换&#xff0c;生成n - 1种不…

Transformer的PyTorch实现之若干问题探讨(一)

《Transformer的PyTorch实现》这篇博文以一个机器翻译任务非常优雅简介的阐述了Transformer结构。在阅读时存在一些小困惑&#xff0c;此处权当一个记录。 1.自定义数据中enc_input、dec_input及dec_output的区别 博文中给出了两对德语翻译成英语的例子&#xff1a; # S: de…

《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(10)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述&#xff08;9&#xff09; 4.2 PCIe体系结构的组成部件 PCIe总线作为处理器系统的局部总线&#xff0c;其作用与PCI总线类似&#xff0c;主要目的是为了连接处理器系统中的外部设备&…

Vue源码系列讲解——虚拟DOM篇【二】(Vue中的DOM-Diff)

目录 1. 前言 2. patch 3. 创建节点 4. 删除节点 5. 更新节点 6. 总结 1. 前言 在上一篇文章介绍VNode的时候我们说了&#xff0c;VNode最大的用途就是在数据变化前后生成真实DOM对应的虚拟DOM节点&#xff0c;然后就可以对比新旧两份VNode&#xff0c;找出差异所在&…

MATLAB知识点:使用逻辑值修改或删除矩阵元素

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章 3.4.4 逻辑运算 3.4.4.3 使用逻辑值修改或删…

Elasticsearch(四)

是这样的前面的几篇笔记&#xff0c;感觉对我没有形成知识体系&#xff0c;感觉乱糟糟的&#xff0c;只是大概的了解了一些基础知识&#xff0c;仅此而已&#xff0c;而且对于这技术栈的学习也是为了在后面的java开发使用&#xff0c;但是这里的API学的感觉有点乱&#xff01;然…

熔断机制解析:如何用Hystrix保障微服务的稳定性

微服务与系统的弹性设计 大家好,我是小黑,在讲Hystrix之前,咱们得先聊聊微服务架构。想象一下,你把一个大型应用拆成一堆小应用,每个都负责一部分功能,这就是微服务。这样做的好处是显而易见的,更新快,容错性强,每个服务可以独立部署,挺美的对吧?但是,问题也随之而…

PKI - 借助Nginx 实现Https 服务端单向认证、服务端客户端双向认证

文章目录 Openssl操系统默认的CA证书的公钥位置Nginx Https 自签证书Nginx Https 使用CA签发证书客户端使用自签证书供服务端验证客户端使用 根证书 签发客户端证书 供服务端验证 Openssl https://www.openssl.net.cn/ openssl是一个功能丰富且自包含的开源安全工具箱。 它提…

放假--寒假自学版 day1(补2.5)

fread 函数&#xff1a; 今日练习 C语言面试题5道~ 1. static 有什么用途&#xff1f;&#xff08;请至少说明两种&#xff09; 1) 限制变量的作用域 2) 设置变量的存储域 2. 引用与指针有什么区别&#xff1f; 1) 引用必须被初始化&#xff0c;指针不必。 2) 引用初始…

无心剑七绝《龙年大吉》

七绝龙年大吉 龙腾五岳九州圆 年吼佳音万里传 大漠苍鹰华夏梦 吉人天相铸奇缘 2024年2月8日 平水韵一先平韵 这首藏头七绝《龙年大吉》是无心剑为2024年春节所创作的诗作。2024年是农历的甲辰年&#xff0c;即龙年。在中国传统文化中&#xff0c;龙是吉祥的象征&#xff0c;代表…

PSM-Net根据Stereo图像生成depth图像

一、新建文件夹 在KITTI数据集下新建depth_0目录 二、激活anaconda环境 conda activate pt14py37三、修改submission.py文件 3.1 KITTI数据集路径 parser.add_argument(--datapath, default/home/njust/KITTI_DataSet/00/, helpselect model)3.2 深度图像输出路径 save…

【复现】九思OA系统 SQL注入漏洞_43

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 九思软件自主研发的iThink协同OA办公自动化系统是面向中高端企业、政府机关和事业单位、等大型企业的协同办公软件&#xff0c;面…

pythn-scipy 查漏补缺

1. 2. 3. 4. 5. 6. 7. 8. 9. 偏度 skewness&#xff0c;峰度 kurtosis

TS学习与实践

文章目录 学习资料TypeScript 介绍TypeScript 是什么&#xff1f;TypeScript 增加了什么&#xff1f;TypeScript 开发环境搭建 基本类型编译选项类声明属性属性修饰符getter 与 setter方法static 静态方法实例方法 构造函数继承 与 super抽象类接口interface 定义接口implement…

Linux操作系统基础(二):Linux操作系统概述

文章目录 Linux操作系统概述 一、Linux起源 二、Linux 的含义 三、Linux发行版 Linux操作系统概述 一、Linux起源 Linux创始人——林纳斯 托瓦兹 Linux 诞生于1991年&#xff0c;作者上大学期间实现的 Linux的特点&#xff1a;开源、免费、拥有最为庞大的源码贡献者 …

nginx+flask+Gunicorn反代理服务拿不到真实IP的解决

背景 本人在宝塔linux环境&#xff0c;要部署flask的简单后端并且用Ngnix反代理&#xff0c;用Gunicorn框架部署。&#xff08;o(╥﹏╥)o中间磕磕绊绊总算部署上去了&#xff0c;需要了解Gunicorn怎么部署的朋友&#xff0c;评论区留言&#xff0c;我加补一篇介绍&#xff09;…