11 串口发送应用之使用状态机实现多字节数据发送

1. 使用串口发送5个字节数据到电脑

uart协议规定,发送的数据位只能是6,7,8位,如果数据位不符合,接收者接收不到数据。所以我们需要将40位数据data分为5个字节数据分别发送,那么接收者就能通过uart协议接收到数据了。

2. 第一次使用状态机写设计代码(不够简洁的版本)

为什么要使用状态机:由于在always语句块中,语句是并行执行的,当我想要处理有先后顺序的问题时,就需要用状态机来解决。

针对发送五个字节数据到电脑的目的按,可将状态机的使用分为以下三种情况:

1. 没有开始发送(数据请求trans_go信号没有出现)

2. 数据请求trans_go信号出现

3. 数据请求trans_go信号出现

2.1 设计代码

module uart_tx_data(
    clk,
    rstn,
    trans_go,
    data40,
    uart_tx,
    trans_done
);

    input clk;
    input rstn;
    input trans_go;
    input [39:0] data40;
    output uart_tx;
    output reg trans_done;
    
    reg [7:0] data;
    reg send_go;
    wire tx_done;
    
    uart_byte_tx uart_byte_tx(
        .clk(clk),
        .rstn(rstn),
        .blaud_set(3'd4),
        .data(data),
        .send_go(send_go),
        .uart_tx(uart_tx),
        .tx_done(tx_done)
    );
    
    reg [2:0]state;
    always@(posedge clk or negedge rstn)
    if(!rstn) begin
        state <= 0;
        send_go <= 0;
        data <= 0;
        trans_done <= 0;
    end    
    else 
        case(state)
        0:  begin
                if(trans_go)begin
                    trans_done <= 0;
                    data <= data40[7:0];
                    send_go <= 1;
                    state <= 1;
                end
                else begin
                    data <= data;
                    send_go <= 0;
                    state <= 0;
                end
            end
            
        1:begin
              if(tx_done)begin
                  data <= data40[15:8];
                  send_go <= 1;
                  state <= 2;
              end
              else 
                  send_go <= 0;
          end

       2:begin
              if(tx_done)begin
                  data <= data40[23:16];
                  send_go <= 1;
                  state <= 3;
              end
              else
                  send_go <= 0;
         end
            
       3:begin
              if(tx_done)begin
                  data <= data40[31:24];
                  send_go <= 1;
                  state <= 4;
              end
              else
                  send_go <= 0;
         end

       4:begin
              if(tx_done)begin
                  data <= data40[39:32];
                  send_go <= 1;
                  state <= 5;
              end
              else
                  send_go <= 0;
          end
        
        5:begin
              if(tx_done)begin
                  trans_done <= 1;
                  state <= 0;
              end
              else
                  send_go <= 0;
           end
        default: begin
                data <= data;
                send_go <= 0;
                state <= 0;
        end
     endcase

endmodule
module uart_byte_tx(
    clk,
    rstn,
    blaud_set,
    data,
    send_go,
    uart_tx,
    tx_done
);
    
    input clk;
    input rstn;
    input [2:0]blaud_set;
    input [7:0]data;
    input send_go;
    output reg uart_tx;
    output tx_done;
    
    //Blaud_set = 0时,波特率 = 9600;
    //Blaud_set = 1时,波特率 = 19200;
    //Blaud_set = 2时,波特率 = 38400;
    //Blaud_set = 3时,波特率 = 57600;
    //Blaud_set = 4时,波特率 = 115200;
    
    reg[17:0] bps_dr;
    always@(*)
        case(blaud_set)
            0: bps_dr = 1000000000/9600/20;
            1: bps_dr = 1000000000/19200/20;
            2: bps_dr = 1000000000/38400/20;
            3: bps_dr = 1000000000/57600/20;
            4: bps_dr = 1000000000/115200/20;
        endcase
        
    reg [7:0] r_data;
    always@(posedge clk)
    if(send_go)
        r_data <= data;
    else
        r_data <= r_data;
        
    reg send_en;  
    always@(posedge clk or negedge rstn)
    if(!rstn)
        send_en <= 0;
    else if(send_go)
        send_en <= 1;
    else if(tx_done)
        send_en <= 0;
        
    
    wire bps_clk;
    assign bps_clk = (div_cnt == 1);
    reg[17:0] div_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        div_cnt <= 0;
    else if(send_en)begin
        if(div_cnt == (bps_dr - 1))
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'd1;
    end
    else
        div_cnt <= 0;    
    
    reg[3:0] bps_cnt;    
    always@(posedge clk or negedge rstn)
    if(!rstn)
        bps_cnt <= 0;
    else if(send_en)begin
        if(bps_cnt == 11)
            bps_cnt <= 0;
        else if(div_cnt == 1)
            bps_cnt <= bps_cnt + 4'd1;
    end
    else
        bps_cnt <= 0;
    
    reg tx_done;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        uart_tx <= 1'd1;
    else 
        case(bps_cnt)
            0: tx_done <= 0;
            1: uart_tx <= 1'd0;
            2: uart_tx <= r_data[0];
            3: uart_tx <= r_data[1];
            4: uart_tx <= r_data[2];
            5: uart_tx <= r_data[3];
            6: uart_tx <= r_data[4];
            7: uart_tx <= r_data[5];
            8: uart_tx <= r_data[6];
            9: uart_tx <= r_data[7];
            10: uart_tx <= 1'd1;
            11: begin uart_tx <= 1'd1; tx_done <= 1; end
            default: uart_tx <= 1'd1;
        endcase
 
endmodule

2.2 仿真代码(学习trans_go脉冲信号以及数据发送完成信号)

以下两点需要学习:

  1. 通过控制trans_go信号的产生与结束,来模拟一个周期的脉冲信号
  2. 通过增加一个输出端口tx_done,来通知我输出何时完成
`timescale 1ns / 1ps

module uart_tx_data_tb();
    
    reg clk;
    reg rstn;
    reg trans_go;
    reg [39:0]data40;
    wire trans_done;
    wire uart_tx;

    uart_tx_data uart_tx_data_inst(
        .clk(clk),
        .rstn(rstn),
        .trans_go(trans_go),
        .data40(data40),
        .trans_done(trans_done),
        .uart_tx(uart_tx)
    );

    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
        rstn = 0;
        trans_go = 0;
        data40 = 0;
        #201;
        rstn = 1;
        #200;
        data40 = 40'h123456789a;
        trans_go = 1; //trans_go脉冲信号的模拟
        #20;
        trans_go = 0; //trans_go脉冲信号的模拟
        @(posedge trans_done) //数据发送完成信号的标识
        #200000;
        
        data40 = 40'ha987654321;
        trans_go = 1;
        #20;
        trans_go = 0;
        @(posedge trans_done)
        #200000;
        $stop;
    end

endmodule

仿真波形

3. 优化状态机代码

1. 任务:优化状态机,实现只要个或3个状态实现发送的功能,并且易于修改为发送任意个字节的数据

2. 征集不使用状态机的思想来实现本任务的方案

任务1完成如下,对于任务2,我的思路是:由于fpga是并行发送数据的,如果我们想要多字节发送数据的话,肯定需要将多字节串起来发送,所以我们可以将五个字节的数据串起来,每个字节之间相隔起始位和结束位,以此来达到在遵循协议的情况下实现多字节的输出。

3.1 设计代码(三个状态):

三个状态:

状态1.等待发送请求

状态2.等待单字节数据发送完成

状态3.检查所有数据是否发送完成

module uart_tx_data1(
    clk,
    rstn,
    trans_go,
    data40,
    uart_tx,
    trans_done
);

    input clk;
    input rstn;
    input trans_go;
    input [39:0] data40;
    output uart_tx;
    output reg trans_done;
    
    reg [7:0] data;
    reg send_go;
    wire tx_done;
    
    uart_byte_tx uart_byte_tx(
        .clk(clk),
        .rstn(rstn),
        .blaud_set(3'd4),
        .data(data),
        .send_go(send_go),
        .uart_tx(uart_tx),
        .tx_done(tx_done)
    );
    
    reg [2:0]state;
    reg [2:0]counter;
    always@(posedge clk or negedge rstn)
    if(!rstn) begin
        state <= 0;
        send_go <= 0;
        data <= 0;
        trans_done <= 0;
        counter <= 0;
    end    
    else 
        case(state)
        0:begin //等待发送请求
              if(trans_go)begin 
                  trans_done <= 0;
                  send_go <= 1;
                  data <= (data40>>8*counter);
                  state <= 1;
              end
              else begin 
                  data <= data;
                  send_go <= 0;
                  state <= 0;
              end
            end
            
        1:begin //等待单字节数据发送完成
                if(tx_done)begin
                    counter <= counter + 1'd1;
                    state <= 2;
                end
                else 
                    send_go <= 0;
          end
        
        2:begin //检查所有数据是否发送完成
            if(counter == 5) begin
                  trans_done <= 1;
                  state <= 0;
                  counter <= 0;
            end
            else begin
                  send_go <= 1;
                  data <= (data40>>(8*counter));
                  state <= 1;
             end
           end
           
        default: begin
                data <= data;
                send_go <= 0;
                state <= 0;
        end
     endcase

endmodule

 

module uart_byte_tx(
    clk,
    rstn,
    blaud_set,
    data,
    send_go,
    uart_tx,
    tx_done
);
    
    input clk;
    input rstn;
    input [2:0]blaud_set;
    input [7:0]data;
    input send_go;
    output reg uart_tx;
    output tx_done;
    
    //Blaud_set = 0时,波特率 = 9600;
    //Blaud_set = 1时,波特率 = 19200;
    //Blaud_set = 2时,波特率 = 38400;
    //Blaud_set = 3时,波特率 = 57600;
    //Blaud_set = 4时,波特率 = 115200;
    
    reg[17:0] bps_dr;
    always@(*)
        case(blaud_set)
            0: bps_dr = 1000000000/9600/20;
            1: bps_dr = 1000000000/19200/20;
            2: bps_dr = 1000000000/38400/20;
            3: bps_dr = 1000000000/57600/20;
            4: bps_dr = 1000000000/115200/20;
        endcase
        
    reg [7:0] r_data;
    always@(posedge clk)
    if(send_go)
        r_data <= data;
    else
        r_data <= r_data;
        
    reg send_en;  
    always@(posedge clk or negedge rstn)
    if(!rstn)
        send_en <= 0;
    else if(send_go)
        send_en <= 1;
    else if(tx_done)
        send_en <= 0;
        
    
    wire bps_clk;
    assign bps_clk = (div_cnt == 1);
    reg[17:0] div_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        div_cnt <= 0;
    else if(send_en)begin
        if(div_cnt == (bps_dr - 1))
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'd1;
    end
    else
        div_cnt <= 0;    
    
    reg[3:0] bps_cnt;    
    always@(posedge clk or negedge rstn)
    if(!rstn)
        bps_cnt <= 0;
    else if(send_en)begin
        if(bps_cnt == 11)
            bps_cnt <= 0;
        else if(div_cnt == 1)
            bps_cnt <= bps_cnt + 4'd1;
    end
    else
        bps_cnt <= 0;
    
    reg tx_done;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        uart_tx <= 1'd1;
    else 
        case(bps_cnt)
            0: tx_done <= 0;
            1: uart_tx <= 1'd0;
            2: uart_tx <= r_data[0];
            3: uart_tx <= r_data[1];
            4: uart_tx <= r_data[2];
            5: uart_tx <= r_data[3];
            6: uart_tx <= r_data[4];
            7: uart_tx <= r_data[5];
            8: uart_tx <= r_data[6];
            9: uart_tx <= r_data[7];
            10: uart_tx <= 1'd1;
            11: begin uart_tx <= 1'd1; tx_done <= 1; end
            default: uart_tx <= 1'd1;
        endcase
 
endmodule

 

仿真代码

`timescale 1ns / 1ps

module uart_tx_data1_tb();
    
    reg clk;
    reg rstn;
    reg trans_go;
    reg [39:0]data40;
    wire trans_done;
    wire uart_tx;

    uart_tx_data1 uart_tx_data_inst1(
        .clk(clk),
        .rstn(rstn),
        .trans_go(trans_go),
        .data40(data40),
        .trans_done(trans_done),
        .uart_tx(uart_tx)
    );

    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
        rstn = 0;
        trans_go = 0;
        data40 = 0;
        #201;
        rstn = 1;
        #200;
        data40 = 40'h123456789a;
        trans_go = 1;
        #20;
        trans_go = 0;
        @(posedge trans_done);
        #200000;
        
        data40 = 40'ha987654321;
        trans_go = 1;
        #20;
        trans_go = 0;
        @(posedge trans_done);
        #200000;
        $stop;
    end

endmodule

仿真波形

3.2 调试

调试1:counter位宽给错了,counter要记到5,但是只给了[1:0]两位:

调试2:counter记到5后未清零,导致数据多发了三次,且由于data = data40>>8*counter,导致数据为00:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

django微博热搜数据分析与可视化系统python毕业设计

简而言之&#xff0c;数据可视化是以图形方式呈现结构化或非结构化数据&#xff0c;从而将隐藏在数据中的信息直接呈现给人们。但是有一个陷阱:它不仅仅是使用数据可视化工具将数据转化为图形。相反&#xff0c;它是从数据的角度看待世界。换句话说&#xff0c;数据可视化的对象…

在django中集成markdown文本框

首先需要下载开源组件&#xff1a;http://editor.md.ipandao.com/&#xff0c;可能需要挂梯子。 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1D9o3P8EQDqSqfhAw10kYkw 提取码&#xff1a;eric 1.在html代码中生成一个div&#xff0c;ideditor <div c…

[Python] 深入理解列表和元组

在学习的C语言中有数组可以用来存储数据&#xff0c;那么在Python中是否也有这样的工具呢&#xff1f;接下来让可莉来给大家讲解列表和元组这两个强力工具吧~ 专栏&#xff1a;《Python》 blog&#xff1a;Keven ’ s blog 在 Python 中&#xff0c;列表和元组是两种常用的序列…

【MySQL进阶之路】BufferPool底层设计(下)

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

购物|电商购物小程序|基于微信小程序的购物系统设计与实现(源码+数据库+文档)

电商购物小程序目录 目录 基于微信小程序的购物系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户前台功能实现 2、管理员后台功能实现 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xff1a; 五、核心代码 六、论文参考 七、最新计算机毕设…

MySQL数据库——索引

索引是数据结构&#xff0c;用于高效获取数据的数据结构&#xff08;有序&#xff09;。 1、索引的优缺点&#xff1a; 1.1、优点&#xff1a; a、提高数据检索效率&#xff0c;降低数据库的IO成本&#xff08;提高查询效率&#xff09; b、通过索引列对数据进行排序&#…

ClickHouse--01--简介

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1. ClickHouse 简介1.1 大数据处理场景1.2 什么是 ClickHouse1.3 OLAP 场景的特征 2. ClickHouse 特性2.1 完备的 DBMS 功能2.2 列式存储行式存储: 在数据写入和修改…

玉米基因miRNA结合位点预测工具

前记 目前&#xff0c;已经有很多种玉米miRNA结合位点预测工具可供选择&#xff0c;以下几种比较常用&#xff1a; 1、psRNATarget&#xff1a;该工具是由华盛顿州立大学开发的&#xff0c;可以用来预测植物miRNA和靶基因之间的相互作用。用户可以使用该工具来预测玉米miRNA和结…

【Web】vulhub Shiro-550反序列化漏洞复现学习笔记

目录 Shiro简介 复现流程 工具一把梭 半脚本半手动 原理分析 反序列化入口 常见的key 登录过程 验证过程 利用原理 Shiro简介 Apache Shiro 是一个强大且易于使用的 Java 安全框架&#xff0c;用于身份验证、授权、加密和会话管理等安全功能。Shiro 的设计目标是简单…

C++入门全集(1):初窥门径

一、前言 C是一种计算机高级程序设计语言&#xff0c;它在C语言的基础上进行了进一步的扩充和完善&#xff0c;并增加了许多有用的库&#xff0c;是一种面向对象的程序设计语言。 所以&#xff0c;C是兼容C语言语法的。 我打算把所有C入门需要学习的知识整合成一个全集&…

DNS 域名系统——应用层

目录 1 域名系统 DNS 1.1 域名系统 1.2 互联网的域名结构 1.2.1 顶级域名 TLD(Top Level Domain) (1) 国家顶级域名 nTLD (2) 通用顶级域名 gTLD (3) 基础结构域名 (infrastructure domain) 1.3 域名服务器 1.3.1 域名服务器的四种类型 &#xff08;1…

bpmn.js一个基于Bpmn 2.0的前端工作流展示和绘制工具

bpmn.js是由开源工作流引擎camunda内部组织BPMN.IO组织开发的一款基于BPMN 2.0的工作流展示、编辑的web端工具库。由于工作流引擎activiti、flowable、camunda属于同宗分流&#xff0c;其工作流定义格式大致相同&#xff0c;所以我们可以使用bpmn.js完美融合其中任一工作流引擎…

使用 openpyxl 操作 Excel

由于单位有任务&#xff0c;需要按照名册制作多个工作表。手动复制和修改内容太费事了&#xff0c;所以使用python完成此项工作&#xff0c;为之后的此类工作提供一个通用脚本。 安装依赖库 pip install openpyxl lxml我们需要用到openpyxl。在官方文档中提到&#xff0c;如果…

SpringCloud-微服务概述、SpringCloud入门概述、服务提供与消费

1.学习前言 1.1 学习前提 熟练使用SpringBoot 微服务快速开发框架了解过Dubbo Zookeeper 分布式基础电脑配置内存不低于8G 1.2 文章大纲 Spring Cloud 五大组件 服务注册与发现——Netflix Eureka负载均衡&#xff1a; ​ 客户端负载均衡——Netflix Ribbon ​ 服务端负载…

前端JavaScript篇之对this对象的理解

目录 对this对象的理解1. 函数调用模式&#xff1a;2. 方法调用模式&#xff1a;3. 构造器调用模式&#xff1a;4. apply、call和bind调用模式&#xff1a; 对this对象的理解 在JavaScript中&#xff0c;this关键字是一个非常重要的概念&#xff0c;它用于指向当前执行上下文中…

【从Python基础到深度学习】2. Ubuntu及插件安装

本期所有软件安装包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1UVEYm-12FivAnrE5NUXevg?pwdum60 一、安装Ubuntu 1.1 软件安装包&#xff1a;下载 VMware Workstation Pro | CN 一直点下一步即可 1.2 双击运行软件&#xff1a; 输入密钥 1 、VMware 15密钥 …

香港倾斜模型3DTiles数据漫游

谷歌地球全香港地区倾斜摄影数据&#xff0c;通过工具转换成3DTiles格式&#xff0c;将这份数据完美加载到三维数字地球Cesium上进行完美呈现&#xff0c;打造香港地区三维倾斜数据覆盖&#xff0c;完美呈现香港城市壮美以及维多利亚港繁荣景象。再由12.5米高分辨率地形数据&am…

双指针和单调栈

双指针 用于解决一类基于子段的统计问题 子段就是&#xff1a;数组中连续的一段 可以用一个闭区间来表示数组中的连续一段 这个方法核心就是优化&#xff1a;两种循环的枚举 也就是枚举左端点l和右端点r的所有可能优化关键就是&#xff1a;去除枚举中的冗余部分 具体优化策略…

eslint报错文档大量红色报错符号 不自动修正

确保eslint在工作 控制台大量报错信息 确保setting.json 开了保存的时候自动格式化代码 这个时候保存的时候代码可以自动被格式化 但是 文档中和控制台中仍然有大量的报错 信息 此时此刻说明 格式化文档的文件不是按照eslint 格式化的 可以网上找找现成可用的setting.json抄…

【linux温故】CFS调度

写在前面 网上关于CFS 调度器的文章多如牛毛&#xff0c;没必要自己写。很多文章写的都非常好。 很多文章里&#xff0c;关键的技术点&#xff0c;都是一样的&#xff0c;只是各个文章说法不一样。 掌握了核心的&#xff0c;关键的&#xff0c;其他的&#xff0c;如果工作中…