麻烦哪位大虾抽空给讲一下通信协议。(100分)

A

allbone

Unregistered / Unconfirmed
GUEST, unregistred user!
小弟初次做pc和单片机的串口通信,对通信协议这方面很迷茫。
手头有《delphi串口通信编程》,但是感觉比较笼统,
请大家详细介绍一下。
 
用Delphi开发串口通信软件一般有两种方法:一是利用Windows的通信API函数,另一种是采用Microsoft的MSComm控件。利用API编写串口通信程序较为复杂,需要掌握大量通信知识,其优点是实现的功能强大,应用面广泛,适合于编写较为复杂的低层次通信程序。而利用MSComm控件则相对较简单,该控件具有丰富的与串口通信密切相关的属性及事件,提供了对串口的各种操作。
  一、MSComm控件的主要属性及事件
  (1)CommPort:设置或返回串行端口号,缺省值1。
  (2)Setting:设置或返回串口通信参数,格式为“波特率,奇偶校验位,数据位,停止位”。例如:MSComm1.Setting:=′9600,n,8,1′
  (3)PortOpen:打开或关闭串行端口,格式为:MSComm1.PortOpen:={True|False}
  (4)InBufferSize:设置或返回接收缓冲区的大小,缺省值为1024字节。
  (5)InBufferCount:返回接收缓冲区内等待读取的字节数,可通过设置该属性为0来清空接收缓冲区。
  (6)RThreshold:该属性为一阀值,它确定当接收缓冲区内的字节个数达到或超过该值后就产生代码为ComEvReceive的OnComm事件。
  (7)SThreshold:该属性为一阀值,它确定当发送缓冲区内的字节个数少于该值后就产生代码为ComEvSend的OnComm事件。
  (8)InputLen:设置或返回接收缓冲区内用Input读入的字节数,设置该属性为0表示Input读取整个缓冲区的内容。
  (9)Input:从接收缓冲区读取一串字符。
  (10)OutBufferSize:设置或返回发送缓冲区的大小,缺省值为512字节。
  (11)OutBufferCount:返回发送缓冲区内等待发送的字节数,可通过设置该属性为0来清空缓冲区。
  (12)OutPut:向发送缓冲区传送一串字符。
  如果在通信过程中发生错误或事件,就会触发OnComm事件,并由CommEvent属性代码反映错误类型,在通信程序的设计中可根据该属性值来执行不同的操作。CommEvent属性值及其含义如下:
  (1)ComEvSend:值为1,发送缓冲区的内容少于SThreshold指定的值。
  (2)ComEvReceive:值为2,接收缓冲区内字符数达到RThreshold指定的值。
  (3)ComEvFrame:值为1004,硬件检测到帧错误。
  (4)ComEvRxOver:值为1008,接收缓冲区溢出。
  (5)ComEvTxFull:值为1010,发送缓冲区溢出。
  (6)ComEvRxParity:值为1009,奇偶校验错误。
  (7)ComEvEOF:值为7,接收数据中出现文件尾(ASCII码为26)字符。
  二、程序样例
  在Delphi3.0中无法使用MSComm控件,笔者使用的是Delphi5.0。MSComm控件是VB中的OCX控件,首先需要将其添加到Delphi中,选择菜单“Component”→“Import ActiveX Control”,在“Import ActiveX”页内选择“Microsoft Comm Control”,点击“Install”安装,安装后在“ActiveX”组件板中出现MSComm图标,即可被使用。有一点要注意,在Object Inspector中MSComm控件的Input和Output属性是不可见的,但它们仍然存在,这两个属性的类型是OleVariant(Ole万能变量)。
  下面是一接收程序的样例(主要部分),大家可根据实际需要进行完善。
  在Form中放置一Memo控件用于显示接收的数据,Combobox1选择通信参数(Setting属性值),Combobox2选择串口(CommPort属性值),按Button1开始接收数据,按Button2停止接收。
  procedure TForm1.FormCreate(Sender: TObject);
  begin
  Mscomm1.InBufferCount :=0; // 清空接收缓冲区
  Mscomm1.InputLen :=0; // Input读取整个缓冲区内容
  Mscomm1.RThreshold :=1; // 每次接收到字符即产生OnComm事件
  end;
  procedure TForm1.Button1Click(Sender: TObject);
  begin
  Mscomm1.Settings :=ComboBox1.Text;
  if ComboBox2.Text =′com1′ then // 假设只考虑COM1和COM2两种情况
Mscomm1.CommPort :=1
  else
    Mscomm1.CommPort :=2;
  Mscomm1.PortOpen :=true; // 打开串口
  Mscomm1.DTREnable :=true; // 数据终端准备好
  Mscomm1.RTSEnable :=true; // 请求发送
  end;
  procedure TForm1.Button2Click(Sender: TObject);
  begin
  Mscomm1.PortOpen :=false; // 关闭串口
   Mscomm1.DTREnable :=false;
  Mscomm1.RTSEnable :=false;
  end;
  procedure TForm1.MSComm1Comm(Sender: TObject);
  var
  recstr:Olevariant;
  begin
   if Mscomm1.CommEvent = 2 then
    begin
  recstr := Mscomm1.Input ;
  Memo1.text := Memo1.Text + recstr;
  end;
end;

 
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, Comm32, ComCtrls, ExtCtrls, CPort;
const
//块大小
kuaidaxiao=90;
type
TForm1 = class(TForm)
Comm321: TComm32;
StatusBar1: TStatusBar;
Panel1: TPanel;
Button1: TButton;
BitBtn1: TBitBtn;
Button2: TButton;
Button3: TButton;
Memo1: TMemo;
Memo2: TMemo;
Button4: TButton;
Comm322: TComm32;
BitBtn2: TBitBtn;
Button5: TButton;
Button6: TButton;
Button7: TButton;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
ComDataPacket1: TComDataPacket;
ComPort1: TComPort;
Button8: TButton;
Button9: TButton;
Timer1: TTimer;
ProgressBar1: TProgressBar;
ProgressBar2: TProgressBar;
procedure BitBtn1Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Comm321ReceiveData(Buffer: Pointer; BufferLength: Word);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure BitBtn2Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Comm322ReceiveData(Buffer: Pointer; BufferLength: Word);
procedure Button7Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

//发送文件名
fawenjianming:string;
//发送文件大小
fawenjiandaxiao:integer;
//发送文件块数 ,接受文件块数
fawenjiankuaishu:integer;
//发送数据块的序数
faxushu,jiexushu:integer;
//发送数据块序数变量
faxushubianliang:string;

//可以发送下一块标记
nextblock:boolean;
//前一块发送错误标记
errorblock:boolean;
//可以发送标记
start:boolean;
//用户中断标记
zhongduan:boolean;

//发送端的数据块1,数据块2和数据块变量
fashujukuai1,fashujukuai2,fashujukuaibianliang:array[1..90] of char;
//发送端的数据块1,数据块2和数据块变量
jieshujukuai1,jieshujukuai2,jieshujukuaibianliang:array[1..90] of char;

//发送字符
fazifu:string;
//发送端件流变量 //接收端件流变量
fawenjianliu,jiewenjianliu: TfileStream;

//接收文件名
jiewenjianming:string;
//接收文件大小
jiewenjiandaxiao:integer;
//接收数据块序数
jiewenjiankuaishu:integer;
//接收端发送过来的要求发送的数据块序数
yaoqiuxushu:integer;
//接收序数变量,用来判断接收是否连续。
jiexushubianliang:integer;
implementation

{$R *.DFM}

//打开端口按钮
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
comm322.StopComm;
if comm321.StartComm
then begin
statusbar1.Panels[0].Text:='已经和'+comm321.CommPort+'端口连接';
//开始置为true
nextblock:=false;
//开始置为false
errorblock:=false;
//开始置为false
start:=false;
//开始置为false
zhongduan:=false;
end;
end;

//关闭端口按钮
procedure TForm1.Button1Click(Sender: TObject);
begin
comm321.StopComm;
statusbar1.Panels[0].Text:='已经和'+comm321.CommPort+'端口断开';
zhongduan:=false;
start:=false;
end;

//接收时间处理过程
procedure TForm1.Comm321ReceiveData(Buffer: Pointer; BufferLength: Word);
var
s:string;
begin
memo2.Lines.Add(pchar(buffer));
//接收端准备好
if pchar(buffer)='START'
then start:=true;

s:=copy(pchar(buffer),1,4);
//可以接受下一块
if s='NEXT'
then begin
s:=copy(pchar(buffer),5,length(pchar(buffer))-4);
yaoqiuxushu:=strtoint(s);
nextblock:=true;
end;
end;

//发送按钮
procedure TForm1.Button2Click(Sender: TObject);
var
c:integer;
i:integer;
zifu:string;
begin
if not start
//接收端没有准备好
then begin
showmessage('错误,接收端还没有准备好'+#13+'等一会儿在单击这个按钮');
exit;
end
//接收端已经准备好了
else if opendialog1.Execute then
begin
statusbar1.Panels[0].Text:='正在发送文件'+opendialog1.FileName;

fawenjianliu:= TFileStream.Create(opendialog1.FileName, fmOpenRead );
fawenjianliu.seek(0, soFromBeginning);
zifu:='';

faxushu:=0;

//取得发送文件名
fawenjianming:=extractfilename(opendialog1.FileName);
//取得发送文件大小
fawenjiandaxiao:=fawenjianliu.Size;
//取得发送文件块数
fawenjiankuaishu:=(fawenjiandaxiao div (kuaidaxiao-4))+1;
//首先发送的内容,格式实例: %00%example.txt%100%
zifu:='%00%'+fawenjianming+'%'+inttostr(fawenjiankuaishu)+'%';

//首先发送文件名和文件块数过去
comm321.WriteCommData(pchar(zifu),length(zifu));

//读文件并向端口写数据
zifu:='';

//发送内容的格式如下:%XX%YYYYY.....
// xx为数据块的序数
// yyyy....为内容

//开始循环,只到所有的数据块发送完毕
repeat

//可以发送下一块数据
if nextblock then
begin
//要求数等于上一次发送的序数,则重发一遍
if faxushu=yaoqiuxushu
then begin
comm321.writecommdata(@fashujukuaibianliang,(c+4));
nextblock:=false;
end
else begin
FillChar(fashujukuai1,SizeOf(fashujukuai1),0);
c := fawenjianliu.Read(fashujukuai1, (kuaidaxiao-4));
for i:=1 to c do
fashujukuai2[i+4]:=fashujukuai1;

fashujukuai2[1]:='%';

faxushubianliang:=inttostr(yaoqiuxushu div 10);
fashujukuai2[2]:=faxushubianliang[1];

faxushubianliang:=inttostr(yaoqiuxushu mod 10);
fashujukuai2[3]:=faxushubianliang[1];

fashujukuai2[4]:='%';

//发送数据块
comm321.writecommdata(@fashujukuai2,(c+4));
//显示已经发送的块数
progressbar1.Position:=round((faxushu/fawenjiankuaishu)*100);
//清空数据块变量
FillChar(fashujukuaibianliang,SizeOf(fashujukuaibianliang),0);

//保存发送的数据块在数据块变量中
fashujukuaibianliang:=fashujukuai2;

//序数相应的加1
faxushu:=faxushu+1;

nextblock:=false;
end;
end;//end of nextblock
//如果用户中断了
if zhongduan
then exit;

//交出权限
application.ProcessMessages;
until faxushu=fawenjiankuaishu;
//发送结束标志

fawenjianliu.Free;
statusbar1.Panels[0].Text:='发送文件'+opendialog1.FileName+'完毕....';
end;
end;

//下一块数据按钮
procedure TForm1.Button3Click(Sender: TObject);
begin
nextblock:=true;
end;

//中断按钮
procedure TForm1.Button7Click(Sender: TObject);
begin
zhongduan:=true;
end;

//start按钮
procedure TForm1.Button8Click(Sender: TObject);
begin
start:=true;
end;

///////////////////////////////////////////////////////////////////////////
////////////////////////////////接收部分///////////////////////////////////

//打开端口按钮
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
comm321.StopComm;
if comm322.StartComm then
begin
statusbar1.Panels[1].Text:='已经和'+comm322.CommPort+'端口连接';
comm322.WriteCommData('START',5);
end;
end;

//关闭端口按钮
procedure TForm1.Button5Click(Sender: TObject);
begin
comm322.StopComm;
statusbar1.Panels[1].Text:='已经和'+comm322.CommPort+'端口断开';
end;

//开始接受
procedure TForm1.Comm322ReceiveData(Buffer: Pointer; BufferLength: Word);
var
i:integer;
changdu:integer;
bianliang1,bianliang2:string;
jiezifu:string;
begin
memo2.Lines.Add(pchar(buffer));

jiezifu:='';
FillChar(jieshujukuai1,SizeOf(jieshujukuai1),0);
Move(Buffer^,pchar(@jieshujukuai1)^, BufferLength);

changdu:=bufferlength;

bianliang1:='';
//取出前面四个字节
for i:= 1 to 4 do
bianliang1:=bianliang1+jieshujukuai1;
//取出中间两个字节,即为数据块数
bianliang2:=copy(bianliang1,2,2);

//接收到的数据块序数
jiexushu:=strtoint(bianliang2);

//开始处理各种情况
if jiexushu=0
//接收到发送过来的文件名和文件块数,格式实例:%00%example.txt%100%
then begin
//取出文件名
bianliang1:=pchar(buffer);
delete(bianliang1,1,4);
jiewenjianming:=copy(bianliang1,1,pos('%',bianliang1)-1);
//取出文件块数
delete(bianliang1,1,pos('%',bianliang1));
bianliang2:=copy(bianliang1,1,pos('%',bianliang1)-1);
jiewenjiankuaishu:=strtoint(bianliang2);
//生成文件
jiewenjianliu:= TFileStream.Create(extractfilepath(application.exename)+jiewenjianming,fmOpenWrite or fmCreate );
//jiewenjianliu.seek(0, soFromBeginning);

//把接收序数变量置0
jiexushubianliang:=0;

jiezifu:='NEXT'+inttostr(jiexushubianliang+1);
comm322.WriteCommData(pchar(jiezifu),length(jiezifu));
end
//接收到数据
else begin
if jiexushu=(jiexushubianliang+1)
//接收正确,处理数据
then begin
FillChar(jieshujukuai1,SizeOf(jieshujukuai1),0);
FillChar(jieshujukuai2,SizeOf(jieshujukuai2),0);
Move(Buffer^,pchar(@jieshujukuai1)^, BufferLength);
//取出数据块
for i:=5 to changdu do
jieshujukuai2[i-4]:=jieshujukuai1;
//写入文件
jiewenjianliu.write(jieshujukuai2,changdu-4);

//显示已经接收的块数
progressbar2.Position:=round((jiexushu/jiewenjiankuaishu)*100);

if jiexushu=jiewenjiankuaishu
//接收完毕
then begin
jiewenjianliu.Free;
statusbar1.Panels[1].Text:='接收文件'+jiewenjianming+'完毕....';
end
//还没有接受完毕
else begin
//发送next信号,要求可以发送下一块数据
jiezifu:='NEXT'+inttostr(jiexushu+1);
comm322.WriteCommData(pchar(jiezifu),length(jiezifu));

//接收序数变量相应加1
jiexushubianliang:=jiexushubianliang+1;
statusbar1.Panels[1].Text:='正在接收文件'+jiewenjianming+'....';
end;
end
//接收错误,发送error信号,要求重发
else begin
//发送next信号,要求可以发送下一块数据
jiezifu:='NEXT'+inttostr(jiexushubianliang+1);
comm322.WriteCommData(pchar(jiezifu),length(jiezifu));
end;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
{setlength(fashujukuai1,kuaidaxiao);
setlength(fashujukuai2,kuaidaxiao);
setlength(fashujukuaibianliang,kuaidaxiao);
setlength(jieshujukuai1,kuaidaxiao);
setlength(jieshujukuai2,kuaidaxiao);
setlength(jieshujukuaibianliang,kuaidaxiao);}
end;

end.


940801 (2000-9-1 18:02:00)
VB 中有一个很漂亮的 demo,真的很漂亮,
事件驱动的处理方式,出错处理也相当不错。
我当初写电话语音卡通信程序就参考过,
找个中文版 vb 来看看吧。

 
谢谢兄台,但是我需要的不是例子
希望详细讲讲自定义通信协议的步骤,规则。
 
还是回去看看书把,基础方面的不是一两句话就能说清的
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
603
import
I
I
回复
0
查看
516
import
I
顶部