1、RS232是UART的一种,没有时钟线,只有两根数据线,分别是rx和tx,这两根线都是1bit位宽的。其中rx是接受数据的线,tx是发送数据的线。
2、rx的位宽为1bit,PC机通过串口调试助手往FPGA发送8bit数据时,FPGA通过串口线rx一位一位的接收数据,从最低位到最高位依次接收,最后在FPGA里面位拼接成8bit数据。
3、tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx 线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照 RS232 协议把这一位一位的数据位拼接成 8bit 数据。
4、串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有 10bit。在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持 高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit 的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如下图所示 为一个最基本的 RS232 帧结构。
PS:Rx:一共是发送十位数据,但是第一位和第十位分别是0和1,中间八位任意,只需要对中间八位进行传输即可
5、波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进 行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元 的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、115200 等,我们选用 9600 的波特率进行串口的编写与仿真。
6、比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为 “每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 9600 的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。
7、由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟 频率下计数 5208 次
8、上位机通过串口发 8bit 数据时,会自动在发 8 位有效数据前发一个波特时间的起始位,也会自动在发完 8 位有效数据后发一个停止位。同理,串口助手接收上位机发送的数据前,必须检测到一个波特时间的起始位才能开始接收数据,接收完 8bit 的数据后,再接收一个波特时间的停止位。
可以看到,顶层的框图一共包含两个模块,分别是接收和发送模块。
接收模块的作用是将从PC输入的串行数据转换为并行数据 。
一帧数据一共有10bit,其中起始位和停止位分别为0和1,接收模块只接收中间八位数据,将中间8位的串行数据转化为并行,然后把8位的并行数据输入到发送模块。
接收模块的框图如下图所示:
接收模块波形图如下:
对波形图的一些理解: 接收的Rx信号需要同步到系统时钟sys_clk下,即单比特信号从低速时钟域同步到快速时钟域,通常使用的方法都是打两拍。但为何要同步三次呢?因为start_flag需要提取一个下降沿,而第一个同步的时钟是一个不稳定的信号,所以需要用第二个和第三个同步后的来提取下降沿。
根据波形图写出代码如下:
module uart_rx
#(
parameter uart_bps = 'd9600,
parameter clk_freq = 'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output reg [7:0] po_data ,
output reg po_flag
);
localparam boud_cnt_MAX = clk_freq/uart_bps ;
reg rx_reg1 ;
reg rx_reg2 ;
reg rx_reg3 ;
reg start_flag ;
reg work_en ;
reg [13:0] boud_cnt ;
reg bit_flag ;
reg [3:0] bit_cnt ;
reg [7:0] rx_data ;
reg rx_flag ;
//regesiter delay, two clock time
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_reg1<=1'b1;
else
rx_reg1<=rx;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_reg2<=1'b1;
else
rx_reg2<=rx_reg1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_reg3<=1'b1;
else
rx_reg3<=rx_reg2;
//start_flag
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
start_flag<=1'b0;
else if((~rx_reg2)&&(rx_reg3))
start_flag<=1'b1;
else
start_flag<=1'b0;
//work_en
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
work_en<=1'b0;
else if(bit_cnt==4'd8&&bit_flag==1'b1)
work_en<=1'b0;
else if(start_flag==1'b1)
work_en<=1'b1;
else
work_en<=work_en;
//boud_cnt
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
boud_cnt<=14'd0;
else if((boud_cnt==boud_cnt_MAX-1'b1)||(work_en==1'b0))
boud_cnt<=14'd0;
else if(work_en==1'b1)
boud_cnt<=boud_cnt+1'b1;
//bit_flag
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_flag<=1'b0;
else if(boud_cnt==boud_cnt_MAX/2-1'b1)
bit_flag<=1'b1;
else
bit_flag<=1'b0;
//bit_cnt
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_cnt<=1'b0;
else if(bit_cnt==4'd8&&bit_flag==1'b1)
bit_cnt<=1'b0;
else if(bit_flag==1'b1)
bit_cnt<=bit_cnt+1'b1;
else
bit_cnt<=bit_cnt;
//rx_data[7:0]
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_data<=8'b0;
else if((bit_cnt>=4'd1)&&(bit_cnt<=4'd8)&&(bit_flag==1'b1))
rx_data<={rx_reg3,rx_data[7:1]};
//rx_flag
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_flag<=1'b0;
else if(bit_flag==1'b1&&bit_cnt==4'd8)
rx_flag<=1'b1;
else
rx_flag<=1'b0;
//po_data
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
po_data<=8'd0;
else if(rx_flag==1'b1)
po_data<=rx_data;
//po_flag
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
po_flag<=1'b0;
else if(rx_flag==1'b1)
po_flag<=1'b1;
else
po_flag<=1'b0;
endmodule
接收模块仿真代码如下:
`timescale 1ns / 1ns
module tb_uart_rx();
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
wire [7:0] po_data ;
wire po_flag ;
initial
begin
sys_clk=1'b1;
sys_rst_n<=1'b0;
rx<=1;
#20
sys_rst_n<=1'b1;
end
always #10 sys_clk=~sys_clk;
initial
begin
#200
rx_bit(8'd0);
rx_bit(8'd1);
rx_bit(8'd2);
rx_bit(8'd3);
rx_bit(8'd4);
rx_bit(8'd5);
rx_bit(8'd6);
rx_bit(8'd7);
end
task rx_bit(
input [7:0] data
);
integer i;
for(i=0;i<10;i=i+1)
begin
case(i)
0:rx<=1'b0;
1:rx<=data[0];
2:rx<=data[1];
3:rx<=data[2];
4:rx<=data[3];
5:rx<=data[4];
6:rx<=data[5];
7:rx<=data[6];
8:rx<=data[7];
9:rx<=1'b1;
endcase
#(5208*20);
end
endtask
uart_rx inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.rx (rx) ,
.po_data (po_data) ,
.po_flag (po_flag)
);
endmodule
Vivado仿真波形图
整体波形如下
对仿真波形图的理解: 在空闲状态下,Rx保持高电平,当检测到低电平时,波特计数器开始计数,当波特计数器计数到最大值的时候,则开始传输数据,一个8bit的数据传输完成后,po_data才开始输出。
发送模块的作用的接收来自接收模块的八位并行数据,然后将这八位并行数据转换位串行数据,一位一位的发送出去 。
一帧共有10bit,第一位和最后一位分别位0和1,来自接收模块的八位数据在中间。
模块设计
我们将串口接收模块取名为 uart_tx,根据功能简介我们对整个设计要求有了大致的了解,其中设计的关键点是如何将串并行数据转化为串行数据并发送出去,也就是按照顺序将并行数据发送至 PC 机上。FPGA 发送的串行数据同样没有时钟,所以要和 PC 机接约定好使用相同的波特率,一个一个地发送比特,为了后面做串口的回环测试我们仍选择使用 9600bps的波特率
模块的Visio框图如下:
发送模块的波形图如下:
ps:注意发送同样是10bit的数据,当第一个bit_flag=1的时候开始发送起始位0,当bit_flag=9的时候发送停止位1,其余时间Tx保持空闲状态。
根据波形图可以写出如下代码:
module uart_tx
#(
parameter uart_bps = 'd9600,
parameter clk_freq = 'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data ,
input wire pi_data_flag ,
output reg tx
);
localparam boud_cnt_max=clk_freq/uart_bps;
reg work_en ;
reg [12:0] boud_cnt ;
reg bit_flag ;
reg [3:0] bit_cnt ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
work_en<=1'b0;
else if((bit_cnt==4'd9)&&(bit_flag==1'b1))
work_en<=1'b0;
else if(pi_data_flag==1'b1)
work_en<=1'b1;
else
work_en<=work_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
boud_cnt<=13'd0;
else if((boud_cnt==boud_cnt_max-1'b1)||work_en==1'b0)
boud_cnt<=13'd0;
else if(work_en==1'b1)
boud_cnt<=boud_cnt+1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_flag<=1'b0;
else if(boud_cnt==13'd1)
bit_flag<=1'b1;
else
bit_flag<=1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_cnt<=4'd0;
else if((bit_cnt==4'd9)&&(bit_flag==1'b1))
bit_cnt<=4'd0;
else if((bit_flag==1'b1)&&(work_en==1'b1))
bit_cnt<=bit_cnt+1'b1;
else
bit_cnt<=bit_cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
tx<=1'b1;
else if(bit_flag==1'b1)
case(bit_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
endmodule
发送模块的仿真代码如下:
`timescale 1ns / 1ns
module tb_uart_tx();
reg sys_clk ;
reg sys_rst_n ;
reg [7:0] pi_data ;
reg pi_data_flag;
wire tx ;
initial
begin
sys_clk=1'b1;
sys_rst_n<=1'b0;
#20
sys_rst_n<=1'b1;
end
always #10 sys_clk=~sys_clk;
initial
begin
pi_data<=8'b0;
pi_data_flag<=1'b0;
#200
//transport the data 0
pi_data<=8'd0;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 1
pi_data<=8'd1;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 2
pi_data<=8'd2;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 3
pi_data<=8'd3;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 4
pi_data<=8'd4;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 5
pi_data<=8'd5;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 6
pi_data<=8'd6;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
//transport the data 7
pi_data<=8'd7;
pi_data_flag<=1'b1;
#20
pi_data_flag<=1'b0;
#(5208*20*10);
end
uart_tx inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.pi_data (pi_data) ,
.pi_data_flag (pi_data_flag) ,
.tx (tx)
);
endmodule
Vivado 仿真波形如下:
顶层模块代码如下:
module top_rs232(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output wire tx
);
parameter uart_bps = 14'd9600;
parameter clk_freq = 26'd50_000_000;
wire [7:0] po_data;
wire po_flag;
uart_rx
#(
.uart_bps(uart_bps),
.clk_freq(clk_freq)
)
uart_rx_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.rx (rx) ,
.po_data (po_data) ,
.po_flag (po_flag)
);
uart_tx
#(
.uart_bps(uart_bps),
.clk_freq(clk_freq)
)
uart_tx_inst1
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_data (po_data),
.pi_data_flag (po_flag),
.tx (tx)
);
endmodule
仿真测试代码如下:
`timescale 1ns / 1ns
module tb_top_rs232();
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
wire tx ;
initial
begin
sys_clk=1'b1;
sys_rst_n<=1'b0;
rx<=1'b1;
#20
sys_rst_n<=1'b1;
end
always #10 sys_clk=~sys_clk;
initial
begin
#200
rx_byte();
end
task rx_byte();
integer j;
for(j=0;j<8;j=j+1)
rx_bit(j);
endtask
task rx_bit(
input [7:0]data
);
integer i;
for(i=0;i<10;i=i+1)
begin
case(i)
0:rx<=1'b0;
1:rx<=data[0];
2:rx<=data[1];
3:rx<=data[2];
4:rx<=data[3];
5:rx<=data[4];
6:rx<=data[5];
7:rx<=data[6];
8:rx<=data[7];
9:rx<=1'b1;
endcase
#(5208*20);
end
endtask
top_rs232 inst(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.rx (rx) ,
.tx (tx)
);
endmodule
Vivado仿真波形图如下:
文章浏览阅读3.2w次,点赞16次,收藏90次。对于这个问题我也是从网上找了很久,终于解决了这个问题。首先遇到这个问题,应该确认虚拟机能不能正常的上网,就需要ping 网关,如果能ping通说明能正常上网,不过首先要用命令route -n来查看自己的网关,如下图:第一行就是默认网关。现在用命令ping 192.168.1.1来看一下结果:然后可以看一下电脑上面百度的ip是多少可以在linux里面ping 这个IP,结果如下:..._linux桥接ping不通baidu
文章浏览阅读512次。小妹在这里已经卡了2-3天了,研究了很多人的文章,除了低版本api 17有成功外,其他的不是channel null 就是没反应 (channel null已解决)拜托各位大大,帮小妹一下,以下是我的程式跟 gradle, 我在这里卡好久又没有人可问(哭)![image](/img/bVcL0Qo)public class MainActivity extends AppCompatActivit..._android 权限申请弹窗 横屏
文章浏览阅读1.4k次,点赞4次,收藏6次。valid padding(有效填充):完全不使用填充。half/same padding(半填充/相同填充):保证输入和输出的feature map尺寸相同。full padding(全填充):在卷积操作过程中,每个像素在每个方向上被访问的次数相同。arbitrary padding(任意填充):人为设定填充。..._cnn “相同填充”(same padding)
文章浏览阅读790次,点赞29次,收藏28次。手绘了下图所示的kafka知识大纲流程图(xmind文件不能上传,导出图片展现),但都可提供源文件给每位爱学习的朋友一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长![外链图片转存中…(img-Qpoc4gOu-1712656009273)][外链图片转存中…(img-bSWbNeGN-1712656009274)]
文章浏览阅读469次。Date对象取得年份有getYear和getFullYear两种方法经 测试var d=new Date;alert(d.getYear())在IE中返回 2009,在Firefox中会返回109。经查询手册,getYear在Firefox下返回的是距1900年1月1日的年份,这是一个过时而不被推荐的方法。而alert(d.getFullYear())在IE和FF中都会返回2009。因此,无论何时都应使用getFullYear来替代getYear方法。例如:2016年用 getFullYea_getyear和getfullyear
文章浏览阅读182次。Unix传奇(上篇) 陈皓 了解过去,我们才能知其然,更知所以然。总结过去,我们才会知道我们明天该如何去规划,该如何去走。在时间的滚轮中,许许多的东西就像流星一样一闪而逝,而有些东西却能经受着时间的考验散发着经久的魅力,让人津津乐道,流传至今。要知道明天怎么去选择,怎么去做,不是盲目地跟从今天各种各样琳琅满目前沿技术,而应该是去 —— 认认真真地了解和回顾历史。 Unix是目前还在存活的操作系_unix传奇pdf
文章浏览阅读308次。哈希算法:将字符串映射为数字形式,十分巧妙,一般运用为进制数,进制据前人经验,一般为131,1331时重复率很低,由于字符串的数字和会很大,所以一般为了方便,一般定义为unsigned long long,爆掉时,即为对 2^64 取模,可以对于任意子序列的值进行映射为数字进而进行判断入门题目链接:AC代码:#include<bits/stdc++.h>using na..._ac算法 哈希
文章浏览阅读952次,点赞13次,收藏27次。由于觉得Qt的编辑界面比较丑,所以想用vs2022的编辑器写Qt加MySQL的项目。_在vs中 如何装qt5sqlmysql模块
文章浏览阅读1k次。选择题题目:下面的哪个调研内容属于经济环境调研?()题目:()的目的就是加强与客户的沟通,它是是网络媒体也是网络营销的最重要特性。题目:4Ps策略中4P是指产品、价格、顾客和促销。题目:网络市场调研是目前最为先进的市场调研手段,没有任何的缺点或不足之处。题目:市场定位的基本参数有题目:市场需求调研可以掌握()等信息。题目:在开展企业网站建设时应做好以下哪几个工作。()题目:对企业网站首页的优化中,一定要注意下面哪几个方面的优化。()题目:()的主要作用是增进顾客关系,提供顾客服务,提升企业_画中画广告之所以能有较高的点击率,主要由于它具有以下特点
文章浏览阅读1k次,点赞2次,收藏5次。以爬取CSDN为例子:第一步:导入请求库第二步:打开请求网址第三步:打印源码import urllib.requestresponse=urllib.request.urlopen("https://www.csdn.net/?spm=1011.2124.3001.5359")print(response.read().decode('utf-8'))结果大概就是这个样子:好的,继续,看看打印的是什么类型的:import urllib.requestresponse=urllib.r_urlopen the read operation timed out
文章浏览阅读304次。修正sina.com/sina.cn邮箱获取不到联系人,并精简修改了其他邮箱代码,以下就是升级版版本的介绍:完整版本,整合了包括读取邮箱通讯录、MSN好友列表的的功能,目前读取邮箱通讯录支持如下邮箱:gmail(Y)、hotmail(Y)、 live(Y)、tom(Y)、yahoo(Y)(有点慢)、 sina(Y)、163(Y)、126(Y)、yeah(Y)、sohu(Y) 读取后可以发送邮件(完..._通讯录 应用读取 邮件 的相关
文章浏览阅读213次。云计算及虚拟化教程学习云计算、虚拟化和计算机网络的基本概念。此视频教程共2.0小时,中英双语字幕,画质清晰无水印,源码附件全课程英文名:Cloud Computing and Virtualization An Introduction百度网盘地址:https://pan.baidu.com/s/1lrak60XOGEqMOI6lXYf6TQ?pwd=ns0j课程介绍:https://www.aihorizon.cn/72云计算:概念、定义、云类型和服务部署模型。虚拟化的概念使用 Type-2 Hyperv_云计算与虚拟化技术 教改