初做串口通信,对于MSCOMM理解不是很明白,
我做了一个测试程序,在自己的计算机上两个COM口这间传数据,是不是设置两者的settings相同,大体如下:
MSComm1.Commport :=1;
MSComm2.CommPort :=2;
MScomm1.settings :='9600,N,8,1';
MScomm1.settings :='9600,N,8,1';
MSComm1.PortOpen :=True;
MSComm2.PortOpen :=True;
发送数据是是否这样:
  MSComm1.OutPut := 'asdkfjalksjflkfasdj';
这样怎样才能在另一口接收数据呢?
  showmessage(MSComm2.Input.AsString);
另外,什么时候才会触发MSComm2.CommEvent事件
才能实现Case MSComm2.CommEvent of
2:
begin
end;
3:
begin
end;
end;
多谢!!!

解决方案 »

  1.   

    转:---- 基 于WIN95/NT 的 串 行 通 信 机 制
    ---- Windows 操 作 系 统 的 机 制 禁 止 应 用 程 序 直 接 访 问 计 算 机 硬 件, 但 它 为 程 序
    员 提 供 了 一 系 列 的 标 准API 函 数, 使 得 应 用 程 序 的 编 制 更 加 方 便 并 且 免 除 了
    对 有 关 硬 件 的 调 试 麻 烦。 在Windows95/NT 中, 原 来Windows3.X 的WM_COMMNOTIFY 消 息
    已 被 取 消, 操 作 系 统 为 每 个 通 信 设 备 开 辟 了 用 户 可 定 义 大 小 的 读/ 写 缓 冲
    区, 数 据 进 出 通 信 口 均 由 操 作 系 统 后 台 完 成, 应 用 程 序 只 需 对 读/ 写 缓 冲 区
    操 作 即 可。WIN95/NT 中 几 个 常 用 的 串 行 通 信 操 作 函 数 如 下:
    CreatFile
     打 开 串 行 口
    CloseHandle
     关 闭 串 行 口
    SetupComm
     设 置 通 信 缓 冲 区 的 大 小
    ReadFile
     读 串 口 操 作
    WriteFile
     写 串 口 操 作
    SetCommState
     设 置 通 信 参 数
    GetCommState
     获 取 默 认 通 信 参 数
    ClearCommError
     清 除 串 口 错 误 并 获 取 当 前 状 态
    ---- 除 上 述 几 个 函 数 外, 还 要 经 常 用 到 一 个 重 要 的 记 录DCB( 设 备 控 制
    块)。DCB 中 记 录 有 可 定 义 的 串 行 口 参 数, 设 置 串 行 口 参 数 时 必 须 先 用
    GetCommState 函 数 将 系 统 默 认 值 填 入DCB 控 制 块, 然 后 才 可 把 用 户 想 改 变 的 自 定
    义 值 设 定。
    ---- 在WIN95/NT 中 进 行 串 行 通 信 除 了 解 基 本 的 通 信 操 作 函 数 外, 还 要 掌 握 多 线
    程 编 程。 线 程 是 进 程 内 部 执 行 的 路 径, 是 操 作 系 统 分 配CPU 时 间 的 基 本 实
    体。 每 个 进 程 都 由 单 线 程 开 始 完 成 应 用 程 序 的 执 行。 串 行 通 信 需 要 利 用 多
    线 程 技 术 实 现, 其 主 要 的 处 理 逻 辑 可 以 表 述 如 下: 进 程 一 开 始 先 由 主 线 程
    做 一 些 必 要 的 初 始 化 工 作, 然 后 主 线 程 根 据 需 要 在 适 当 时 候 建 立 通 信 监 视
    线 程 监 视 通 信 口, 当 指 定 的 串 行 口 事 件 发 生 时, 向 主 线 程 发 送
    WM_COMMNOTIFY 消 息( 由 于WIN95 取 消 了WM_COMMNOTIFY 消 息, 因 此 必 须 自 己 创
    建), 主 线 程 对 其 进 行 处 理。 若 不 需 要WM_COMMNOTIFY 消 息, 则 主 线 程 终 止 通
    信 监 视 线 程。
    ---- 多 线 程 同 时 执 行, 将 会 引 起 对 共 享 资 源 的 冲 突。 为 避 免 冲 突, 就 要 用 同
    步 多 线 程 对 共 享 资 源 进 行 访 问。WIN95 提 供 了 许 多 保 持 线 程 同 步 的 方 法, 笔
    者 采 用 创 建 事 件 对 象 来 保 持 线 程 同 步。 通 过CraeteEvent() 创 建 事 件 对 象, 使
    用SetEvent() 或PulseEvent() 函 数 将 事 件 对 象 设 置 成 信 号 同 步。 在 应 用 程 序 中,
    利 用WaitSingleObject() 函 数 等 待 同 步 的 触 发, 等 到 指 定 的 事 件 被 其 它 线 程 设 置
    为 有 信 号 时, 才 继 续 向 下 执 行 程 序。
    ---- Delphi 下 的 具 体 实 现 方 法
    ---- Delphi 的 强 大 功 能 和 支 持 多 线 程 的 面 向 对 象 编 程 技 术, 使 得 实 现 串 行 通 信
    非 常 简 单 方 便。 它 通 过 调 用 外 部 的API 函 数 来 实 现, 主 要 步 骤 如 下: 首 先, 利
    用CreateFile 函 数 打 开 串 行 口, 以 确 定 本 应 用 程 序 对 此 串 行 口 的 占 有 权, 并 封
    锁 其 它 应 用 程 序 对 此 串 口 的 操 作; 其 次, 通 过GetCommState 函 数 填 充 设 备 控 制
    块DCB, 再 通 过 调 用SetCommState 函 数 配 置 串 行 口 的 波 特 率、 数 据 位、 校 验 位 和
    停 止 位。 然 后, 创 建 串 行 口 监 视 线 程 监 视 串 行 口 事 件。 在 此 基 础 上 就 可 以
    在 相 应 的 串 口 上 操 作 数 据 的 传 输; 最 后, 用CloseHandle 函 数 关 闭 串 行 口。 具 体
    的 程 序 如 下, 本 程 序 用Delphi3.0 编 制 在Win95 环t 境 下 调 试 通 过, 已 投 入 实 际 应 用
    中, 供 广 大 读 者 参 考。
    ---- 程 序:
    unit comdemou;
    interface
    uses
      Windows, Messages, SysUtils, Classes,
    Graphics, Controls, Forms, Dialogs;
    const
         Wm_commNotify=Wm_User+12;
    type
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        Procedure comminitialize;
    Procedure MsgcommProcess(Var
    Message:Tmessage); Message Wm_commnotify;
        { Private declarations }
      public
        { Public declarations }
      end;
      //线程声明
      TComm=Class(TThread)
      protected
         procedure Execute;override;
      end;
    var
      Form1: TForm1;
      hcom,Post_Event:Thandle;
      lpol:Poverlapped;
    implementation
    {$R *.DFM}
    Procedure TComm.Execute; //线程执行过程
    var
    dwEvtMask:Dword;
    Wait:Boolean;
    Begin
    fillchar(lpol,sizeof(toverlapped),0);
    While True do Begin
          dwEvtMask:=0;
          Wait:=WaitCommEvent(hcom,dwevtmask,lpol); 
     //等待串行口事件;
          if Wait Then Begin
             waitforsingleobject(post_event,infinite);
    //等待同步事件置位;
             resetevent(post_event);  //同步事件复位;
             PostMessage(Form1.Handle,
    WM_COMMNOTIFY,0,0);//发送消息;
             end;
          end;
    end;
    procedure Tform1.comminitialize; 
    //串行口初始化
    var
    lpdcb:Tdcb;
    Begin
    hcom:=createfile('com2',generic_read or
     generic_write,0,nil,open_existing,
    file_attribute_normal or
     file_flag_overlapped,0);//打开串行口
        if hcom=invalid_handle_value then
        else
            setupcomm(hcom,4096,4096);
    //设置输入,输出缓冲区皆为4096字节
            getcommstate(hcom,lpdcb);
    //获取串行口当前默认设置
            lpdcb.baudrate:=2400;
            lpdcb.StopBits:=1;
            lpdcb.ByteSize:=8;
            lpdcb.Parity:=EvenParity;     //偶校验
            Setcommstate(hcom,lpdcb);
            setcommMask(hcom,ev_rxchar);
    //指定串行口事件为接收到字符;
    end;
    Procedure TForm1.Msgcomm
    Process(Var Message:Tmessage);
    var
    Clear:Boolean;
    Coms:Tcomstat;
    cbNum,ReadNumber,lpErrors:Integer;
    Read_Buffer:array[1..100]of char;
    Begin
    Clear:=Clearcommerror(hcom,lpErrors,@Coms);
    if Clear Then Begin
       cbNum:=Coms.cbInQue;
       ReadFile(hCom,Read_Buffer,
    cbNum,ReadNumber,lpol);
       //处理接收数据
       SetEvent(Post_Event);   
    //同步事件置位
       end;
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    comminitialize;
    post_event:=CreateEvent
    (nil,true,true,nil); //创建同步事件;
    Tcomm.Create(False);  
    //创建串行口监视线程;
    end;
    end.
      

  2.   


    --------------------------------------------------------------------------------
        用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;
       (江西 万雪勇) 
    -------------------------------------------------------------------------------- 
    Delphi中串口通信的实现
     
    河南省计算中心 张海航Delphi是一种具有功能强大、简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选择 Delphi作为开发工具编制各种应用程序。但是,美中不足之处是 Delphi没有自带的串口通信控件,在它的帮助文档里也没有提及串口通信,这就给编制通信程序的开发人员带来许多不便。
     
    目前,利用 Delphi实现串口通信的常用的方法有 3种:一是利用控件,如 MSCOMM控件和 SPCOMM控件;二是使用 API函数;三是调用其他串口通信程序。其中利用 API编写串口通信程序较为复杂,需要掌握大量的通信知识。相比较而言,利用 SPCOMM控件则相对较简单,并且该控件具有丰富的与串口通信密切相关的属性及事件,提供了对串口的各种操作,而且还支持多线程。下面本文结合实例详细介绍 SPCOMM控件的使用。
     
    SPCOMM的安装
     
    1.选择下拉菜单 Component中的 Install Component选项,弹出窗口。
     
    在 Unit file name处填写 SPCOMM控件所在的路径,其他各项可用默认值,点击 OK按钮。
     
    2.安装后,在 System控件面板中将出现一个红色控件 COM。现在就可以像 Delphi自带控件一样使用 COM控件了。
     
    SPCOMM的属性、方法和事件
     
    1.属性
     
    ●CommName:表示 COM1、 COM2等串口的名字;
     
    ●BaudRate:根据实际需要设定的波特率,在串口打开后也可更改此值,实际波特率随之更改;
     
    ●ParityCheck:表示是否需要奇偶校验;
     
    ●ByteSize:根据实际情况设定的字节长度;
     
    ●Parity:奇偶校验位;
     
    ●StopBits:停止位;
     
    ●SendDataEmpty:这是一个布尔型属性,为 true时表示发送缓存为空,或者发送队列里没有信息;为 false时表示发送缓存不为空,或者发送队列里有信息。
     
    2.方法
     
    ●Startcomm方法用于打开串口,当打开失败时通常会报错。错误主要有 7种:⑴串口已经打开;⑵打开串口错误;⑶文件句柄不是通信句柄;⑷不能够安装通信缓存;⑸不能产生事件;⑹不能产生读进程;⑺不能产生写进程;
     
    ●StopComm方法用于关闭串口,没有返回值;
     
    ●WriteCommData(pDataToWrite: PChar;dwSizeofDataToWrite:Word )方法是个带有布尔型返回值的函数,用于将一个字符串发送到写进程,发送成功返回 true,发送失败返回 false。执行此函数将立即得到返回值,发送操作随后执行。该函数有两个参数,其中 pDataToWrite是要发送的字符串, dwSizeofDataToWrite是发送字符串的长度。
     
    3.事件
     
    ●OnReceiveData :procedure (Sender: TObject;Buffer: Pointer;BufferLength: Word) of object
     
    当有数据输入缓存时将触发该事件,在这里可以对从串口收到的数据进行处理。 Buffer中是收到的数据, BufferLength是收到的数据长度。
     
    ●OnReceiveError : procedure(Sender: TObject; EventMask : DWORD)
     
    当接收数据出现错误时将触发该事件。
     
    SPCOMM的使用
     
    下面是一个利用 SPCOMM控件的串口通信的例子。
     
    以实现 PC机与单片机 8051之间的通信为例,首先要调通它们之间的握手信号。假定它们之间的通信协议是: PC到 8051一帧数据 6个字节, 8051到 PC一帧数据也为 6个字节。当 PC发出( F0,01,FF,FF,01,F0)后 8051能收到一帧( F0,01,FF,FF,01,F0),表示数据通信握手成功,两者之间就可以按照协议相互传输数据。
     
    创建一个新的工程 COMM.DPR,把窗体的 NAME属性定为 FCOMM,把窗体的标题定义为测试通信,按照图 2添加控件 (图 2中黑色矩形围住的控件即为 COMM1)。
     
     
     
    图 2
     
    1.设定 COMM1属性:
     
    ●波特率: 4800;
     
    ●奇偶校验位:无;
     
    ●字节长度: 8;
     
    ●停止位: 1;
     
    ●串口: COM1。
      

  3.   


    Memo1中将显示发送和接收的数据。将新的窗体存储为 Comm.pas。
     
    2.编写源代码
     
    //变量说明
     
    var
     
    fcomm: TFCOMM;
     
    viewstring:string;
     
    i:integer;
     
    rbuf,sbuf:array[16] of byte;
     
    //打开串口
     
    procedure TFCOMM.FormShow(Sender: TObject);
     
    begin
     
    comm1.StartComm;
     
    end;
     
    //关闭串口
     
    procedure TFCOMM.FormClose(Sender: TObject; var Action: TCloseAction);
     
    begin
     
    comm1.StopComm;
     
    end;
     
    //自定义发送数据过程
     
    procedure senddata;
     
    var
     
    i:integer;
     
    commflg:boolean;
     
    begin
     
    viewstring:=‘’ ;
     
    commflg:=true;
     
    for i:=1 to 6 do
     
    begin
     
    if not fcomm.comm1.writecommdata(@sbuf[i],1) then
     
    begin
     
    commflg:=false;
     
    break;
     
    end;
     
    //发送时字节间的延时
     
    sleep(2);
     
    viewstring:=viewstring+ inttohex(sbuf[i],2)+‘’ ; end;
     
    viewstring:=‘发送’+ viewstring;
     
    fcomm.memo1.lines.add(viewstring);
     
    fcomm.memo1.lines.add(‘’ );
     
    if not commflg then messagedlg(‘发送失败 !’ ,mterror,[mbyes],0);
     
    end;
     
    //发送按钮的点击事件
     
    procedure TFCOMM.Btn_sendClick(Sender: TObject);
     
    begin
     
    sbuf[1]:=byte($ f0); //帧头
     
    sbuf[2]:=byte($ 01); //命令号
     
    sbuf[3]:=byte($ ff);
     
    sbuf[4]:=byte($ ff);
     
    sbuf[5]:=byte($ 01);
     
    sbuf[6]:=byte($ f0); //帧尾
     
    senddata;//调用发送函数
     
    end;
     
    //接收过程
     
    procedure TFCOMM.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;BufferLength: Word);
     
    var
     
    i:integer;
     
    begin
     
    viewstring:=‘’ ;
     
    move(buffer^,pchar(@rbuf^),bufferlength);
     
    for i:=1 to bufferlength do
     
    viewstring:=viewstring+ inttohex(rbuf[i],2)+‘’ ;
     
    viewstring:=‘接收’+ viewstring;
     
    memo1.lines.add(viewstring);
     
    memo1.lines.add(‘’ );
     end;
     
    如果 memo1上显示发送 F0 01 FF FF 01 F0和接收到 F0 01 FF FF 01 F0,这表示串口已正确地发送出数据并正确地接收到数据,则串口通信成功。
      

  4.   

    多谢!
    我是想知道,是否把字符串放入INPUT就是已经发送到与之相连的设备上,也就是说,这边INPUT,就可以在那边的OUTPUT中取数据了!
      

  5.   

    那可不一定;送入发送缓冲区不代表一定能正确传输。你用双串口一个发送一个接收,这个时候只要设置正确,一个放入INPUT,另一个应该可以在OUTPUT中取到。在通信过程中发生错误或事件,就会引发OnComm事件。用事件的方式解决接收会更好。  另一种是时间方式,用sleep()一段时间以后也可从缓冲区中接收,但是这样有时不易控制。