FPGA串口调试文档格式.docx
- 文档编号:20215596
- 上传时间:2023-01-20
- 格式:DOCX
- 页数:12
- 大小:102.46KB
FPGA串口调试文档格式.docx
《FPGA串口调试文档格式.docx》由会员分享,可在线阅读,更多相关《FPGA串口调试文档格式.docx(12页珍藏版)》请在冰豆网上搜索。
其中,数据线上没有数据传输的时候是保持高电平,而第一个低电平的出现就是起始位。
当发送数据和接收数据的时候,按以上格式进行就可以了。
不知道大家会不会有这样一个问题,经常听说串口传输的速度是多少多少,看了这个数据帧格式,没发现速度是怎么控制的啊。
对的,这儿就引出了波特率的问题。
波特率时钟并没有表现在传输线上,它其实是用来指示我们每采样或发送一个数据位的速度的。
比如说,我们定义一个波特率时钟为96k,那么我们用这个时钟把数据串行一个一个打出去,接收端只要匹配一个相同的时钟,一个一个数据位接受进来,那数据传输就可以了!
2.代码这部分的代码相对于前面的就稍稍有点多了,我们来一点一点分析,由于这次用到了模块调用,这次上代码,我们把每个文件名都写上。
首先我们来看下这个工程的树状代码:
33.jpg(9.46KB)2011-5-1014:
02通过这个树状图,大家应该就对这个工程有一定了解了,主模块是uart,它包含四个子模块,分别是接收模块rx、接收模块的波特率时钟rx_clk、发送模块tx、发送模块的波特率时钟tx_clk。
1、主模块:
uart.v复制内容到剪贴板代码:
moduleuart(clk,rst_n,rxd,txd);
inputclk;
/输入时钟,50Minputrst_n;
inputrxd;
/串口输入rxoutputtxd;
/串口输出txwirebps_start1,bps_start2;
/rx、tx的波特率启动信号wirebps_clk1,bps_clk2;
/rx、tx的波特率时钟wirerx_done;
/数据接收完毕信号,有rx输出到tx,rx接收数据由tx发送回去wire7:
0data;
/数据寄存器bps_generaterx_clk(.clk(clk),/50M.rst_n(rst_n),.bps_start(bps_start1),/拉高,产生波特率时钟.bps_clk(bps_clk1);
rxrx(.clk(clk),.rst_n(rst_n),.rxd(rxd),.bps_clk(bps_clk1),.bps_start(bps_start1),.rx_done(rx_done),.data(data);
bps_generatetx_clk(.clk(clk),/50M.rst_n(rst_n),.bps_start(bps_start2),/拉高,产生波特率时钟.bps_clk(bps_clk2);
txtx(.clk(clk),.rst_n(rst_n),.bps_clk(bps_clk2),.bps_start(bps_start2),.data(data),.rx_done(rx_done),.txd(txd);
endmodule通常,我们在顶层模块里不会写具体操作的代码,主模块的作用是将各个模块连接在一起。
这样有利于代码的维护!
我们看到主模块对外的输入输出引脚只有时钟clk,复位rst_n,以及数据输入线rx和输出线tx。
往下看,我们看到一堆wire的定义,这个很重要。
由于input和output的端口Quartus软件会自动配置成wire型,所以这些端口不用再wire定义。
而其他剩下的端口,如果不“wire”一下,那么下面的各模块例化相互的端口就不会连接在一起。
接下来终于要说到模块例化了,所谓“例化”,是从英文的instantiate翻译过来的。
这里我说个不太严谨的说法:
就是把另一个模块添加在这个模块当中,更不严谨的,也可以说是调用吧。
代码:
bps_generaterx_clk(.clk(clk),/50M.rst_n(rst_n),.bps_start(bps_start1),/拉高,产生波特率时钟.bps_clk(bps_clk1);
我们引用波特率时钟模块来说明例化的格式吧。
首先,bps_generate是原来模块的模块名,后面加一个或几个空格,紧跟着rx_clk是在这个模块下(也就是uart)的模块名。
通常,我们会把两个命名一样,或者后面的命名为:
i1、i2,意思是模块1,2这个可以随意。
接下来在();
里添加这个模块的输入输出引脚,其中.clk()是原来模块的名称,而括号里则是在当前模块下的名称。
在当前模块里,两个名称一样,再“wire”一下,那这个信号就连接在一起了。
这儿,你可以直接给这个接口赋一个数,例如.data(16h11),这样,也是可以的。
2、波特率产生模块:
bps_generate.v代码:
modulebps_generate(clk,rst_n,bps_start,bps_clk);
/50Minputrst_n;
inputbps_start;
/高电平,产生波特率时钟outputbps_clk;
parameterCNT_NUM=434;
/波特率为50M/434=115200parameterCNT_NUM_2=216;
/计数值的一半,产生一个高电平reg15:
0cnt;
always(posedgeclkornegedgerst_n)beginif(!
rst_n)cnt=16b0;
elseif(cnt=CNT_NUM)cnt=16b0;
elseif(bps_start)cnt=cnt+1b1;
elsecnt=16b0;
endregbps_clk_r;
rst_n)bps_clk_r=1b0;
elseif(cnt=CNT_NUM_2)bps_clk_r=1b1;
elsebps_clk_r=1b0;
endassignbps_clk=bps_clk_r;
endmodule这里,我们首先用到了parameter,因为我们常通过修改计数值来改波特率时钟,所以,用parameter是很方便的!
接下来就是计数的操作,我想不用介绍太多。
最后产生的波特率时钟差不多是这样的,我们利用每个高电平来采集、发送数据。
44.jpg(3.81KB)2011-5-1014:
02这里,还要讲到一个模块重复调用的问题。
由于接收和发送的波特率时钟是一样的,我们在顶层模块uart调用的时候都是调用了这个代码文件,但分别命名为rx_clk和tx_clk,Quartus软件在综合的时候就会综合成两个电路。
不同于软件程序的调用,大家一定要引起重视!
3、接收模块:
rx.v代码:
modulerx(clk,rst_n,bps_clk,rxd,bps_start,rx_done,data);
inputrst_n;
inputbps_clk;
outputbps_start;
outputrx_done;
output7:
regreg_rxd0;
rst_n)reg_rxd0=1b1;
elsereg_rxd0=rxd;
endregreg_rxd1,reg_rxd2;
rst_n)beginreg_rxd1=1b1;
reg_rxd2=1b1;
endelsebeginreg_rxd1=reg_rxd0;
reg_rxd2=reg_rxd1;
endendwirereg_negedge=reg_rxd2&
(reg_rxd1);
/下降沿检测regbps_start_r;
regrx_done_r;
rst_n)beginbps_start_r=1b0;
rx_done_r=1b0;
endelseif(reg_negedge)bps_start_r=1b1;
/检测到起始位,打开波特率时钟elseif(state=4d9)rx_done_r=1b1;
/数据接收完成,启动一次数据传输elseif(state=4d10)beginbps_start_r=1b0;
/一帧数据传输完毕,关闭波特率时钟rx_done_r=1b0;
/标志位关闭,避免重复传输endendassignbps_start=bps_start_r;
assignrx_done=rx_done_r;
/数据传输标志位,拉高,表明rx接收一帧数据完成,tx发送一次该组数据reg3:
0state;
rst_n)state=4b0;
elseif(state=4d10)state=4b0;
/一帧数据传输完毕,回到初始状态elseif(bps_clk)/波特率每个高电平进行状态转移begincase(state)4d0:
state=4d1;
4d1:
state=4d2;
4d2:
state=4d3;
4d3:
state=4d4;
4d4:
state=4d5;
4d5:
state=4d6;
4d6:
state=4d7;
4d7:
state=4d8;
4d8:
state=4d9;
4d9:
state=4d10;
4d10:
state=4b0;
default:
endcaseendendreg7:
0data_temp;
rst_n)begindata_temp=8b0;
endelseif(bps_clk)begincase(state)4d1:
data_temp0=rxd;
data_temp1=rxd;
data_temp2=rxd;
data_temp3=rxd;
data_temp4=rxd;
data_temp5=rxd;
data_temp6=rxd;
data_temp7=rxd;
/逐位存入数据endcaseendendassigndata=data_temp;
endmodule在这个代码文件中,蓝色部分是用来下降沿检测的,前面已经介绍过。
红色部分,是一个简单的状态机。
FPGA内部相当于硬件电路,都是并行执行,但是,有些逻辑却是有一定顺序的,这时候,我们就需要使用状态机来完成顺序执行。
大家发现,红色部分第一个always模块的case结构,状态state是随着波特率的高电平elseif(bps_clk)一个一个转移的。
而在第二个always模块里,利用每一个状态执行一次数据读入的操作!
4、数据发送模块:
tx.v代码:
moduletx(inputclk,inputrst_n,inputbps_clk,inputrx_done,input7:
0data,outputbps_start,outputtxd);
regbps_start_r;
rst_n)bps_start_r=1b0;
elseif(rx_done)bps_start_r=1b1;
elseif(state=4d11)bps_start_r=1b0;
endassignbps_start=bps_start_r;
reg7:
0tx_data;
rst_n)tx_data=8b0;
elseif(rx_done)tx_data=data;
endreg3:
elseif(bps_clk)begincase(state)4d0:
state=4d11;
/4d10:
4d11:
endcaseendendregtxd_r;
rst_n)txd_r=1b1;
elseif(bps_clk)begincase(state)4d1:
txd_r=1b0;
txd_r=tx_data0;
txd_r=tx_data1;
txd_r=tx_data2;
txd_r=tx_data3;
txd_r=tx_data4;
txd_r=tx_data5;
txd_r=tx_data6;
txd_r=tx_data7;
txd_r=1b1;
/crc4d11:
/stopendcaseendendassigntxd=txd_r;
endmodule发送模块和接收模块是非常相似的,这里就不重复介绍了。
3.调试前准备对于我们这个串口调试:
1、我们需要准备一个USB数据线,是标准的一边小头,一边大头的那种,我想,很多MP3,手机都是采用这种数据线的,很好找。
2、安装串口转USB的驱动程序。
3、安装一个串口调试助手。
这些,我们都将在帖子里附件打包。
4.上电调试分配好管脚,下载代码,打开串口大师如下图:
当上面的窗口能显示出下面窗口我们发送的内容,就算是调试成功了O(_)O!
55.jpg(38.68KB)2011-5-1014:
025.总结这次实验,我们学习了简单的串口RS232的通信时序和逻辑设计。
也是我们第一次用到了模块调用,第一次设计了一个相对长一些的代码。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- FPGA 串口 调试
![提示](https://static.bdocx.com/images/bang_tan.gif)