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

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

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

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

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

模块构成:

在这里插入图片描述

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

接收状态机模块rx_state_machine——进行解帧处理,接收有效数据

假设:帧头为AA,帧尾为55,有效数据为32bit

思路:使用三段式状态机

接收状态机标志位是什么?

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);

在uart_recv中,有一个uart_done信号,是接收一帧数据完成标志,当uart_done拉高,表明接收了一帧数据(8bit),并存到了 uart_data 中。

我们可以用uart_done的上升沿作为状态机跳转的使能信号。

边沿检测如下:

//边沿检测 uart_done 信号
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)beginuart_done_prev1 <= 0;uart_done_prev2 <= 0;endelse beginuart_done_prev1 <= uart_done;	uart_done_prev2 <= uart_done_prev1;  //延迟两拍end
end
//上升沿检测
assign uart_done_rise = uart_done_prev1 & ~uart_done_prev2;

检测到uart_done的上升沿时,uart_done_rise拉高一个时钟周期。当uart_done_rise拉高时(表明接收了一帧数据,并存到了 uart_data 中),状态机进入跳转判断。

状态机设计

在上面的假设中,帧头为AA,帧尾为55,有效数据为4帧(32bit)。

所以状态机的状态有:

  • 起始状态IDLE

  • 如果接收到AA,则进入DATA_RX状态

  • 如果接收的数据>=4帧 ,进入下一个状态,即帧尾检测状态

  • 如果检测到了帧尾,则进入DONE状态,表明解帧完成

在接收帧头或帧尾的状态中,只要不是AA或55,则重新进入起始状态。

localparam 	SOF		= 8'haa;	//帧头
localparam 	EOF		= 8'h55;	//帧尾localparam	IDLE		= 4'd0;	
localparam	DATA_SOF	= 4'd1;
localparam	DATA_RX		= 4'd2;
localparam 	DATA_EOF	= 4'd3;
localparam 	DONE		= 4'd4;//时序逻辑状态切换
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) c_status <= 4'd0;elsec_status <= n_status;
end//组合逻辑状态切换
always @(*) begincase(c_status)IDLE : begin 					//起始状态if(uart_done_rise) beginif(uart_data == SOF)n_status = DATA_RX;	//如果接收到AA,则进入DATA_RX状态elsen_status = IDLE;endelse n_status = IDLE;endDATA_RX: begin					//接收有效数据状态if(uart_done_rise) beginif(r_bytecnt >= 3'd3)n_status = DATA_EOF;elsen_status = DATA_RX;endelse n_status = DATA_RX;endDATA_EOF: begin					//接收帧尾状态if(uart_done_rise) beginif(uart_data == EOF)n_status = DONE;elsen_status = IDLE;endelse n_status = DATA_EOF;endDONE: begin 					//解帧完成n_status = IDLE;enddefault: beginn_status = IDLE;endendcase
end
//接收4个有效数据的计数器
always @(posedge sys_clk) beginif(c_status == DATA_RX) beginif(uart_done_rise) r_bytecnt <= r_bytecnt+1;else ;endelse r_bytecnt <= 3'b0;
end

代码解释:首先定义帧头SOF= 8’haa;(start of flag),帧尾EOF= 8’h55;(end of flag),以及5个状态(在后面设计状态机时发现DATA_SOF状态用不到)

  • 第一个always语句块,时序逻辑状态切换,保证了当前状态c_status和下个状态n_status的切换。

  • 第二个always语句块,4个状态所执行的操作。

    • c_status为IDLE,uart_done_rise拉高时,状态机进入跳转判断。

      如果此时uart_data中的数据是帧头SOF,则准备进入下一个状态,进行有效数据的接收DATA_RX状态;

      否则继续停留在IDLE状态。

    • c_status为DATA_RX,uart_done_rise拉高时,状态机进入跳转判断。

      在DATA_RX状态中,如果接收的字节数r_bytecnt>= 3'd3,说明已经完整地接收了4帧的有效数据,准备进入下个状态,进行帧尾检测DATA_EOF;

      否则,说明数据还没有接收完,继续停留在DATA_RX状态接收数据。

    • c_status为DATA_EOF,uart_done_rise拉高时,状态机进入跳转判断。

      如果此时uart_data中的数据是帧尾EOF,则进入解帧完成状态DONE;

      否则,说明帧尾不正确,该组数据无效,状态进入IDLE,重新解帧新的数据

  • 第三个always语句中,是接收4个有效数据的计数器。c_status处于有效数据的接收状态时,每次uart_done_rise拉高(表明接收了一帧数据),计数器r_bytecnt加1;


上面的三个always语句块,实现了状态的跳转和判断。接下来需要计算数据,并判断什么情况下进行输出。

//将所有接收到的值赋给寄存器 reg_rx_data
always @(posedge sys_clk) beginif((c_status == DATA_RX) && uart_done_rise) begincase(r_bytecnt)3'd0: reg_rx_data[31:24] <= uart_data;3'd1: reg_rx_data[23:16] <= uart_data;3'd2: reg_rx_data[15:8] <= uart_data;3'd3: reg_rx_data[7:0] <= uart_data;		default: ;endcaseend
end

首先,将有效数据存入寄存器reg_rx_data中。

c_status = DATA_RX状态下,一共接收了4帧的数据,现在把这4帧数据组合成一个32bit的数据。

当r_bytecnt为0时,这时候接收的是最高位,存入reg_rx_data[31:24],

当r_bytecnt为1时,将此时刻接收的数据存入reg_rx_data[23:16],

依此类推。

//判断数据是否有效
always @(posedge sys_clk) beginif((c_status == DONE)) beginrx_data <= reg_rx_data;rx_vaild <= 1'b1;rx_error <= 1'b0;endelse if((c_status == DATA_EOF) && (uart_data != EOF)) beginrx_data <= rx_data;rx_error <= 1'b1;rx_vaild <= 1'b0;endelse beginrx_data <= rx_data;rx_vaild <= 1'b0;rx_error <= 1'b1;end	
end

接下来,判断什么情况下进行输出 / 判断该组数据是否有效

  • c_status == DONE时,表明完整地解帧完了一组数据,rx_data <= reg_rx_data;将寄存器reg_rx_data的值赋给输出rx_data;同时给两个标志位rx_vaild、rx_error赋值。
  • 当解帧的帧尾不等于EOF时,接收数据无效,rx_data保持不变。

注释:至于为什么这里只判断了帧尾,没有判断帧头,是因为帧头的判断在状态机里就实现了:如果帧头不正确,则直接回到起始状态,进行不到接收有效数据的状态。但是帧尾还需要再判断,是因为进行到帧尾的状态时,就已经接收完有效数据了,并已经存入了reg_rx_data中。所以,还要再对帧尾进行判断,如果不符合,则rx_data不更新,认为这个数据无效。

接收解帧结果

ila_0 ila_rx (.clk(sys_clk), // input wire clk.probe0(uart_done), // input wire [0:0]  probe0  .probe1(uart_data), // input wire [7:0]  probe1 .probe2(rx_vaild), // input wire [0:0]  probe2 .probe3(rx_data), // input wire [31:0]  probe3 .probe4(uart_done_rise), // input wire [0:0]  probe4 .probe5(c_status), // input wire [2:0]  probe5 .probe6(r_bytecnt) // input wire [2:0]  probe6
);

在这里插入图片描述

在串口调试助手中,我发送的是aa1234567855,结果如上图,有效数据12345678存入了rx_data。


在下一节继续写逐帧发送部分

相关文章:

  • 字符串和内存函数(1)
  • 基于SSM框架的《超市订单管理系统》Web项目开发(第五天)供应商管理,增删改查
  • 【漏洞复现】速达软件存在任意文件上传
  • 《内蒙古自治区“十四五”能源发展规划》明确提出,重点打造()、 阿拉善盟和内蒙古东部的通辽市等地区千万千瓦级风电基地。
  • 虹科案例 | OPC UA SDK快速扩展VIMANA智能制造软件连接性
  • 代码随想录刷题题Day5
  • ultralytics yolo图像分类训练案例;pytorch自有数据集图像分类案例
  • SpringBoot整合MongoDB
  • 使用com组件编辑word
  • 【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现
  • Android wifi 框架以及Enable流程
  • 第八节HarmonyOS @Component自定义组件的生命周期
  • ubuntu虚拟机设置跳不出来
  • 【Rust】所有权的认识
  • 华为 ArkTS 边框怎么设置,当边边框怎么设置(鸿蒙开发)
  • Google 是如何开发 Web 框架的
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • HTTP那些事
  • Java多态
  • Laravel Telescope:优雅的应用调试工具
  • Meteor的表单提交:Form
  • ng6--错误信息小结(持续更新)
  • Python学习之路16-使用API
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • Spring Boot MyBatis配置多种数据库
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 飞驰在Mesos的涡轮引擎上
  • 聊聊flink的BlobWriter
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 一个完整Java Web项目背后的密码
  • 因为阿里,他们成了“杭漂”
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 正则表达式-基础知识Review
  • #laravel 通过手动安装依赖PHPExcel#
  • #pragma once
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (42)STM32——LCD显示屏实验笔记
  • (Ruby)Ubuntu12.04安装Rails环境
  • (备忘)Java Map 遍历
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (四)库存超卖案例实战——优化redis分布式锁
  • (未解决)macOS matplotlib 中文是方框
  • (转)setTimeout 和 setInterval 的区别
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net 4.0发布后不能正常显示图片问题
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .Net 6.0 处理跨域的方式
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .net core 控制台应用程序读取配置文件app.config
  • .net FrameWork简介,数组,枚举
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .NET 跨平台图形库 SkiaSharp 基础应用