Verlog-串口发送-FPGA

Verlog-串口发送-FPGA

引言:

​ 随着电子技术的不断进步,串口通信已成为嵌入式系统和计算机外设中一种广泛使用的异步通信方式。串口通信因其简单性、可靠性以及对硬件资源的低要求,在数据传输领域扮演着重要角色。在FPGA(现场可编程门阵列)设计中,实现串口通信功能是连接外部处理器和FPGA、实现数据交换的关键技术之一。 本实验旨在通过Verilog语言,设计并实现一个能够在FPGA上运行的串口发送模块。通过本实验,将有机会深入了解FPGA的通信接口设计,掌握Verilog语言在串口通信应用中的编程技巧,以及如何将理论知识应用到实际的硬件设计中。

希望你在本次学习过后,能够有一定的收获!!!

​ 冲啊!!!! ٩(͡๏̯͡๏)۶ ٩(͡๏̯͡๏)۶ ٩(͡๏̯͡๏)۶

文章目录

  • Verlog-串口发送-FPGA
    • 一、任务介绍
    • 二、知识基础
      • 1、字符编码(GBK)
      • 2、串口通信
    • 三、实验内容
      • 1、实验代码
        • ctrl.v
        • uart_tx.v
      • 2、实验原理
      • 3、实验器材
      • 4、引脚配置图
    • 四、实验效果
    • 五、总结

一、任务介绍

  1. 熟悉FPGA的开发流程
  2. 练习并且巩固有关于verilog代码的相关内容
  3. 使用Verilog语言编写程序实现FPGA的串口输出
  4. 了解熟悉GBK编码

二、知识基础

1、字符编码(GBK)

GBK编码概述: GBK是一种用于简体中文字符的编码方式,它是对GB2312编码的扩展,支持更多的汉字和符号。GBK编码可以表示21003个汉字和图形符号,包括6763个汉字,并且兼容ASCII编码中的所有字符。

GBK编码特点:

  1. 双字节表示: 每个GBK字符由两个字节组成,最高位为1,这样可以与ASCII编码区分开来。
  2. 兼容GB2312: GBK继承了GB2312的编码规则,同时增加了对更多汉字的支持。
  3. 广泛的应用: 在中文操作系统、数据库和网页中,GBK被广泛用于表示中文字符。

GBK编码在串口通信中的应用: 在FPGA的串口通信中,经常需要发送或接收文本信息。由于ASCII编码只能表示128个字符,不足以涵盖所有的中文字符,因此,使用GBK编码可以传输完整的中文文本。在设计串口发送模块时,需要将待发送的字符串转换为GBK编码的字节序列,然后通过串口发送出去。

GBK编码转换:

  1. 编码转换: 将需要发送的字符串转换为GBK编码的字节序列。这可以通过编程实现,也可以使用专门的工具完成。
  2. 串口发送: 将转换后的GBK字节序列通过FPGA的串口发送出去。在Verilog代码中,需要设计一个发送状态机来控制数据的发送过程。

实验中的GBK应用: 在本实验中,将学习如何将字符串转换为GBK编码,并在FPGA上实现串口发送模块,将GBK编码的数据通过串口发送出去。这不仅涉及到GBK编码的知识,还需要掌握Verilog语言的编程技巧和FPGA的串口通信机制。

编码转换网站:查看字符编码(简体中文) (mytju.com)

在这里插入图片描述

2、串口通信

串口通信概述: 串口通信是一种计算机硬件设备之间常用的串行通信方式,它允许数据以串行的方式在设备之间传输。串口通信广泛应用于嵌入式系统、计算机外设、工业控制系统等领域。

串口通信特点:

  1. 串行传输: 数据一位接一位地在发送设备和接收设备之间传输。
  2. 异步通信: 发送和接收设备使用各自的时钟信号,不需要同步时钟。
  3. 简单易用: 只需要少数几根线(如TX、RX、GND),易于实现和扩展。
  4. 波特率: 串口通信的数据传输速率,以比特每秒(bps)计量。

串口通信的关键参数:

  • 数据位: 每个字符的数据宽度,常见的有7位、8位等。
  • 停止位: 数据传输结束后的一位或两位空闲状态,用于标识数据结束。
  • 奇偶校验位: 用于检测数据传输中的错误,可以是奇校验、偶校验或无校验。

串口通信在FPGA设计中的应用: 在FPGA设计中,实现串口通信功能可以用于:

  1. 与外部设备通信: 如与PC机、微控制器或其他FPGA芯片交换数据。
  2. 调试和监控: 通过串口输出FPGA内部的状态信息,便于调试和监控。
  3. 数据传输: 实现与其他系统的串行数据传输。

FPGA串口通信设计要点:

  1. 波特率发生器: 根据所需的波特率生成时钟信号,控制数据的发送和接收速率。
  2. 发送器(TX): 负责将并行数据转换为串行数据,并控制数据的发送。
  3. 接收器(RX): 负责将串行数据转换为并行数据,并处理接收到的数据。
  4. 状态机: 通常使用有限状态机(FSM)来控制数据的发送和接收过程。

实验中的串口通信实现: 在本实验中,将学习如何使用Verilog语言在FPGA上实现串口发送模块。这包括:

  • 设计波特率发生器,生成合适的时钟信号。
  • 实现发送器,将待发送的数据转换为串行格式,并控制数据的发送。
  • 通过FPGA的串口接口(如UART)与外部设备进行通信。

通过本实验,将深入理解串口通信的原理和实现方法,为未来在更复杂的通信协议和系统设计中打下坚实的基础。

三、实验内容

1、实验代码

ctrl.v
module ctrl (
    input clk,
    input rst_n,
    output tx
);

    wire    [7:0] send_data[31:0] ; // 发送的数据数组 -- Hello World !!! 你干嘛,哎哟~~~
    assign send_data[00] = {8'h48};
    assign send_data[01] = {8'h65};
    assign send_data[02] = {8'h6C};
    assign send_data[03] = {8'h6C};
    assign send_data[04] = {8'h6F};
    assign send_data[05] = {8'h20};
    assign send_data[06] = {8'h57};
    assign send_data[07] = {8'h6F};
    assign send_data[08] = {8'h72};
    assign send_data[09] = {8'h6C};
    assign send_data[10] = {8'h64};
    assign send_data[11] = {8'h20};
    assign send_data[12] = {8'h21};
    assign send_data[13] = {8'h21};
    assign send_data[14] = {8'h21};
    assign send_data[15] = {8'h20};
    assign send_data[16] = {8'hC4};
    assign send_data[17] = {8'hE3};
    assign send_data[18] = {8'hB8};
    assign send_data[19] = {8'hC9};
    assign send_data[20] = {8'hC2};
    assign send_data[21] = {8'hEF};
    assign send_data[22] = {8'hA3};
    assign send_data[23] = {8'hAC};
    assign send_data[24] = {8'hB0};
    assign send_data[25] = {8'hA5};
    assign send_data[26] = {8'hD3};
    assign send_data[27] = {8'hB4};
    assign send_data[28] = {8'h7E};
    assign send_data[29] = {8'h7E};
    assign send_data[30] = {8'h7E};
    assign send_data[31] = {8'h0A}; // 换行

    parameter TIME_pause   = 28'd 50_000_000;

    // 每次完整的发送时间 1s
    reg    [40-1:0]    cnt_pause     ; 
    wire   add_cnt_pause ,  end_cnt_pause ; 
    always @(posedge clk or negedge rst_n) 
        if (!rst_n)
            cnt_pause  <= 40'b0;
        else if (add_cnt_pause )
            if (end_cnt_pause )
                cnt_pause <=40'b0;
            else 
                cnt_pause  <= cnt_pause  +1'd1;
        else 
            cnt_pause  <= cnt_pause ;
    assign add_cnt_pause  = 1'b1;
    assign end_cnt_pause  = add_cnt_pause  && (TIME_pause -1 == cnt_pause );
    // 每次完整的发送时间 1s

    // 每个1s 切换一次发送状态
    reg flag_sent;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            flag_sent <= 1'b0;
        else if (end_cnt_pause)
            flag_sent <= ~flag_sent;
        else 
            flag_sent <= flag_sent;
    end
    // 每个1s 切换一次发送状态

    // 每一个字符之间的间隔发送时间
    reg    [40-1:0]    cnt_bit_pause     ; 
    wire   add_cnt_bit_pause ,  end_cnt_bit_pause ; 
    always @(posedge clk or negedge rst_n) 
        if (!rst_n)
            cnt_bit_pause  <= 40'b0;
        else if (add_cnt_bit_pause )
            if (end_cnt_bit_pause )
                cnt_bit_pause <=40'b0;
            else 
                cnt_bit_pause  <= cnt_bit_pause  +1'd1;
        else 
            cnt_bit_pause  <= cnt_bit_pause ;
    assign add_cnt_bit_pause  = flag_sent;
    assign end_cnt_bit_pause  = add_cnt_bit_pause  && (cnt_bit_pause == (300_00 -1));
    // 每一个字符之间的间隔发送时间

    // 一个句是否发送完成标志
    reg one_epoch;



    // 单个字符数据
    reg     [7:0]   tx_data         ;

    reg    [6-1:0]    cnt_bit     ; 
    wire   add_cnt_bit ,  end_cnt_bit ; 
    always @(posedge clk or negedge rst_n) 
        if (!rst_n)
            cnt_bit  <= 6'b0;
        else if (add_cnt_bit)
            if (end_cnt_bit )
                cnt_bit <=6'b0;
            else 
                cnt_bit  <= cnt_bit  +1'd1;
        else 
            cnt_bit  <= cnt_bit ;
    assign add_cnt_bit  = flag_sent && end_cnt_bit_pause && !one_epoch;
    assign end_cnt_bit  = add_cnt_bit  && (6'd32-1 == cnt_bit );


    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            one_epoch <= 1'b0;
        else if (end_cnt_bit)
            one_epoch <= 1'b1;
        else if(!flag_sent)
            one_epoch <= 1'b0;
        else 
            one_epoch <= one_epoch;
    end




    always @(*) begin
        tx_data <= send_data[cnt_bit];
    end

    uart_tx inst_tx_uart (
        .clk                (clk),
        .rst_n              (rst_n),
        .din                (tx_data),
        .din_vld            (end_cnt_bit_pause && !one_epoch),
        .dout               (tx)
    );

endmodule
uart_tx.v
module uart_tx (
    input clk,
    input rst_n,
    input [7:0] din,
    input din_vld,
    output reg dout
);
    parameter BAUD = 434;

    reg [8:0] cnt_bsp;
    wire add_cnt_bsp;
    wire end_cnt_bsp;

    reg [3:0] cnt_bit;
    wire add_cnt_bit;
    wire end_cnt_bit;

    reg flag;
    reg [8:0] data;

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) cnt_bsp <= 0;
        else if(add_cnt_bsp)
            if(end_cnt_bsp) cnt_bsp <= 0;
            else cnt_bsp <= cnt_bsp + 1;
        else cnt_bsp <= cnt_bsp;
    end
    assign add_cnt_bsp = flag;
    assign end_cnt_bsp = add_cnt_bsp && (cnt_bsp == BAUD - 1);


    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) cnt_bit <= 0;
        else if(add_cnt_bit)
            if(end_cnt_bit) cnt_bit <= 0;
            else cnt_bit <= cnt_bit + 1;
        else cnt_bit <= cnt_bit;
    end
    assign add_cnt_bit = end_cnt_bsp;
    assign end_cnt_bit = add_cnt_bit && (cnt_bit == 8);

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n) flag = 0;
        else if (din_vld) flag <=1;
        else if (end_cnt_bit) flag <=0;
        else flag <= flag;
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n) data <= !0;
        else if (din_vld)  data <= {din,1'b0} ; // 数据加上起始位
        else data <= data;
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n) dout <=1;
        else if (cnt_bsp == 1) dout <= data[cnt_bit]; // 串行输出 LSB
        else if (end_cnt_bit) dout <= 1'b1;
        else dout <= dout;  
    end


endmodule

2、实验原理

模块功能概述: 本实验的目的是设计一个能够在FPGA上运行的串口发送模块,该模块能够通过串口发送预定义的字符串数据。代码中定义了一个发送数据数组send_data,包含了要发送的每个字符的ASCII码或GBK码。

串口发送原理:

  1. 数据数组定义: 使用一个寄存器数组send_data来存储要发送的字符数据,每个字符占用8位,数组长度可以根据需要发送的字符数量来定义。
  2. 发送数据加载: 通过assign语句将特定的ASCII码或GBK码赋给send_data数组的每个元素,形成要发送的数据序列。
  3. 波特率控制: 通过一个计数器生成与设定波特率相匹配的时钟信号,控制数据发送的速率。
  4. 发送状态机: 利用有限状态机(FSM)管理数据的发送过程,包括起始位的发送、数据位的逐位发送、以及停止位的添加。

代码实现细节:

  1. 数据发送控制: 使用标志位flag_sent来控制数据的发送,该标志位每秒切换一次,以实现每秒发送一次数据数组。
  2. 发送暂停计数: 使用cnt_pause计数器和相关逻辑来实现发送之间的暂停,以便在发送每个字符后提供适当的延迟。
  3. 位发送控制: 使用cnt_bit计数器来控制单个字符的位发送过程,确保每个字符的8位数据按顺序发送。
  4. 串口发送模块: 实例化uart_tx模块,它负责根据波特率控制信号逐位发送数据,并在每发送完一个字节后添加停止位。

波特率发生器:

  • uart_tx模块内部实现了一个波特率发生器,通过计数器cnt_bsp来控制发送速率,以匹配预设的波特率BAUD

数据发送:

  • 数据通过data寄存器存储,并在每个时钟周期发送最低位(LSB),直到8位数据全部发送完毕。

串口信号输出:

  • 串口的dout信号负责输出当前正在发送的位,起始位为0,停止位为1。

实验关键点:

  • 数据编码: 理解如何将字符转换为对应的ASCII或GBK编码。
  • 时序控制: 掌握如何使用计数器和状态机来控制数据的发送时序。
  • 串口通信规范: 了解串口通信的标准,包括起始位、数据位、停止位和波特率。

通过本实验,将能够理解并实践FPGA上串口发送模块的设计,为进一步学习更复杂的串口通信应用打下基础。

3、实验器材

在这里插入图片描述

在这里插入图片描述

4、引脚配置图

在这里插入图片描述

四、实验效果

v-串口

五、总结

经过本次实验,我成功设计并实现了一个FPGA上的串口发送模块,该模块能够按照预定的波特率发送存储在数据数组send_data中的字符串。通过这个过程,我深入理解了串口通信的工作原理和在FPGA上实现它所需的关键技术。 实验过程回顾:

  1. 我首先定义了一个包含所有要发送字符的数组send_data,并为每个字符分配了相应的ASCII或GBK编码。
  2. 我实现了一个发送控制逻辑,使用一个标志位flag_sent来控制每秒发送一次数据数组中的字符。
  3. 我设计了一个波特率发生器,通过计数器cnt_bsp来控制数据的发送速率,以匹配预设的波特率。
  4. 我利用了一个状态机来管理数据的发送过程,包括起始位的生成、数据位的逐位发送和停止位的添加。
  5. 我实例化了一个uart_tx模块,它负责实际的串口信号输出,包括数据位的串行化和波特率控制。

实验成果: 通过本次实验,我不仅掌握了Verilog语言在串口通信应用中的编程技巧,还学会了如何将理论知识应用到实际的硬件设计中。我能够独立完成从设计、仿真到硬件测试的整个FPGA设计流程,并且能够通过外部串口工具接收发送的数据。

实验反思: 在实验过程中,我遇到了一些挑战,比如确保数据在发送过程中的时序准确性,以及调试串口通信中的常见问题。通过不断测试和调整,我学会了如何对FPGA设计进行调试,解决可能出现的问题。

未来展望: 虽然本次实验已经完成,但我认识到在FPGA设计和串口通信方面还有许多可以学习和探索的地方。未来,我计划探索更复杂的通信协议,如SPI、I2C等,并尝试将这些技术应用到更大型的FPGA项目中。

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

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

相关文章

Nachi那智不二越机器人维修技术合集

一、Nachi机械手维护基础知识 1. 定期检查&#xff1a;定期检查机器人的各个部件&#xff0c;如机械手伺服电机、机器人减速器、机械臂传感器等&#xff0c;确保其运行正常。 2. 清洁与润滑&#xff1a;定期清洁Nachi工业机器人表面和内部&#xff0c;并使用合适的润滑油进行润…

用websocket实现一个简单的im聊天功能

WebSocket前后端建立以及使用-CSDN博客 经过我上一篇的基本理解websocket的建立以及使用后&#xff0c;这篇就写一个简单的demo 实现im聊天 首先就是后端代码&#xff0c;详细解释我都放到了每一句的代码解析了&#xff0c;文章最后我会说怎么运行流程 放置后端代码 packa…

闪耀2023“物联之星”,迈威通信荣膺中国物联网企业创新产品榜

近日&#xff0c;IOTE 2024中国智联网生态大会暨“2023物联之星”年度榜单颁奖典礼在沪召开&#xff0c;迈威通信凭借其出色的技术创新和产品实力&#xff0c;其千兆卡轨式工业级5G路由器MIR785R成功荣登物联之星2023年度榜单——“中国物联网行业创新产品”。这不仅是对迈威通…

ICode国际青少年编程竞赛- Python-4级训练场-嵌套for循环练习

ICode国际青少年编程竞赛- Python-4级训练场-嵌套for循环练习 1、 for i in range(3):Spaceship.step(4)for j in range(4):Dev.step(2)Dev.turnRight()Spaceship.turnLeft()Spaceship.step(4)Spaceship.turnRight()2、 for i in range(4):Spaceship.step(6)for j in range(3):…

鸿蒙开发接口Ability框架:【(窗口扩展能力)】

窗口扩展能力 WindowExtensionAbility基于ExtensionAbility&#xff0c;WindowExtensionAbility中展示的内容作为一个控件(AbilityComponent)内容展示在其他应用窗口中&#xff0c;实现在一个窗口中展示多个应用程序内容的功能。 说明&#xff1a; 本模块首批接口从API versio…

探索智慧推理:线上剧本杀小程序引领新潮流

随着科技的飞速发展&#xff0c;线上剧本杀小程序作为一种新兴的数字娱乐形式&#xff0c;正以其独特的魅力引领着新潮流&#xff0c;并在内容创造上展现出无限的潜力。这种融合了角色扮演、推理解谜和社交互动的游戏模式&#xff0c;不仅为用户带来了沉浸式的体验&#xff0c;…

创新指南 |风险管理:企业如何应对颠覆性创新带来的挑战

在当今充满变数的商业时代&#xff0c;颠覆性创新是企业突破传统、引领市场的关键。然而&#xff0c;真正的创新之旅并不平坦&#xff0c;充满了挑战和不确定性。文章将快速引导读者了解颠覆性创新的定义、挑战、以及如何通过设计思维、市场洞察、利益相关者沟通和文化变革等关…

ok_Keil实用小技巧 | Keil定制Hex文件名实现的方法

Keil实用小技巧 | Keil定制Hex文件名实现的方法 echo off REM 可执行文件&#xff08;Hex&#xff09;文件名 set HEX_NAMEDemo REM 可执行文件&#xff08;Hex&#xff09;文件路径 set HEX_PATH.\Objects REM 定制Hex输出路径 set OUTPUT_PATH.\Output REM 软件版本文件…

oracle 新_多种块大小的支持9i

oracle 新_多种块大小的支持 conn sys/sys as sysdba SHOW PARAMETER CACHE ALTER SYSTEM SET DB_CACHE_SIZE16M; ALTER SYSTEM SET DB_4K_CACHE_SIZE8M; CREATE TABLESPACE K4 DATAFILE F:\ORACLE\ORADATA\ZL9\K4.DBF SIZE 2M BLOCKSIZE 4K; CREATE TABLE SCOTT.A1 TABLESP…

国内有哪些知名的网络安全厂商?

首先就是360&#xff0c;这个我相信大家并不陌生了吧&#xff0c;你的电脑装过360么&#xff1f; 360在个人终端服务那是妥妥的扛把子&#xff0c;但是在企业服务里虽然有他们的身影却略显不足。 第二个就是深信服&#xff0c;网络安全的老牌大佬&#xff0c;业务覆盖了全球5…

java日历类概述

Java中的Calendar类位于java.util包下&#xff0c;它是一个抽象类&#xff0c;用于表示和管理日期及时间。Calendar类并不是直接实例化的&#xff0c;而是通过其提供的静态方法来获取实例。通常情况下&#xff0c;当你尝试创建一个Calendar实例时&#xff0c;实际上你得到的是G…

skywalking的使用

文章目录 介绍概念介绍探针agent后台服务 使用后台界面查询异常接口查看访问量 遇到的问题 介绍 官网 https://skywalking.apache.org/ 安装包下载 https://skyapm.github.io/document-cn-translation-of-skywalking/ 组成 Agent&#xff08;探针&#xff09;&#xff1a;Ag…

MSP430环境搭建

1.下载ccs编译器 注意&#xff1a;安装路径和工作路径不能出现中文&#xff01; 没有说明的步骤就点next即可&#xff01; 1.1下载适合自己电脑的压缩包。 下载好压缩包后解压&#xff0c;点击有图标进行安装。 1.2创建一个文件夹用于安装编译器位置 选择安装地址&#xff0…

vue3实现el-table导出为excel表格(亲测可用)

✅ 作者 : 布克吉&#xff08;微信公众号同名&#xff09; &#x1f34e;简介 : 专注于前端开发&#xff0c;微信小程序&#xff0c;后台管理&#xff08;VueReact&#xff09; 本博客主要用于分享前端技术知识&#xff0c;更多内容请看下方&#x1f447; ✨人生态度 &#xff…

Android 简单的下拉选择框实现

要实现这种效果,目前知道的方法有以下两种,Spinner 和 ListPopupWindow,当然肯定还有很多别的方法,这里我们先尝试使用ListPopupWindow来实现这个效果; 以下是一个简单的demo: public class MainActivity extends AppCompatActivity {private List<String> dataList;pr…

【半夜学习MySQL】表结构的操作(含表的创建、修改、删除操作,及如何查看表结构)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 创建表查看表结构修改表删除表 创建表 语法&#xff1a; create table table_name(field1 datatype,field2 datatype,fiel…

hive日常使用时忘记部分补充(不定时)

1、date_formate、unix_timestamp、from_unixtime用法&#xff1a; 2、lag&#xff08;&#xff09;、lead()用法&#xff1a; lag&#xff08;)窗口函数返回分区中当前行之前行&#xff08;可以指定第几行&#xff09;的值。 如果没有行&#xff0c;则返回null。 lead()窗口…

dos命令改3389端口,通过dos命令更改3389端口的操作

要使用DOS命令更改3389端口&#xff0c;通常涉及修改Windows注册表中的相关键值。请注意&#xff0c;直接操作注册表具有一定的风险&#xff0c;因此在进行任何更改之前&#xff0c;请确保您了解正在进行的操作&#xff0c;并已经采取了适当的备份措施。 以下是一个基本的操作步…

2024年4月12日饿了么春招实习试题【第二题:魔法师】-题目+题解+在线评测【二分】

2024年4月12日饿了么春招实习试题【第二题:魔法师】-题目题解在线评测【二分】 题目描述&#xff1a;输入格式输出格式样例输入样例输出评测数据与规模 解题思路一&#xff1a;解题思路二&#xff1a;解题思路三&#xff1a;动态规划 题目描述&#xff1a; 塔子哥是一名魔法师…

浅谈运维数据安全

在数字化日益深入的今天&#xff0c;运维数据安全已经成为企业信息安全体系中的核心要素。运维工作涉及到企业信息系统的各个方面&#xff0c;从硬件维护到软件升级&#xff0c;从网络配置到数据备份&#xff0c;无一不需要严谨的数据安全保障措施。本文将从运维数据安全的重要…