jjzjj

FPGA实现串口通信(RS232)含代码

fanyuandrj 2024-02-07 原文

硬件需求

带有CH340的FPAG开发板

接收模块

该模块的功能是接收通过 PC 机上的串口调试助手发送的固定波特率的数据,串口接收模块按照串口的协议准确接收串行数据,解析提取有用数据后需将其转化为并行数据;简单的说,接收模块的功能就是解析+串转并
具体实现步骤如下:
1、算出波特率和FPGA时钟的对应关系
每个码元的持续时间=FPGA时钟计数Fclk/Baud次
例如波特率为9600,代表着每秒传输9600个码元,每个码元的持续时间为1/9600秒,设FPGA时钟为50MHz,则需要计数约5028次(细微的近似计数差别不会产生数据错误)。
2、产生读取数据标志
在1的例子中,每个码元都持续了5028个时钟周期,那我们该什么时候取数据呢?理论上讲,在中间位置时取数最稳定,所以我们选择当计数器计数到 2603时取数据。
3、串并转换
串口发送的每一帧包含了10bit数据,其中第一个码元是开始位,最后一个码元是停止位,第2位到第9位的数据为有效数据。我们取出中间的8位有效数据转换为并行数据即可。
4、串口状态的确定
我们可以设置一个使能信号。当串口处于空闲状态时,数据线为高电平,当起始位到来时变为低电平,我们只要找到第一个下降沿就认为串口开始进行数据传输。此时拉高使能信号,等待10bit数据传输完成,再将使能信号拉低,等待下一个下降沿的到来。因此可以通过设置使能信号来确定串口状态,当使能信号为高时串口在传输数据,使能信号为低时串口处于空闲状态。

module usart232_rx(
input wire sys_clk,
input wire sys_rst,
input wire data_in_1bit,
output reg [7:0] po_data,
output reg po_flag
);
parameter BOUND_CNT_MAX = 13'd5208;//波特率9600,时钟频率50MHz
reg  reg1;
reg  reg2;
reg  reg3;
reg [7:0]data_out;
reg [12:0] bound_cnt;//波特率计数器
reg start_flag;//开始标志(下降沿标志)
reg enable;//使能信号
reg bit_flag;//读取数据标志
reg [3:0] bit_cnt;//用于计数该时刻接收的数据是第几个比特的计数器
reg data_out_flag;//串并转换完成标志
//时钟同步数据
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
reg1<=1;
else
reg1<=data_in_1bit;
//打一拍,减少出现亚稳态的概率
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
reg2<=1;
else
reg2<=reg1;
//再打一拍,再次减少出现亚稳态的概率
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
reg3<=1;
else
reg3<=reg2;

//时钟计数
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
bound_cnt<=0;
else if (bound_cnt==BOUND_CNT_MAX-1'b1||enable==0)
bound_cnt<=0;
else if(enable ==1)
bound_cnt<=bound_cnt+1'b1;
else 
bound_cnt<=0;

//开始标志,提取下降沿
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
start_flag<=0;
else if(reg3==1&&reg2==0)
start_flag<=1;
else
start_flag<=0;


//串行数据采集
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
bit_flag<=0;
else if(bound_cnt==(BOUND_CNT_MAX/2-1'b1))
bit_flag<=1;
else
bit_flag<=0;

//采样计数
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
bit_cnt<=0;
else if(bit_flag==1&&bit_cnt==4'd8)
bit_cnt<=0;
else if (bit_flag==1)
bit_cnt<=bit_cnt+1'b1;


//使能信号
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
enable<=0;
else if(start_flag==1)
enable<=1;
else if(bit_cnt==4'd8&&bit_flag==1)
enable<=0;

//串并转换
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
data_out<=0;
else if(bit_cnt>=4'b1&&bit_cnt<=4'd8&&bit_flag==1'b1)
data_out<={reg3,data_out[7:1]};

//数据转换完成标志
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
data_out_flag<=0;
else if(bit_cnt==4'd8&&bit_flag==1'b1)
data_out_flag<=1;
else 
data_out_flag<=0;

//输出数据
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
po_data<=0;
else if(data_out_flag==1)
po_data<=data_out;

//输出数据有效标志
always@(posedge sys_clk or negedge sys_rst)
if (sys_rst==0)
po_flag<=0;
else if(data_out_flag==1)
po_flag<=1;
else
po_flag<=0;

endmodule

发送模块

该模块的功能是将 FPGA 中的数据以固定的波特率发送到 PC 机的串口调试助手,串口发送模块按照串口的协议组装成帧,然后按照顺序一个比特一个比特将数据发送至 PC 机,需将其转化为串行数据发送。即并转串+按一定的波特率发送
1、波特率与FPGA时钟周期
与接收模块相对应的,每5028个时钟周期发送一次数据。
2、数据格式
按照串口通信协议,需要在数据前加一个持续5028个时钟周期的低电平,在数据结尾加一个持续5028个时钟周期的高电平。数据位为8位,每一位的持续时间为5028个时钟周期。
3、发送时机
我们按照 5208 个系统时钟周期的波特率间隔来发送 1bit 数据。那应该在什么位置开发送呢?理论上我们在第一个 5208 系统时钟周期内的任意一个位置发送数据都可以,但是尽量不要选择计数器的计数值为 0 或 5207 这种端点,因为容易出问题。
4、串口状态控制
同样产生一个使能信号,在使能信号为高时,进行数据的发送,在使能信号为低时,将串口的电平拉高,恢复空闲状态。

module usart232_tx(
input wire sys_clk,
input wire sys_rst,
input wire [7:0] pi_data,
input wire pi_flag,
output reg tx
);
parameter Baund_cnt_max=13'd5208;
reg [12:0] baund_cnt;
reg enable;
reg send_flag;
reg [3:0] send_cnt;
//波特计数
always@(posedge sys_clk or negedge sys_rst)
if(!sys_rst)
baund_cnt<=0;
else if(baund_cnt==Baund_cnt_max-1'b1||enable==1'b0)
baund_cnt<=0;
else
baund_cnt<=baund_cnt+1'b1;
//发送标志
always@(posedge sys_clk or negedge sys_rst)
if(!sys_rst)
send_flag<=0;
else if(baund_cnt==1'b1&&enable)//
send_flag<=1'b1;
else
send_flag<=0;
//发送数据计数
always@(posedge sys_clk or negedge sys_rst)
if(!sys_rst)
send_cnt<=0;
else if(send_cnt==4'd9&&send_flag||enable==1'b0)
send_cnt<=0;
else if(send_flag)
send_cnt<=send_cnt+1'b1;
//使能信号
always@(posedge sys_clk or negedge sys_rst)
if(!sys_rst)
enable<=0;
else if(pi_flag)
enable<=1;
else if(send_cnt==4'd9&&send_flag)
enable<=0;
//并转串+数据发送
always@(posedge sys_clk or negedge sys_rst)
if(!sys_rst)
tx<=1'b1;
else if(send_flag)
begin
case (send_cnt)
0 : tx <= 1'b0;
1 : tx <= pi_data[0];
2 : tx <= pi_data[1];
3 : tx <= pi_data[2];
4 : tx <= pi_data[3];
5 : tx <= pi_data[4];
6 : tx <= pi_data[5];
7 : tx <= pi_data[6];
8 : tx <= pi_data[7];
9 : tx <= 1'b1;
default : tx <= 1'b1;
endcase
end
endmodule

顶层模块

顶层模块将发送和接收连接起来进行回环测试。上位机通过串口发送数据给FPGA,FPGA接收后通过串口将数据再发回上位机。

module top_usart(
input wire sys_clk,
input wire sys_rst,
input wire rx,
output wire tx
);
wire [7:0] po_data;
wire po_flag;

usart232_rx usart232_rx_inst(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.data_in_1bit(rx),
.po_data(po_data),
.po_flag(po_flag)
);

usart232_tx usart232_tx_inst(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.pi_data(po_data),
.pi_flag(po_flag),
.tx(tx)
);
endmodule

仿真结果


其中rxx是上位机发送给FPGA的数据,tx是FPGA发送给上位机的数据,可以看到经过一段时间的延迟后,数据被原封不动的发回了上位机。

测试结果


通过串口调试助手可以看到,发送给FPGA的数据被FPGA成功的回传了。

有关FPGA实现串口通信(RS232)含代码的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  6. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  10. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

随机推荐