delphi—spcomm

  • Post author:
  • Post category:其他


Spcomm共实现了三个类:串口类Tcomm、读线程类TreadThread以及写线程类TwziteThread[1]。Tcomm的某个实例在方法StartComm中打开串口,并实例化了一个读线程ReadThread和一个写线程WriteThread,它们和主线程之间进行消息的传递,实现串口通信。

3.1 Spcomm控件的基本属性、方法和事件

Spcomm串口通信控件的基本属性、方法和事件说明如下:

CommName属性:计算机串口端口号的名字,COM1、COM2……等,在打开串口前,必须填写好此值。

Parity属性:校验位 None、Odd、Even、Mark、Space等。

BaudRate:设定支持串口通信用的波特率9600,4800等,根据实际需要来定,在串口打开后也可更改波特率,实际波特率随之更改。

ByteSize属性:表示一个字节中,使用多少个数据位收发数据,根据具体情况设定5、6、7、8等。

StopBits属性:表示一个字节中,使用停止位的位数,根据具体情况设定1、1.5、2等。


SendDataEmpty属性:布尔属性,为True时表示发送缓存为空,或者发送队列里没有信息;为False时表示表示发送缓存不为空,或者发送队列里有信息。

StartComm方法:用来打开通信串口,开始通信。如果失败,则会导致串行口错误。错误类型大致分为串行口己处于打开状态,所以不能打开串行口,不能创建读写进程,不能建立串行口缓冲区等。

StopComm方法:用来停止通信串行口的所有进程,关闭串口。

WriteCommData

(pDataToWriteChar;dwSizeofDataToWrite:Word)方法是带有布尔型返回值的函数,其中参量pszStr-ingToWrite是要写入串行口的字符串,DwSizeaf- DataToWrite是要写入的字符串的长度。该函数通过一个写线程向串行口输出缓冲区发送数据。发送操作将在后台默认执行。如果写线程PostMessage成功,则返回值是True,若写线程失败,返回值是False。


OnReceiveData

(Bufferointer;BufferLength:Word),其中Buffer是指向输入缓冲区的指针。BufferLength是从缓冲区收到的数据长度。当输入缓冲区收到数据时,该事件被触发。当输入缓存有数据时将触发该事件,对从串口收到的数据进行处理。

3.2 Spcomm串口通信的实现

Spcomm串行通信控件具有多线程的特性,接收和发送数据分别在两个线程内完成,

接收线程负责收到数据时触发OnReceiveData事件;用WriteCommData()函数将待发送的数据写入输出缓冲器

,发送线程在后台完成数据发送工作。

在接收和发送数据前需要初始化串口,用StartComm方法打开串口,退出程序时用StopComm方法关闭串口。


实现PC机与单片机之间的数据发送及接收需要以下步骤:

(1)初始化并打开串口

需要选择本次通信使用的串口,确定通信协议,即设置波特率、校验方式、数据位、停止位等属性,打开该串口。示例代码如下:

//初始化并打开串口
Comm1.BaudRate:=9600;//波特率9600bps
Comm1.Parity:=None;//奇偶检验无
Comm1.ByteSize:=8;//数据位8
Comm1.StopBits:=1;//停止位1
Comm1.StartComm; //打开串口

(2)建立握手信号

实现PC机与单片机之间的通信,首先要调通它们之间的握手信号,握手信号可以随意选择某特定字符串, 当PC发出这样一帧数据后,通过接收事件能收到单片机返回的这一帧数据或特定的某字符串,则表示握手成功,系统通信正常。两者之间就可以按照协议相互传输数据。否则需重新建立握手信号。

(3)发送数据

在编写基于串口的计算机工业测控时,通常需要由PC机向下位机发送命令以控制下位机的行为,同时向下位机发送有关数据。利用Spcomm串口控件向下位机发送数据示例代码如下:

//发送数据和控制字程序
procedure senddata;
	var
	i:integer; commflg : Boolean;
begin
	commflg:=true ;
	for i:=1 to 8 do
	begin
	if not fcomm comml writecommdata(sendbutter,i) then
		begin
		Commflg=false;
		break;
		end;
	end;
end;

(4) 接收数据

在编写基于串口的计算机工业测控时,通常需要由下位机向PC机发送数据以使PC机了解系统的测试数据或下位机的运行状态,并进而控制下位机的行为。利用Spcomm串口控件接收下位机发送的数据信息的示例代码如下:

//事件驱动方式接收数据程序
procedure TForm1.CommlReceiveData(Sender:Tobject;
Bufferointer; bufferLength:Word);
var
receivedata:array of byte;
begin
sleep(100);//等待100ms,保证接收到所有数据
move(buffef ,receivedata,bufferlength);
//将接收缓存区中的数据转移到数组中
……
end;

(5) 关闭串口

在系统开发中,应注意在不使用串口时应及时关闭串口,释放系统资源,否则可能会影响系统的其它应用。关闭串口的代码如下:

procedure TForm1.FormClose ( Sender;TObj ect:var Action:TCIoseAction );
begin
comml.StopComm ;
end;

4 Spcomm串口通信的关键技术问题

Spcomm应用的核心在于主线程、读线程和写线程之间的消息传递机制,而通信数据相关信息的传递也是以消息传递的方式进行的。在使用Spcomm进行串口通信编程,除按照说明使用外,还需要特别注意以下两个问题。

首先,Spcomm是通过ReadIntervalTimeout属性的设置,来确定所接收到的数据是否属子同一帧数据,其默认值是100ms,也就是说,只要任何两个字节到达的时间

间隔小于1OOms

,都被认为是属于同一帧数据,在与单片机协同工作时,要特别注意这个问题[2]。

另外,Spcomm的默认属性设置是支持软件流控制的,用于流控制的字符是13H(XoffChar)和11H(XonChar),当单片机以二进制方式发送数据时,

必须要禁用Spcomm对于软件流控制的支持,否则,在数据帧中出现的13H,11H会被Spcomm作为控制字符而加以忽略。


3.1 串口调试助手的概要设计

端口初始化就是对波特率、校验、数据位和停止位的设置。当端口打开时指示灯为绿色,当端口为打开时指示灯为红色。

当接收数据后对数据进行变换时,要先判断接收的数据是否符合变换的条件,例如当以十进制显示时,接收的数据不是十六进制数就会报错。

发送数据之前必须保证串口打开,所以先判断端口有没有打开。由于发送空内容没有意义,所以还要保证发送内容不能为空。

自动发送当选中CheckAutosend时,串口调试助手以设置好的时间间隔发送数据。

procedure TForm1.Button2Click(Sender: TObject);
var
   i ,j,TextLen: Integer;
   //aucBuf : array[0..4096] of byte;
   SendBuf : string;
   strbuf : string;
begin
     strbuf :=Memo2.text;
     sendbuf := '';
     if HexSendFlag = True then
     begin
          strbuf := StringReplace(strbuf, #10, '', [rfReplaceAll]);
          strbuf := StringReplace(strbuf, #13, '', [rfReplaceAll]);
          strbuf := StringReplace(strbuf, ' ', '', [rfReplaceAll]);
          strbuf := StringReplace(strbuf, #9, '', [rfReplaceAll]);
          TextLen := Length(strbuf);
          i:=1;
          while (i <= TextLen) and (strbuf[i] in ['0'..'9','A'..'F','a'..'f']) do
                inc(i);
          if i <= TextLen then
          begin
               ShowMessage('非法的十六进制数');
               Exit;
          end;
          if TextLen > 0 then
          begin
              for j:=0 to (TextLen div 2 - 1) do
              begin
                  //aucBuf[j] := Byte(StrToIntDef('$' + strbuf[2*j + 1] + strbuf[2*j + 2], 0));
                  SendBuf := SendBuf + Char(StrToIntDef('$' + strbuf[2*j + 1] + strbuf[2*j + 2], 0));
              end;
              if TextLen mod 2 <> 0 then
              begin
                   //aucBuf[j] := Byte(StrToIntDef('$0'+ strbuf[2*j + 1], 0));
                   SendBuf := SendBuf + Char(StrToIntDef('$0'+ strbuf[2*j + 1], 0));
              end;
              //comm1.writecommdata(@aucBuf, TextLen div 2 + textLen mod 2);
              comm1.writecommdata(pchar(SendBuf), TextLen div 2 + textLen mod 2);
              //ShowMessage(IntToStr(TextLen div 2 + TextLen mod 2));
              SendLen := SendLen + TextLen div 2 + textLen mod 2;
          end;
     end
     else
     begin
         if Length(Memo2.Text) > 0 then
         begin
              comm1.writecommdata(pchar(strbuf), Length(Memo2.Text));
         end;
         if CheckBox6.Checked = true then
         begin
              strbuf := #13;
              comm1.writecommdata(pchar(strbuf), 1);
              SendLen := SendLen + 1;
         end;
         if CheckBox7.Checked = true then
         begin
              strbuf := #10;
              comm1.writecommdata(pchar(strbuf), 1);
              SendLen := SendLen + 1;
         end;
         SendLen := SendLen + Length(Memo2.Text);
     end;
     StatusBar1.Panels[0].Text := 'S:' + IntToStr(SendLen);
end;




j:=trunc((length(s1)/2))-1;
  for i:=0 to j do
  //s2:= s2+char(strtoint('$'+(copy(s1,2*i+1,2))));
  s3[i]:=char(strtoint('$'+(copy(s1,2*i+1,2))));
  comm1.writecommdata(s3,trunc((length(s1)/2)));



procedure sendhex(s:string);
var
  i,j,TextLen:integer;
  sendbuf,strbuf:string;
  p:pchar;
begin
  strbuf:='';
  sendbuf:= '';
  for i:=1 to length(s) do
    begin
      if ((copy(s,i,1)>='0') and (copy(s,i,1)<='9'))or((copy(s,i,1)>='a') and (copy(s,i,1)<='f'))or((copy(s,i,1)>='A') and (copy(s,i,1)<='F')) then
         begin
           strbuf:=strbuf+copy(s,i,1);
         end;
    end;
  showmessage(strbuf);
  TextLen:= Length(strbuf);
  showmessage(inttostr(TextLen));
  p:=pchar(strbuf);
  showmessage(p);
  try
  comm1.writecommdata(p,TextLen-1);
  except
    exit;
  end;
  if TextLen > 0 then
          begin
              for j:=0 to (TextLen div 2 - 1) do
              begin
                  //aucBuf[j] := Byte(StrToIntDef('$' + strbuf[2*j + 1] + strbuf[2*j + 2], 0));
                  SendBuf:= SendBuf + Char(StrToIntDef('$' + strbuf[2*j + 1] + strbuf[2*j + 2], 0));
              end;
              if TextLen mod 2 <> 0 then
              begin
                   //aucBuf[j] := Byte(StrToIntDef('$0'+ strbuf[2*j + 1], 0));
                   SendBuf:= SendBuf + Char(StrToIntDef('$0'+ strbuf[2*j + 1], 0));
              end;
              showmessage(SendBuf);
              //comm1.writecommdata(@aucBuf, TextLen div 2 + textLen mod 2);
       //       comm1.writecommdata(pchar(SendBuf), TextLen div 2 + textLen mod 2);
              //ShowMessage(IntToStr(TextLen div 2 + TextLen mod 2));
              //SendLen := SendLen + TextLen div 2 + textLen mod 2;
          end;