verilog串口通信程序Word文件下载.docx

上传人:b****3 文档编号:16860525 上传时间:2022-11-26 格式:DOCX 页数:22 大小:97.93KB
下载 相关 举报
verilog串口通信程序Word文件下载.docx_第1页
第1页 / 共22页
verilog串口通信程序Word文件下载.docx_第2页
第2页 / 共22页
verilog串口通信程序Word文件下载.docx_第3页
第3页 / 共22页
verilog串口通信程序Word文件下载.docx_第4页
第4页 / 共22页
verilog串口通信程序Word文件下载.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

verilog串口通信程序Word文件下载.docx

《verilog串口通信程序Word文件下载.docx》由会员分享,可在线阅读,更多相关《verilog串口通信程序Word文件下载.docx(22页珍藏版)》请在冰豆网上搜索。

verilog串口通信程序Word文件下载.docx

initial 

begin

TxD_start=0;

TxD_data=0;

clk=0;

rst=1;

#54rst=0;

#70rst=1;

#40TxD_start=1'

b1;

#10TxD_data=8'

b;

#100TxD_start=1'

b0;

end 

alwaysbegin

#30clk=~clk;

#10clk=~clk;

end

endmodule

二、综合

三、

FPGA与PC串口自收发通信

串口通信其实简单实用,这里我就不多说,只把自己动手写的verilog代码共享下。

实现的功能如题,就是FPGA里实现从PC接收数据,然后把接收到的数据发回去。

使用的是串口UART协议进行收发数据。

上位机用的是老得掉牙的串口调试助手,如下:

发送数据的波特率可选9600bps,19200bps,38400bps,57600bps,115200bps等,是可调的。

发送格式为:

1bit起始位,8bit数据,1bit停止位,无校验位。

以下的代码有比较详细的注释,经过下载验证,存在误码率(<

5%),仅供学习!

代码如下:

(顶层模块):

modulemy_uart_top(clk,rst_n,rs232_rx,rs232_tx);

inputclk;

lk(clk),st_n(rst_n),

.bps_start(bps_start),

.clk_bps(clk_bps)

);

my_uart_rxmy_uart_rx(.clk(clk),st_n(rst_n),

.rs232_rx(rs232_rx),

.clk_bps(clk_bps),

.rx_data(rx_data),

.rx_int(rx_int)

my_uart_txmy_uart_tx(.clk(clk),st_n(rst_n),

.rx_int(rx_int),

.rs232_tx(rs232_tx),

.bps_start(bps_start)

modulespeed_select(clk,rst_n,bps_start,clk_bps);

允许全双工的双向通讯(也就是说计算机可以在接收数据的同时发送数据).

最大可支持的传输速率为10KBytes/s.

DB-9插头

你可能已经在你的计算机背后见到过这种插头

它一共有9个引脚,但是最重要的3个引脚是:

引脚2:

RxD(接收数据).

引脚3:

TxD(发送数据).

引脚5:

GND(地).

仅使用3跟电缆,你就可以发送和接收数据.

串行通讯

数据以每次一位的方式传输;

每条线用来传输一个方向的数据。

由于计算机通常至少需要若干位数据,因此数据在发送之前先“串行化”。

通常是以8位数据为1组的。

先发送最低有效位,最后发送最高有效位。

异步通讯

RS-232使用异步通讯协议。

也就是说数据的传输没有时钟信号。

接收端必须有某种方式,使之与接收数据同步。

对于RS-232来说,是这样处理的:

1.串行线缆的两端事先约定好串行传输的参数(传输速度、传输格式等)

2.当没有数据传输的时候,发送端向数据线上发送"

1"

3.每传输一个字节之前,发送端先发送一个"

0"

来表示传输已经开始。

这样接收端便可以知道有数据到来了。

4.开始传输后,数据以约定的速度和格式传输,所以接收端可以与之同步

5.每次传输完成一个字节之后,都在其后发送一个停止位("

让我们来看看0x55是如何传输的:

0x55的二进制表示为:

01010101。

但是由于先发送的是最低有效位,所以发送序列是这样的:

-0.

下面是另外一个例子:

传输的数据为0xC4,你能看出来吗?

从图中很难看出来所传输的数据,这也说明了事先知道传输的速率对于接收端有多么重要。

数据传输可以多快?

数据的传输速度是用波特来描述的,亦即每秒钟传输的数据位,例如1000波特表示每秒钟传输100比特的数据,或者说每个数据位持续1毫秒。

波特率不是随意的,必须服从一定的标准,如果希望设计123456波特的RS-232接口,对不起,你很不幸运,这是不行的。

常用的串行传输速率值包括以下几种:

1200波特.

9600波特.

38400波特.

115200波特(通常情况下是你可以使用的最高速度).

在115200波特传输速度下,每位数据持续(1/115200)=μs.如果传输8位数据,共持续8xμs=69μs。

但是每个字节的传输又要求额外的“开始位”和“停止位”,所以实际上需要花费10xμs=87μs的时间。

最大的有效数据传输率只能达到每秒。

在115200波特传输速度下,一些使用了不好的芯片的计算机要求一个长的停止位或2位数据的长度),这使得最大传输速度降到大约每秒

物理层

电缆上的信号使用正负电压的机制:

"

用-10V的电压表示(或者在-5V与-15V之间的电压).

用+10V的电压表示(或者在5V与15V之间的电压).

所以没有数据传输的电缆上的电压应该为-10V或-5到-10之间的某个电压。

FPGA实现串行接口RS232

(2)

2008-12-1711:

39

波特率发生器

这里我们使用串行连接的最大速度115200波特,其他较慢的波特也很容易由此产生。

FPGA通常运行在远高于115200Hz的时钟频率上(对于今天的标准的来说RS-232真是太慢了),这就意味着我们需要用一个较高的时钟来分频产生尽量接近于115200Hz的时钟信号。

从的时钟产生

通常RS-232芯片使用的时钟,以为这个时钟很容易产生标准的波特率,所以我们假设已经拥有了一个这样的时钟源。

只需要将16分频便可得到115200Hz的时钟,多方便啊!

reg[3:

0]BaudDivCnt;

always@(posedgeclk)BaudDivCnt<

=BaudDivCnt+1;

wireBaudTick=(BaudDivCnt==15);

所以"

BaudTick"

每16个时钟周期需要置位一次,从而从的时钟得到115200Hz的时钟。

从任意频率产生

早期的发生器假设使用的时钟。

但如果我们使用2MHz的时钟怎么办呢要从2MHz的时钟得到115200Hz,需要将时钟"

..."

分频,并不是一个整数。

我的解决办法是有时候17分频,有时候18分频,使得整体的分频比保持在"

"

这是很容易做到的。

下面是实现这个想法的C语言代码:

while

(1)."

个时钟间隔打印出一个"

*"

为了从FPGA得到同样的效果,考虑到串行接口可以容忍一定的波特率误差,所以即使我们使用或者这样的分频比也是没有关系的。

FPGA波特率发生器

我们希望2000000是2的整数幂,但很可惜,它不是。

所以我们改变分频比,"

2000000/115200"

约等于"

1024/59"

=.这跟我们要求的分频比很接近,并且使得在FPGA上实现起来相当有效。

当有数传输的时候,使"

busy"

信号有效,此时“TxD_start”信号被忽略.

RS-232模块的参数是固定的:

8位数据,2个停止位,无奇偶校验.

数据串行化

假设我们已经有了一个115200波特的"

信号.

我们需要产生开始位、8位数据以及停止位。

用状态机来实现看起来比较合适。

0]state;

always@(posedgeclk)

case(state)

4'

b0000:

if(TxD_start)state<

=4'

b0100;

b0100:

if(BaudTick)state<

b1000;

现在,我们只需要产生"

TxD"

输出即可.

regmuxbit;

always@(state[2:

0])

case(state[2:

0:

muxbit<

=TxD_data[0];

1:

=TxD_data[1];

2:

=TxD_data[2];

3:

=TxD_data[3];

4:

=TxD_data[4];

5:

=TxD_data[5];

6:

=TxD_data[6];

7:

=TxD_data[7];

endcase

RxD线上有数据时,接收模块负责识别RxD线上的数据

2.当收到一个字节的数据时,锁存接收到的数据到"

data"

总线,并使"

data_ready"

有效一个周期。

注意:

只有当"

有效时,"

总线的数据才有效,其他的时间里不要使用"

总线上的数据,因为新的数据可能已经改变了其中的部分数据。

过采样

异步接收机必须通过一定的机制与接收到的输入信号同步(接收端没有办法得到发送断的时钟)。

这里采用如下办法。

1.为了确定新数据的到来,即检测开始位,我们使用几倍于波特率的采样时钟对接收到的信号进行采样。

2.一旦检测到"

开始位"

,再将采样时钟频率降为已知的发送端的波特率。

典型的过采样时钟频率为接收到的信号的波特率的16倍,这里我们使用8倍的采样时钟。

当波特率为115200时,采样时钟为921600Hz。

假设我们已经有了一个8倍于波特率的时钟信号"

Baud8Tick"

,其频率为921600Hz。

具体设计

首先,接受到的"

RxD"

信号与我们的时钟没有任何关系,所以采用两个D触发器对其进行过采样,并且使之我我们的时钟同步。

reg[1:

0]RxD_sync;

always@(posedgeclk)if(Baud8Tick)RxD_sync<

={RxD_sync[0],RxD};

首先我们对接收到的数据进行滤波,这样可以防止毛刺信号被误认为是开始信号。

0]RxD_cnt;

regRxD_bit;

if(Baud8Tick)

if(RxD_sync[1]&

&

RxD_cnt!

=2'

b11)RxD_cnt<

=RxD_cnt+1;

else

if(~RxD_sync[1]&

b00)RxD_cnt<

=RxD_cnt-1;

if(RxD_cnt==2'

b00)RxD_bit<

=0;

b11)RxD_bit<

=1;

end

一旦检测到"

,使用如下的状态机可以检测出接收到每一位数据。

if(~RxD_bit)state<

将FPGA的8个引脚作为输出(称为“通用输出”)。

FPGA收到任何数据时都会更新这8个GPout的值。

2.将FPGA的8个引脚作为输入(称为“通用输入”)。

FPGA收到仁厚数据后,都会将GPin上的数值通过串行口发送出去。

通用输出可以用来通过计算机远程控制任何东西,例如FPGA板上的LED,甚至可以再添加一个继电器来控制咖啡机。

moduleserialfun(clk,RxD,TxD,GPout,GPin);

inputRxD;

outputTxD;

output[7:

0]GPout;

input[7:

0]GPin;

lk(clk),.RxD(RxD),.RxD_data_ready(RxD_data_ready),.RxD_data(RxD_data));

reg[7:

always@(posedgeclk)if(RxD_data_ready)GPout<

=RxD_data;

lk(clk),.TxD(TxD),.TxD_start(RxD_data_ready),.TxD_data(GPin));

记得包含异步发送和接收模块的设计文件,并更新里面的时钟频率。

代码.zip

moduleserial(clk,rst,rxd,txd,en,seg_data,key_input,lowbit);

inputclk,rst;

inputrxd;

//串行数据接收端

inputkey_input;

//按键输入

0]en;

output[7:

0]seg_data;

reg[7:

outputtxd;

//串行数据发送端

outputlowbit;

////////////////////innerreg////////////////////

reg[15:

0]div_reg;

//分频计数器,分频值由波特率决定。

分频后得到频率8倍波特率的时钟

reg[2:

0] 

div8_tras_reg;

//该寄存器的计数值对应发送时当前位于的时隙数

div8_rec_reg;

//该寄存器的计数值对应接收时当前位于的时隙数

reg[3:

0]state_tras;

//发送状态寄存器

0]state_rec;

//接受状态寄存器

regclkbaud_tras;

//以波特率为频率的发送使能信号

regclkbaud_rec;

//以波特率为频率的接受使能信号

regclkbaud8x;

//以8倍波特率为频率的时钟,它的作用是将发送或接受一个bit的时钟周期分为8个时隙

regrecstart;

//开始发送标志

regrecstart_tmp;

regtrasstart;

//开始接受标志

regrxd_reg1;

//接收寄存器1

regrxd_reg2;

//接收寄存器2,因为接收数据为异步信号,故用两级缓存

regtxd_reg;

//发送寄存器

0]rxd_buf;

//接受数据缓存

0]txd_buf;

//发送数据缓存

0]send_state;

//每次按键给PC发送"

Welcome"

字符串,这是发送状态寄存器

reg[19:

0]cnt_delay;

//延时去抖计数器

regstart_delaycnt;

//开始延时计数标志

regkey_entry1,key_entry2;

//确定有键按下标志

////////////////////////////////////////////////

parameterdiv_par=16'

h104;

//分频参数,其值由对应的波特率计算而得,按此参数分频的时钟频率是波倍特率的8 

//倍,此处值对应9600的波特率,即分频出的时钟频率是9600*8

assigntxd=txd_reg;

assignlowbit=0;

assignen=8'

//7段数码管使能信号赋值

always@(posedgeclk)

if(!

rst)begin

cnt_delay<

=0;

start_delaycnt<

elseif(start_delaycnt)begin

if(cnt_delay!

=20'

d800000)begin

=cnt_delay+1;

elsebegin

key_input&

cnt_delay==0)

=1;

always@(posedgeclk)

rst)

key_entry1<

if(key_entry2)

elseif(cnt_delay==20'

key_input)

rst)

div_reg<

if(div_reg==div_par-1)

else

=div_reg+1;

always@(posedgeclk)//分频得到8倍波特率的时钟

clkbaud8x<

elseif(div_reg==div_par-1)

=~clkbaud8x;

always@(posedgeclkbaud8xornegedgerst)

div8_rec_reg<

elseif(recstart)//接收开始标志

=div8_rec_reg+1;

//接收开始后,时隙数在8倍波特率的时钟下加1循环

div8_tras_reg<

elseif(trasstart)

=div8_tras_reg+1;

//发送开始后,时隙数在8倍波特率的时钟下加1循环

always@(div8_rec_reg)

if(div8_rec_reg==7)

clkbaud_rec=1;

//在第7个时隙,接收使能信号有效,将数据打入

clkbaud_rec=0;

always@(div8_tras_reg)

if(div8_tras_reg==7)

clkbaud_tras=1;

//在第7个时隙,发送使能信号有效,将数据发出

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 材料科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1