最近在学习delphi,想通过学习开发个串口通讯的程序,通过自己学习和别人的指导,基本开发完成,可是朋友说是接受的数据不对,不知道啥原因,求指教。麻烦高手大哥了。也可以QQ指导:262431920unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, SPComm, StdCtrls, ComCtrls, ExtCtrls, Menus;
type
  Tmybytes = array[0..255] of byte;
  TForm1 = class(TForm)
    Panel1: TPanel;
    StatusBar1: TStatusBar;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    Memo1: TMemo;
    Comm1: TComm;
    Label3: TLabel;
    Edit3: TEdit;
    Button4: TButton;
    Edit4: TEdit;
    Label4: TLabel;
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    N4: TMenuItem;
    N5: TMenuItem;
    N6: TMenuItem;
    N7: TMenuItem;
    PopupMenu2: TPopupMenu;
    N8: TMenuItem;
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
      BufferLength: Word);
    procedure menuClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure N8Click(Sender: TObject);
  private
    // function CheckCRC8(InputBuff: Pchar; BuffLen: integer; Jiaoyan: word): boolean;    function senddataFUN(sendvalue: string): string;
    function GetCmd(var cmd: Tmybytes; data: string): integer;
    procedure menuClickopen(openvalue: integer);
    { Private declarations }
  public
    strlist: Tstringlist;
    { Public declarations }
  end;
function CheckCRC8(InputBuff: Pchar; BuffLen: integer; Jiaoyan: Word): boolean;var
  Form1: TForm1;
  rbuf, sbuf: array[0..7] of byte;
  resultbuf: array[0..39] of byte;  table8: array[0..255] of byte =
  ($00, $07, $0E, $09, $1C, $1B, $12, $15, $38, $3F, $36, $31, $24, $23, $2A, $2D,
    $70, $77, $7E, $79, $6C, $6B, $62, $65, $48, $4F, $46, $41, $54, $53, $5A, $5D,
    $E0, $E7, $EE, $E9, $FC, $FB, $F2, $F5, $D8, $DF, $D6, $D1, $C4, $C3, $CA, $CD,
    $90, $97, $9E, $99, $8C, $8B, $82, $85, $A8, $AF, $A6, $A1, $B4, $B3, $BA, $BD,
    $C7, $C0, $C9, $CE, $DB, $DC, $D5, $D2, $FF, $F8, $F1, $F6, $E3, $E4, $ED, $EA,
    $B7, $B0, $B9, $BE, $AB, $AC, $A5, $A2, $8F, $88, $81, $86, $93, $94, $9D, $9A,
    $27, $20, $29, $2E, $3B, $3C, $35, $32, $1F, $18, $11, $16, $03, $04, $0D, $0A,
    $57, $50, $59, $5E, $4B, $4C, $45, $42, $6F, $68, $61, $66, $73, $74, $7D, $7A,
    $89, $8E, $87, $80, $95, $92, $9B, $9C, $B1, $B6, $BF, $B8, $AD, $AA, $A3, $A4,
    $F9, $FE, $F7, $F0, $E5, $E2, $EB, $EC, $C1, $C6, $CF, $C8, $DD, $DA, $D3, $D4,
    $69, $6E, $67, $60, $75, $72, $7B, $7C, $51, $56, $5F, $58, $4D, $4A, $43, $44,
    $19, $1E, $17, $10, $05, $02, $0B, $0C, $21, $26, $2F, $28, $3D, $3A, $33, $34,
    $4E, $49, $40, $47, $52, $55, $5C, $5B, $76, $71, $78, $7F, $6A, $6D, $64, $63,
    $3E, $39, $30, $37, $22, $25, $2C, $2B, $06, $01, $08, $0F, $1A, $1D, $14, $13,
    $AE, $A9, $A0, $A7, $B2, $B5, $BC, $BB, $96, $91, $98, $9F, $8A, $8D, $84, $83,
    $DE, $D9, $D0, $D7, $C2, $C5, $CC, $CB, $E6, $E1, $E8, $EF, $FA, $FD, $F4, $F3);implementationuses
  uCRC16;
{$R *.dfm}{ TForm1 }{function CheckCRC8(InputBuff: Pchar; BuffLen: integer; Jiaoyan: Word): boolean;
var
  i: integer;
  Index, CRCvalue: byte;
begin
  Result := false;
  CRCvalue := 0;
  for i := 0 to BuffLen - 1 do
    begin
      Index := CRCvalue xor byte(InputBuff[i]);
      CRCvalue := table8[Index];
    end;
  //  CRCvalue := not CRCvalue;
  if CRCvalue = Jiaoyan then Result := true;
end;    }
function CheckCRC8(InputBuff: Pchar; BuffLen: integer; Jiaoyan: Word): boolean;
var
  i: integer;
  temp, CRCvalue: byte;
begin
  CRCvalue := 0;
  for i := 0 to BuffLen - 1 do
    begin
      temp := table8[CRCvalue];
      CRCvalue := temp xor byte(InputBuff[i]);
    end;
  if CRCvalue = Jiaoyan then Result := true;
end;
function HexToByte(const Hex: Char): byte;
const
  H: array[0..21] of Char = '0123456789abcdefABCDEF';
  X: Pointer = @H;
asm
      MOV   ECX,   21
      MOV   EDX,   [X]
  @LoopBegin:
      CMP   AL,   byte   PTR   [EDX   +   ECX]
      JZ   @Find
      LOOP   @LoopBegin      XOR   AL,AL
      JMP   @End  @Find:
      CMP   CL,15
      JNG   @NotGreaterThan15
      SUB   CL,6
  @NotGreaterThan15:
      MOV   AL,   CL
  @End:
end;
//得到校验码
function TForm1.GetCmd(var cmd: Tmybytes; data: string): integer;
var
  i, len: integer;
begin
  len := length(data) div 2;
  for i := 0 to len - 1 do
    begin
      cmd[i] := HexToByte(data[2 * i + 1]) * 16 + HexToByte(data[2 * i + 2]);
    end;
  cmd[len] := GetCRC8(@cmd, len);
  cmd[len + 1] := $4E;
end;
//发送数据
function TForm1.senddataFUN(sendvalue: string): string;
var
  len, i: integer;
  commflg: boolean;
  arrcmd: Tmybytes;
  strsend: string;
begin  commflg := true;
  strsend := sendvalue + GetCRC8B(@arrcmd, length(sendvalue) div 2) + '4E';
  len := length(strsend) div 2;
  for i := 0 to len - 1 do
    begin
      if not Comm1.writecommdata(@strsend[i], 1) then
        begin
          commflg := false;
          break;
        end;
      sleep(2);                         {发送时字节间的延时}
    end;
  if not commflg then
    showmessage('发送失败!');
  Result := strsend;
end;//接受
procedure TForm1.Button2Click(Sender: TObject);
begin
  with Comm1 do
    begin
      StopComm;
      CommName := Edit3.Text;
      StartComm;
    end;end;
//关闭COM
procedure TForm1.Button4Click(Sender: TObject);
begin
  Comm1.StopComm;
end;
//串口接受数据下发事件
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
  BufferLength: Word);
var
  viewstring1, viewstring, hexstringr: string;
  len, i: integer;
begin
  move(Buffer^, Pchar(@resultbuf)^, BufferLength);
  len := 20 + 10;
  for i := 0 to len - 1 do
    begin
      hexstringr := hexstringr + inttohex(resultbuf[i], 2);
    end;
  Memo1.Lines.Add('收到02 16进制数据:' + hexstringr);
  Memo1.Lines.Add('');
  if (resultbuf[0] = $53) and (resultbuf[len - 1] = $4E) then
    begin
      if CheckCRC8(@resultbuf, (len - 2), ord(resultbuf[len - 2])) then
        begin
          for i := 0 to BufferLength - 1 do
            viewstring := viewstring + inttohex(resultbuf[i], 2) + '  ';
          viewstring := '接受:' + viewstring;
          Memo1.Lines.Add(viewstring);
          Memo1.Invalidate;
          case resultbuf[5] of
            $03:
              begin
                viewstring1 := '电池电压:' + floattostr((ord(resultbuf[16]) * 256 + ord(resultbuf[17])) / 100) + ' V'
                  + #13 + #10;
                viewstring1 := viewstring1 + '温度值:' + floattostr((ord(resultbuf[20]) * 256 + ord(resultbuf[21])) /
                  100) + ' C';
              end;
            $05:
              Memo1.Lines.Add('操作应答数据报');
          end;
          Memo1.Lines.Add(#13 + #10 + viewstring1 + #13 + #10);
          if strlist.Count > 0 then
            for i := 0 to strlist.Count - 1 do
              begin
                menuClickopen(strtoint(strlist[i]));
              end
          else
            menuClickopen(5);
          strlist.Clear;
        end;
    end;
  if strlist.Count > 0 then
    for i := 0 to strlist.Count - 1 do
      begin
        menuClickopen(strtoint(strlist[i]));
      end
  else
    menuClickopen(5);
end;procedure TForm1.menuClickopen(openvalue: integer);
var
  strdata: string;
  senddata: string;
begin
  case openvalue of
    1:                                  // 上传时间间隔
      senddata := '53' + inttohex(strtoint(Edit1.Text), 4) + '01' + inttohex(strtoint(Edit4.Text), 4);
    2:                                  //进入测试模式
      senddata := '53' + inttohex(strtoint(Edit1.Text), 4) + '020001';
    3:                                  //退出测试模式
      senddata := '53' + inttohex(strtoint(Edit1.Text), 4) + '020002';
    4:                                  //重新上传数据
      senddata := '4E' + inttohex(strtoint(Edit1.Text), 4) + '040003';
    5:                                  //不需要重新上传数据
      senddata := '53' + inttohex(strtoint(Edit1.Text), 4) + '040000';
  end;  case openvalue of
    1..5:
      begin
        strdata := senddataFUN(senddata);
        Memo1.Lines.Add('向设备下发16进制数据 :' + strdata);
        Memo1.Lines.Add('');
      end;
  end;end;
procedure TForm1.menuClick(Sender: TObject);
var
  strdata: string;
  senddata: string;
begin
  strlist.Add(inttostr((Sender as TMenuItem).Tag));
  menuClickopen(2);
end;procedure TForm1.Button1Click(Sender: TObject);
begin
  PopupMenu1.Popup(left + Button1.left + Button1.Parent.left, top + Button1.Parent.top + Button1.top + Button1.Height *
    2);
end;procedure TForm1.FormShow(Sender: TObject);
begin
  strlist := Tstringlist.Create;
  Button2Click(button2);
end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Comm1.StopComm;
end;procedure TForm1.N8Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
end;end.{
1. COM==0x01,设置数据上传时间间隔,PARAM表示将要设置的时间间隔,以分钟为单位。
2. COM==0x02,测试模式选择命令,
               若PARAM==0x0001,进入测试模式,30分钟后自动退出测试模式。在测试模式下,传感器每两分钟上传一次数据。
               若PARAM==0x0002,退出测试模式。
3. COM==0x04,应答命令。
               PARAM==0x0003,要求传感器重新上传数据。主机接收到传感器上传的数据后发现有误码或者其它原因需要重新上传时可用此命令;
               PARAM==0x0000,数据接收完整,不需要重新上传。如果传感器20ms内收不到任何应答命令,则认为主机没收到数据,自动重发,最多重试2次。
}

解决方案 »

  1.   

    mscomm没用过,不熟悉,粗看下:
    Comm1ReceiveData没有判断接收到的数据长度,还有MSCOMM中是不是有个设置接收到多少个字节后才触发事件?如果是1的话,这时数据还没收全吧。应当是先判断长度,然后再考虑Move数据
      

  2.   

    我这个用的是spcomm组件,开发上位机程序,硬件提供有文档,不过我刚接触上位机,有点看不懂。
    谁能帮我讲讲吗?上传数据格式:
    开始标志1byte 设备地址4byte 控制字1byte 数据长度2byte 数据区
    n个byte 校验和
    1byte 结束标志
    1byte
    (STX)
    0x53 (ADDR)
    0x00000002 (COM)
    0x03 (SIZE)
    0x14 (DATA)
    20-bytes数据区 (CHK)
    校验和 (ETX)
    0x4e
    (83, 0, 0, 0, 2, 3, 0, 20, 0, 16, 255, 236, 0, 0, 61, 189, 3, 241, 0, 0, 5, 200, 0, 0, 10, 191, 0, 0, 20, 78, 83, 0, 0, 0, 2, 3, 0, 20, 0, 255)  78数据区格式:
    2byte 2byte 2byte 2byte 2byte 2byte 2byte 2byte 2byte 2byte
    本次开机后发送数据包计数 发送数据包总计数 未用 温度模数转换值 电池电压模数转换值 未用 电池电压值 未用 温度值 未用
      

  3.   

    通信是采用485方式吧?
    上位机对某台设备发个命令(就是采集数据了),等待下位机返回数据,就是在Comm1ReceiveData事件里了.
    在这个事件方法里,第一句就是move(Buffer^, Pchar(@resultbuf)^, BufferLength);没有进行数据长度判断啊,(很遗憾,我也没用过spcomm,我基本是用cport)不知道spcomm是不是也有个属性设置接收到多少长度数据后才触发事件,如果没有,这种写法肯定是有问题的。
    对于这种比较固定的时间的,可以采用一种简单的延时方法,在move前加个sleep(),保证能接收到一帧全部数据就行。
      

  4.   

    我刚学,很多地方不太懂,现在也还在改。6楼说的sleep。程序中好像有。
      

  5.   

    你在move设置断点看看接收到的数据
      

  6.   

    我刚做了一个这样的程序。
    procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;BufferLength: Word);
    事件中的接受不是将设备发送的数据一次能接受完的,有时事件可能响应2次。
    最好能定义一个全局的array ,事件到来时依次判断是否有开始位,如果有就往array 中写数据,然后判断结束标志,到了作处理和清空array,重新判断开始位。