FPGA串口接收解帧、并逐帧发送有效数据-2

FPGA串口接收解帧、并逐帧发送有效数据

工程实现的功能:FPGA串口接收到串口调试助手发来的数据,将其数据解帧。判断到正确的帧头和帧尾之后,将有效数据存入rx_data中;另一方面发送端将有效数据逐帧发送出去。

参考:正点原子官方FPGA串口通信实验

模块构成:

在这里插入图片描述

在原子哥的基础上改的代码。添加了接收状态机模块:rx_state_machine修改了串口发送模块:uart_send。其余部分代码基本不变(只加了例化,修改数据位宽)


本节接着上一章的内容来写,来写发送端的部分。实现功能:将数据打包发送,发送帧头+接收端解帧后的有效数据+帧尾。

假设发送端打包时,帧头是FF,帧尾是EE.

代码思路分析

首先,给出正点原子的代码

module uart_send(input	      sys_clk,                  //系统时钟input         sys_rst_n,                //系统复位,低电平有效input         uart_en,                  //发送使能信号input  [7:0]  uart_din,                 //待发送数据output        uart_tx_busy,             //发送忙状态标志 output        en_flag     ,output  reg   tx_flag,                  //发送过程标志信号output  reg [ 7:0] tx_data,             //寄存发送数据output  reg [ 3:0] tx_cnt,              //发送数据计数器output  reg   uart_txd                  //UART发送端口);//parameter define
parameter  CLK_FREQ = 50000000;            //系统时钟频率
parameter  UART_BPS = 9600;                //串口波特率
localparam  BPS_CNT  = CLK_FREQ/UART_BPS;   //为得到指定波特率,对系统时钟计数BPS_CNT次//reg define
reg        uart_en_d0; 
reg        uart_en_d1;  
reg [15:0] clk_cnt;                           //系统时钟计数器//wire define
wire       en_flag;//*****************************************************
//**                    main code
//*****************************************************
//在串口发送过程中给出忙状态标志
assign uart_tx_busy = tx_flag;//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n) beginuart_en_d0 <= 1'b0;                                  uart_en_d1 <= 1'b0;end                                                      else begin                                               uart_en_d0 <= uart_en;                               uart_en_d1 <= uart_en_d0;                            end
end//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程          
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n) begin                                  tx_flag <= 1'b0;tx_data <= 8'd0;end else if (en_flag) begin                 //检测到发送使能上升沿                      tx_flag <= 1'b1;                //进入发送过程,标志位tx_flag拉高tx_data <= uart_din;            //寄存待发送的数据end//计数到停止位结束时,停止发送过程else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT - (BPS_CNT/16))) begin                                       tx_flag <= 1'b0;                //发送过程结束,标志位tx_flag拉低tx_data <= 8'd0;endelse begintx_flag <= tx_flag;tx_data <= tx_data;end 
end//进入发送过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                             clk_cnt <= 16'd0;                                  else if (tx_flag) begin                 //处于发送过程if (clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零endelse                             clk_cnt <= 16'd0; 				    //发送过程结束
end//进入发送过程后,启动发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                             tx_cnt <= 4'd0;else if (tx_flag) begin                 //处于发送过程if (clk_cnt == BPS_CNT - 1)			//对系统时钟计数达一个波特率周期tx_cnt <= tx_cnt + 1'b1;		//此时发送数据计数器加1elsetx_cnt <= tx_cnt;       endelse                              tx_cnt  <= 4'd0;				    //发送过程结束
end//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin        if (!sys_rst_n)  uart_txd <= 1'b1;        else if (tx_flag)case(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= tx_data[0];   //数据位最低位4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7];   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: ;endcaseelse uart_txd <= 1'b1;                   //空闲时发送端口为高电平
endendmodule

代码解释:以 uart_en 为发送使能信号。uart_en 的上升沿将启动一次串口发送过程,将 uart_din 接口上的数据通过串口发送端口 uart_txd 发送出去。

tx_cnt是发送数据计数器,从0-9分别发送起始位、数据位、停止位。

我们要实现的功能是发送帧头+接收端解帧后的有效数据+帧尾。

所以只需要对这个代码进行简单的修改,即可满足功能。

首先,我加了一个计数器

always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)        sent_cnt <= 4'd0;else if(tx_flag) begin		//处于发送过程时,开始计数。每个数都对应需要发送的数据if(sent_cnt < 4'd6)sent_cnt <= sent_cnt + 1'b1;else sent_cnt <= 4'd0;end
end

reg [3:0] sent_cnt; //发送计数器,用来计数发送的数据是FF、data、EE

在原子哥的代码中,一个case语句,发送的是完整的一个字节(8bit)数据。但是现在,我想要一次发送帧头(1个字节)+数据(4个字节)+帧尾(1个字节)。所以,我用了两个case语句嵌套使用。

我外层的case语句,case (sent_cnt),判断发送的是第几个字节(如:第一个字节,发送帧头;第二个字节,发送数据高8位)

内层的case语句,则与原子哥相同,发送完整的一个字节(8bit)数据。

//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin        if (!sys_rst_n)  uart_txd <= 1'b1;        else if (tx_flag)case (sent_cnt)4'd0: uart_txd <= uart_txd;4'd1: begincase(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= 1'b1;   //数据位最低位4'd2: uart_txd <= 1'b1;4'd3: uart_txd <= 1'b1;4'd4: uart_txd <= 1'b1;		//发送 帧头 FF4'd5: uart_txd <= 1'b1;4'd6: uart_txd <= 1'b1;4'd7: uart_txd <= 1'b1;4'd8: uart_txd <= 1'b1;   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: uart_txd <= uart_txd;endcaseend4'd2: begincase(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= tx_data[0];   //数据位最低位4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7];   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: ;endcaseend4'd3: begincase(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= tx_data[8];   //数据位最低位4'd2: uart_txd <= tx_data[9];4'd3: uart_txd <= tx_data[10];4'd4: uart_txd <= tx_data[11];4'd5: uart_txd <= tx_data[12];4'd6: uart_txd <= tx_data[13];4'd7: uart_txd <= tx_data[14];4'd8: uart_txd <= tx_data[15];   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: ;endcaseend			4'd4: begincase(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= tx_data[16];   //数据位最低位4'd2: uart_txd <= tx_data[17];4'd3: uart_txd <= tx_data[18];4'd4: uart_txd <= tx_data[19];4'd5: uart_txd <= tx_data[20];4'd6: uart_txd <= tx_data[21];4'd7: uart_txd <= tx_data[22];4'd8: uart_txd <= tx_data[23];   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: ;endcaseend		4'd5: begincase(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= tx_data[24];   //数据位最低位4'd2: uart_txd <= tx_data[25];4'd3: uart_txd <= tx_data[26];4'd4: uart_txd <= tx_data[27];4'd5: uart_txd <= tx_data[28];4'd6: uart_txd <= tx_data[29];4'd7: uart_txd <= tx_data[30];4'd8: uart_txd <= tx_data[31];   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: ;endcaseend			4'd6: begincase(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= 1'b0;   //数据位最低位4'd2: uart_txd <= 1'b1;4'd3: uart_txd <= 1'b1;4'd4: uart_txd <= 1'b1;		//发送 帧尾 EE4'd5: uart_txd <= 1'b0;4'd6: uart_txd <= 1'b1;4'd7: uart_txd <= 1'b1;4'd8: uart_txd <= 1'b1;   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: uart_txd <= uart_txd;endcaseend		endcaseelse uart_txd <= 1'b1;                   //空闲时发送端口为高电平
end

代码解释:发送过程标志信号tx_flag拉高时,进入发送状态。

首先case (sent_cnt)判断发送的是第几个字节

然后case(tx_cnt),发送完整的一个字节。

结果展示

ila_1 ila_tx (.clk(sys_clk), // input wire clk.probe0(tx_flag), // input wire [0:0]  probe0  .probe1(din), // input wire [31:0]  probe1 .probe2(uart_txd), // input wire [0:0]  probe2 .probe3(tx_data), // input wire [31:0]  probe3 .probe4(tx_data[7:0]), // input wire [7:0]  probe4 .probe5(tx_data[15:8]), // input wire [7:0]  probe5 .probe6(tx_data[23:16]), // input wire [7:0]  probe6 .probe7(tx_data[31:24]) // input wire [7:0]  probe7
);

在这里插入图片描述

如上投入所示,发送帧头+接收端解帧后的有效数据+帧尾

  • tx_data_1是发送的有效数据

  • din 是接收端的有效数据。可以看出,这两个相同

  • uart_txd是FPGA串口发送的信号,由0和1组成,发送的是整个完整的数据(帧头+有效数据+帧尾)


各个模块的输入输出

发送端相对接收端简单、易理解

接下来我将所有模块的输入输出放下面,方便大家理解整体的框架

uart_loopback_top

module uart_loopback_top(input           sys_clk,            //外部50M时钟input           sys_rst_n,          //外部复位信号,低有效input           uart_rxd,           //UART接收端口output          uart_txd            //UART发送端口);

uart_recv

module uart_recv(input			  sys_clk,                  //系统时钟input             sys_rst_n,                //系统复位,低电平有效input             uart_rxd,                 //UART接收端口output  reg       uart_done,                //接收一帧数据完成标志output  reg       rx_flag,                  //接收过程标志信号output  reg [ 3:0] rx_cnt,                  //接收数据计数器output  reg [ 7:0] rxdata,output  reg [7:0] uart_data,                 //接收的数据output  reg [31:0] rx_data);

rx_state_machine

module rx_state_machine(input			sys_clk		,input			sys_rst_n	,input			uart_done	,		//接收一帧数据完成标志input	[7:0] 	uart_data	,		//串口接收到上位机的所有数据input			rx_flag		,output	reg		rx_vaild	,		//接收解帧有效数据output	reg		rx_error	,		//接收解帧错误信号output	reg [31:0]	rx_data				//接收有效数据);

uart_send

module uart_send(input	      sys_clk,                  //系统时钟input         sys_rst_n,                //系统复位,低电平有效input         uart_en,                  //发送使能信号input  [7:0]  uart_din,                 //待发送数据input  [31:0] din,                 output        uart_tx_busy,             //发送忙状态标志 output        en_flag     ,output  reg   tx_flag,                  //发送过程标志信号output  reg [ 31:0] tx_data,             //寄存发送数据output  reg [ 3:0] tx_cnt,              //发送数据计数器output  reg   uart_txd                  //UART发送端口);

uart_loop

module uart_loop(input	         sys_clk,                   //系统时钟input            sys_rst_n,                 //系统复位,低电平有效input            recv_done,                 //接收一帧数据完成标志input      [31:0] recv_data,                 //接收的数据input            tx_busy,                   //发送忙状态标志      output reg       send_en,                   //发送使能信号output reg [31:0] send_data                  //待发送数据);

有几个可能出错的地方:我的发送端的数据高低位可能颠倒了。如果大家觉得确实发送数据高低位错了,请自行改正。

有好些信号我没有用到,但是在代码中我没有删除。

另外,数据环回的功能好像没有做好,我觉得还是数据位宽的原因。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【电路笔记】-串联和并联电阻

串联和并联电阻 文章目录 串联和并联电阻1、概述2、串联和并联电阻示例13、串联和并联电阻示例2 电阻器可以无限数量的串联和并联组合连接在一起&#xff0c;形成复杂的电阻电路。 1、概述 在之前的教程中&#xff0c;我们学习了如何将各个电阻器连接在一起以形成串联电阻器网…

linux设置权限_setfacl_getfacl

3.2 设置权限ACL&#xff08;access control list&#xff09; 假设&#xff1a;/data所有者与所属组均为root&#xff0c;在不改变所有者的前提下&#xff0c;要求用户tom对该目录有完全访问权限&#xff08;rwx&#xff09;。只能考虑&#xff1a; 方法一&#xff1a;给/dat…

超详细实现【贪吃蛇】(1)

目录 技术要点介绍 &#x1f642;Win32 API &#x1f642;控制台程序 &#x1f387;标题 &#x1f387;大小 在Windows终端上&#xff1a; 在VS上&#xff1a; &#x1f387;坐标 &#x1f642;光标 &#x1f636;‍&#x1f32b;️GetStdHandle &#x1f636;‍&am…

基于STM32 + TIM _定时器的基本机构和工作原理详解

前言 本篇博客主要学习了解定时器的基本结构和工作原理&#xff0c;掌握定时器的驱动程序和设计。本篇博客大部分是自己收集和整理&#xff0c;如有侵权请联系我删除。 本次博客板子使用的是正点原子精英版&#xff0c;芯片是STM32F103ZET6,需要资料可以我拿取。 本博客内容原…

[数据结构]红黑树的定义以及添加原则

红黑树是一种自平衡的二叉查找树&#xff0c;是一种常用的数据结构 1972年出现&#xff0c;在当时被称为平衡二叉B树。后来1978年被修改为如今的“红黑树” 它是一个特殊的二叉查找树&#xff0c;红黑树的每一个节点上都有储存位表示节点的颜色 每一个节点可以是红或者黑&#…

代码生成器——MyBatisX插件

MyBatisX插件 MyBatis-Plus为我们提供了强大的mapper和service模板&#xff0c;能够大大的提高开发效率。 但是在真正开发过程中&#xff0c;MyBatis-Plus并不能为我们解决所有问题&#xff0c;例如一些复杂的SQL&#xff0c;多表联查&#xff0c;我们就需要自己去编写代码和SQ…

密码学实验三

第一题&#xff1a; 寻找满足特定条件的 e&#xff1b; 第一步&#xff1a; 第二步&#xff1a; 由式1.7知&#xff0c;给定e,p,q&#xff0c;就可计算出相应的RSA不动点的数目。因此设计算法步骤如下&#xff1a; 枚举找出所有与φ(n)互素的e。枚举所有满足条件的e&#xff…

为XiunoBBS4.0开启redis缓存且支持密码验证

修改模块文件1 xiunoPHP/cache_redis.class.php: <?phpclass cache_redis {public $conf array();public $link NULL;public $cachepre ;public $errno 0;public $errstr ;public function __construct($conf array()) {if(!extension_loaded(Redis)) {return $thi…

HTTP 基本概念(计算机网络)

一、HTTP 是什么&#xff1f; HTTP(HyperText Transfer Protocol) &#xff1a;超文本传输协议。 HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。 「HTTP 是用于从互联网服务器传输超文本到本地浏览器的协议…

Linux部分基础指令讲解

目录 1.echo指令 2.more指令 3.less指令&#xff08;重要&#xff09; 4.head指令 5.tail指令 6.管道| 7.时间相关的指令 8.cal指令 9.find指令 10.grep指令 1.echo指令 我们先看效果 如图所示我们可以看到显示器显示出了hellow world和hellow这两句话&#xff0c;我们的echo的…

51单片机对SHT30的驱动,读取温湿度

一、SHT30简介 SHT30温湿度传感器是一种数字式温湿度传感器&#xff0c;由Sensirion公司开发和生产。它具有高精度、快速响应和稳定性强的特点&#xff0c;被广泛用于气象观测、室内环境监测、智能家居和工业自动化等领域。 以下是SHT30温湿度传感器的主要特点&#xff1a; 高精…

简单句子成分、阅读技巧

四、段落的主旨题&#xff1a;问这一段讲了什么&#xff08;一般都在段落的第一句话或最后一句话&#xff09; 词汇题的答案一般都在生词的上一句或者下一句 做题步骤&#xff1a; 1、先标段落 2、看题&#xff0c;划出关键词 3、去原文定位&#xff0c;标注中文意思 4、第一遍…

Dart编程基础 - 一种新的编程语言

Dart编程基础 – 一种新的编程语言 Dart Programming Essentials - A New Type of Programming Language By JacksonML Dart is a client-optimized language for fast apps on any platform From dart.dev 在1999年之前&#xff0c;和我一样对计算机技术感兴趣的伙伴们&…

1094. 拼车(差分堆排序)

Problem: 1094. 拼车 文章目录 题目思路Review 差分数组定义区间加法减法更新差分数组&#xff1a;为啥这样更新 思路1 Code思路2 Code 题目 车上最初有 capacity 个空座位。车 只能 向一个方向行驶&#xff08;也就是说&#xff0c;不允许掉头或改变方向&#xff09; 给定整…

高级前端面试中的三个 “送命题” !!!

原型与原型链 说到原型&#xff0c;就不得不提一下构造函数&#xff0c;首先我们看下面一个简单的例子&#xff1a; function Dog(name,age){this.name name;this.age age; }let dog1 new Dog("哈士奇",3); let dog2 new Dog("泰迪",2);首先创造空的…

机械臂运动规划、抓取末端执行器、抓取开源项目

运动规划 1.1已有抓取点 假设抓取点已检测到。这些方法设计了从机器人手到目标物体抓取点的路径。这里运动表示是关键问题。虽然存在从机器人手到目标抓握点的无限数量的轨迹&#xff0c;但是由于机器人臂的限制&#xff0c;许多区域无法到达。因此&#xff0c;需要对轨迹进行…

python etree.HTML 以及xpath 解析网页的工具

文章目录 导入模块相关语法实战 导入模块 from lxml import etree相关语法 XPath&#xff08;XML Path Language&#xff09;是一种用于在XML文档中定位和选择元素的语言。XPath的主要应用领域是在XML文档中进行导航和查询&#xff0c;通常用于在XML中选择节点或节点集合。以…

UiPath学习笔记

文章目录 前言RPA介绍UiPath下载安装组件内容 前言 最近有一个项目的采集调研涉及到了客户端的采集&#xff0c;就取了解了一下RPA和UIPATH&#xff0c;记录一下 RPA介绍 RPA&#xff08;Robotic Process Automation&#xff1a;机器人处理自动化&#xff09;&#xff0c;是…

pta模拟题(7-15 BCD解密、7-16 数列求和-加强版、7-17 出租、7-18 反向数相加、7-19 一帮一、7-20 连续因子)

7-15 BCD解密 BCD数是用一个字节来表达两位十进制的数&#xff0c;每四个比特表示一位。所以如果一个BCD数的十六进制是0x12&#xff0c;它表达的就是十进制的12。但是小明没学过BCD&#xff0c;把所有的BCD数都当作二进制数转换成十进制输出了。于是BCD的0x12被输出成了十进制…

JS 实现一键复制文本内容

1、演示&#xff1a; 2、代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>一键复制</title&g…