打印

串口RS232通信程序

[复制链接]
3145|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 lovelyegle 于 2009-12-16 23:26 编辑

串口有9个管脚,其中只有三个是最重要的,分别是


pin 2: RxD (receive data).接收数据

pin 3: TxD (transmit data).发送数据

pin 5: GND (ground).     

串行通信时序


我们先来看看字节0x55的发送

0x55的二进制代码是01010101,但发送时由低字节开始的,因此发送次序依次为1-0-1-0-1-0-1-0.



串行通信电平

·
"1" is sent using -10V (or between -5V and -15V).

·
"0" is sent using +10V (or between 5V and 15V).


由于计算机RS232的电平与电路板(通常+5V)之间电平的不同所以要用到转换芯片

如果PCB板电源+-5V的话用MAX232

如果PCB板(FPGA)电源是+-3.3V的话用MAX3232




这个图的串口如果采用母头的话,要用交叉公母线,保证是PCB板上这边的RxD连计算机的TxD3 Pin),PCB板这边的TxD连计算机的RxD(2 Pin).




沙发
lovelyegle|  楼主 | 2009-12-16 23:35 | 只看该作者
本帖最后由 lovelyegle 于 2009-12-16 23:45 编辑

串行通信波特率

这里要弄清楚波特率与比特率的差别

比特率是数字信号的传输速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数bit/s(bps)、每秒千比特数(Kbps)或每秒兆比特数(Mbps)来表示(此处KM分别为10001000000,而不是涉及计算机存储器容量时的10241048576)

波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)




波特率与比特率的关系为:比特率=波特率X单个调制状态对应的二进制位数



两相调制(单个调制状态对应1个二进制位)的比特率等于波特率;

四相调制(单个调制状态对应2个二进制位)的比特率为波特率的两倍;

八相调制(单个调制状态对应3个二进制位)的比特率为波特率的三倍;

依次类推。



对于串行通信来说,或者说是对于普通的数字电路来说,都是两相调制(单个调制状态对应1个二进制位),因此波特率=比特率(通常叫波特率)。



PS:可以看看下面图就知道什么是四相调制。

如果系统时钟是1.8432MHz,那16分频就得到115200Hz

reg
[3:0] BaudDivCnt;
always @(posedge clk) BaudDivCnt <= BaudDivCnt + 1;

wire BaudTick = (BaudDivCnt==15);



但通常系统的时钟不是刚刚好是波特率的整数倍,如果不采用DCM对系统进行倍频的话,可以采用下面程序进行处理,设系统时钟为2MHz=2000000Hz

2000000/115200=17.36111

1024/59= 17.356

2000000/1152001024/59

两个频率很接近,可以采用下面程序产生我们要的波特率。

// 10 bits for the accumulator ([9:0]), and one extra bit for the accumulator carry-out ([10])reg
[10:0] acc;          // 11 bits total!

always @(posedge clk)
  acc <= acc[9:0] + 59;


// use only 10 bits from the previous result, but save the full 11 bits

wire BaudTick = acc[10]; // so that the 11th bit is the carry-out


当系统时钟为2MHz的时候,计算得到的波特率的值为115234,与115200只有0.03%的误差。

我们怎么得到59呢,可以看下面的推导


[localimg=180,145]5[/localimg]

其中Baud<<BaudGeneratorAccWidthBaud左移BaudGeneratorAccWidth位,相当于Baud乘以2BaudGeneratorAccWidth次方。

参照上面的程序与公式推导可以把程序修改如下:

parameter
ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;
parameter BaudGeneratorAccWidth = 16;
parameter BaudGeneratorInc = (Baud<<BaudGeneratorAccWidth)/ClkFrequency;

reg [BaudGeneratorAccWidth:0] BaudGeneratorAcc;
always @(posedge clk)
  BaudGeneratorAcc <= BaudGeneratorAcc[BaudGeneratorAccWidth-1:0] + BaudGeneratorInc;

wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth];




当要注意的是,上面程序中BaudGeneratorInc的计算公式出错,因为在Verilog语言中中间结果只能32位,而这个公式计算的结果超过了32位。所以要把这行改为

parameter
BaudGeneratorInc = ((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4);



程序改变,得到的波特率不变。

使用特权

评论回复
板凳
lovelyegle|  楼主 | 2009-12-16 23:50 | 只看该作者
RS232发送接收模块


可以参考下面文档,这里就不贴出来了。




RS232发送模块(Verilog

RS232接收模块(Verilog

以上程序均标注了J



调用串口发送接收模块

`timescale 1ns / 1ps



module serialfun(clk, RxD, TxD);

input clk;     //系统时钟



input RxD;

output TxD;



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

wire RxD_data_ready;

wire [7:0] RxD_data;



async_receiver deserializer(                //RS-232接收模块

                        .clk(clk),

                                                .RxD(RxD),

                                                .RxD_data_ready(RxD_data_ready),

//当接收到一个字节的数据时,"RxD_data_ready"有效一个周期

                                                .RxD_data(RxD_data)   //接收一个字节数据

                                                );



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

async_transmitter serializer(               //RS-232发送模块

                        .clk(clk),

                                               .TxD(TxD),

                                                .TxD_start(RxD_data_ready),   

//"TxD_start"置位后开始传输

                                                .TxD_data(RxD_data)  //发送一个字节数据

                                                );



endmodule



这个程序的结果是在从计算机发送八个字节到FPGAFPGA再把这八个字节转发回计算机。

要注意是如果以十六进制发送的话,就要以十六进制显示,8个字节可以发送2个字符(0~F)。如果没选以十六进制发送的话,会以ASCII码发送,只能发送一个字符(一个字符的ASCII8个字节)。




Spartan3E Starter Kit开发板上有两个串口,所以设置管脚时要注意选择哪个串口,选择母头的话(DCE)与计算机相连的串口线选择交叉的公母线;选择公头的话(DTE),与计算机相连的串口线选择交叉的母母线

我选择了公头,UCF文件如下(约束管脚)



NET
"clk" LOC
= "C9" |
IOSTANDARD
= LVCMOS33 ;

NET
"RxD" LOC
= "U8" |
IOSTANDARD
= LVTTL ;

NET
"TxD" LOC
= "M13" |
IOSTANDARD
= LVTTL |DRIVE
= 8 |SLEW
= SLOW ;



本文Xilinx ISE工程文件(在Spartan3E Starter Kit开发板上实现)

使用特权

评论回复
地板
wangwo| | 2009-12-17 21:05 | 只看该作者
好**!

使用特权

评论回复
5
god_like| | 2009-12-18 10:26 | 只看该作者
LZ对串口通讯很有研究啊

使用特权

评论回复
6
thf2008| | 2009-12-19 09:39 | 只看该作者
LZ研究的很深啊

使用特权

评论回复
7
linhai2009| | 2009-12-19 10:07 | 只看该作者
好**,转了

使用特权

评论回复
8
huamunv| | 2009-12-20 23:33 | 只看该作者
PIAO LAING

使用特权

评论回复
9
linhai1986| | 2009-12-21 00:18 | 只看该作者
不错,学习了

使用特权

评论回复
10
ncxiaoliu| | 2010-1-21 22:30 | 只看该作者
好高深哦

使用特权

评论回复
11
god_like| | 2010-1-21 23:58 | 只看该作者
恩,写的很深,不过我们一般的应用好像用不了这么深吧

使用特权

评论回复
12
hulan1985| | 2010-1-22 22:31 | 只看该作者
一共也用不了20%

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

640

帖子

1

粉丝