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

这篇具有很好参考价值的文章主要介绍了FPGA串口接收解帧、并逐帧发送有效数据-2。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

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

模块构成:

FPGA串口接收解帧、并逐帧发送有效数据-2,ZYNQ&FPGA实例,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) begin
        uart_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;
        end
        else begin
            tx_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;
        else
            clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
    end
    else                             
        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;		//此时发送数据计数器加1
        else
            tx_cnt <= tx_cnt;       
    end
    else                              
        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: ;
        endcase
    else 
        uart_txd <= 1'b1;                   //空闲时发送端口为高电平
end

endmodule

代码解释:以 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: begin
				case(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;		//发送 帧头 FF
					4'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;
					endcase
			end
			4'd2: begin
				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: ;
				endcase
			end
			4'd3: begin
				case(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: ;
				endcase
			end			
			4'd4: begin
				case(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: ;
				endcase
			end		
			4'd5: begin
				case(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: ;
				endcase
			end			
			4'd6: begin
				case(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;		//发送 帧尾 EE
					4'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;
					endcase
			end		
		endcase
    else 
        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
);

FPGA串口接收解帧、并逐帧发送有效数据-2,ZYNQ&amp;FPGA实例,fpga开发,信息与通信

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

  • 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                  //待发送数据
    );

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

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

另外,数据环回的功能好像没有做好,我觉得还是数据位宽的原因。文章来源地址https://www.toymoban.com/news/detail-755790.html

到了这里,关于FPGA串口接收解帧、并逐帧发送有效数据-2的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 串口发送数据,只接收到00的原因之一

    1、环境: STM32F103RCT6 KEIL 5 2、现象: 不管我发送任何类型的数据,在串口助手上显示接收到的都是00, 我头很大,去网上找了别人的代码对比检查。 终于,细心的我发现,原来粗心的我少了一行代码。 3、解决方案1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); 好了,加上这一行

    2024年02月12日
    浏览(64)
  • STM32实现三个串口同时开启发送接收数据

            实现STM32开通三个串口,每个串口都可以实现接收和发送数据。          编程时,严禁在中断函数中写入发送串口数据代码,否则会出错,具体原因不清楚(有大佬知道的话帮我指出),可能原因是DR寄存器冲突导致。         RX,TX连接到A9,A10使用串口1,使

    2024年04月13日
    浏览(52)
  • 最详细STM32,cubeMX串口发送,接收数据

    这篇文章将详细介绍 串口 发送数据,接受数据。 实验开发板:STM32F103C8T6。 所需软件:keil5 , cubeMX 。 实验目的:了解 串口的基础知识,掌握串口如何发送,接收数据 。 实验:串口发送数据点亮 led。 如果想了解串口的基础知识可以参考我之前的文章: STM32Cube串口USART发送

    2024年02月04日
    浏览(49)
  • C#与松下PLC串口通讯发送,接收数据

    记录与学习 第一次跟PLC打交道,C#与松下plc交互读写功能,很多东西都是自己在网上找的,整理了下做个记录  引入“Panasonic.dll”文件 下载地址 百度盘百度网盘 请输入提取码  提取码:8vnm  public Panasonic.PLC Sp_PLC;   Sp_PLC.WCS(\\\"R\\\", \\\"1\\\", true);//提示PLC软件初始化完成,可以正常工

    2023年04月12日
    浏览(33)
  • C#串口通信从入门到精通(26)——多个串口多个线程发送数据和接收数据

    我们在开发串口程序的过程中有时候会遇到多个串口,并且多个串口也需要在多个线程进行操作,本文就来讲解如何实现多个串口在多线程下的安全发送与接收。 我们首先使用虚拟串口助手虚拟COM1、COM2这一对串口;COM3、COM4这一对串口,然后使用代码操作COM1,然后打开一个

    2024年02月11日
    浏览(40)
  • Qt+C++串口调试接收发送数据曲线图

    程序示例精选 Qt+C++串口调试接收发送数据曲线图 如需安装运行环境或远程调试,见文章底部个人 QQ 名片,由专业技术人员远程协助! 这篇博客针对Qt+C++串口调试接收发送数据曲线图编写代码,代码整洁,规则,易读。 学习与应用推荐首选。 一、所需工具软件 二、使用步骤

    2024年02月11日
    浏览(32)
  • 串口通信——发送和接收数据(8位和16位数据之间的转换)

    1.发送两个字节数据,就是16位的数据,每一次发送8位,发送两次,这里要进行数据的拆分,如发送一个0XFF56,接收得到的也是FF56(16进制显示); 2.接收两个字节的数据(这里通过串口助手以16进制发送一个数据),将拼接的数据(只能一个字节一个字节接收)除以100展示出来

    2024年01月17日
    浏览(30)
  • QT三驾马车(一)——实现上位机(串口数据发送和接收)

    以后同学们做项目一定会用到QT的三驾马车,QT的三驾马车即QT的串口编程,QT的网络编程和QT的GPIO,今天我们通过一个项目来介绍第一部分,QT的串口编程。 之前看过很多相关的文章,但是按照顺序来编译总是会出错,可是我自己还找不到原因,对于我这种新手小白来说极其

    2024年02月15日
    浏览(36)
  • 【PHP】PHP实现与硬件串口交互,接收硬件发送的实时数据

    目的:借助虚拟串口软件(VSPD)模拟硬件串口发送数据,使用PHP语言实现接收硬件发送的数据。 我这里的需求是连接天平,把天平的称量数据实时的传送到PHP使用。 使用工具:vspd+串口调试工具 使用语言:PHP 使用到的工具有VSPD和串口调试工具,其中VSPD是模拟硬件串口,串

    2024年02月02日
    浏览(29)
  • FPGA接收串口数据并通过LCD1602显示

    一、前言 在学习《FPGA设计与Verilog HDL实现》第九章内容Verilog驱动常用I/O外设时,书中有一个驱动LCD1602的例程,但其是通过状态机显示固定的几个字符。本着动手实践的原则,决定利用手头的硬件实现FPGA接收串口数据并在LCD1602上显示,下面记录项目开始的过程。因为刚接触

    2024年02月06日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包