Notes on the documentation

BECKHOFF Automation: Foreword

文档编制说明

本说明书仅供那些在控制与自动化工程领域中熟悉国家标准并受过培训专业人士使用。

在安装和调试这些组件时，操作人员须遵循下列说明和解释。

相关负责人员必须确保所述产品的应用或使用满足所有安全要求，包括相关法律、法规、准则和标准。

免责声明

此文档需注意的是：尽管本文档经过精心编制，但所述的产品正处在不断地更新换代中。

因此，我们并不能保证文档中所描述的产品性能数据、标准或其它特性与实际产品相一致。

倘若，本文档出现一些技术或编辑上的错误，我们时刻保留对文档信息作出修改的权力，且恕不另行通知。

对于所提供产品，如果在本文档中已对数据、图表以及说明进行了修改，我们将不再特别加以声明。

商标

Beckhoff®, TwinCAT®, EtherCAT®商标以及EtherCAT®, TwinSAFE® 和XFC® 的安全商标已为Beckhoff 自动化有限公司的注册商标并已由Beckhoff 自动化有限公司授权。

未经许可，任何在公共场合以盈利为目的使用本公司的商标，本公司将依法追究。

申请中的专利

TwinCAT技术已被注册，包含但不局限于如下专利申请和专利：

在许多其他国家有关EP0851348, US6167425的相关申请和注册。

版权

© Beckhoff 自动化有限公司

未经许可，任何人不得复制、分发和使用本手册以及内容传递。
 违者将追究损害赔偿，倘若批准一个专利、使用模式或设计，本公司保留所有权利。


Introduction

TwinCAT I/O To Ring 3 Applications

TwinCAT I/O接口的基本思想是:在不使用ADS通讯的情况下,使Windows程序(Windows NT/2000/XP用户模式或Windows CE应用程序)能够直接访问TwinCAT I/O子系统.

因此,提供一个拥有一系列接口函数的用户模式DLL来触发现场总线I/O口并和TwinCAT I/O子系统交换过程数据.

在标准TwinCAT下通过TwinCAT System Manager来完成相关配置.

这个接口DLL被优化为尽可能直接地访问过程映像( process images).通过这个接口,WindowsNT用户模式应用程序便可以与支持TwinCAT 系统的各种类型的现场总线集成(如Lightbus, Profibus, Interbus, CANOpen, DeviceNet, Ethernet,…).

在本文档后续章节中，提供了与现场总线I/O相关的参考例子.


System Requirements

TwinCAT I/O To Ring 3 Applications

系统需求

Windows NT:

- TwinCAT 2.5 或更高版本

- TCatIoDrv.dll

- TCatIoApi.h

- TCatIoDrv.lib

Windows CE:

- Image Version 1.90 or 或更高版本

- TwinCAT I/O 相关文件 : TCatIoW32Api.h, TCatIoDrvW32.lib

- TwinCAT Timer 相关文件(可选) : TcTimerAPI.h, TcTimerWrap.lib

- TwinCAT CE "Run as Device" 项必须选中. 可通过CE image的"CxConfig Tool"来完成该配置.

完整代码点这里


TCatIoOpen

TCatIoOpen函数的作用是开通一个到TwinCAT I/O服务器的连接. 在任何I/O处理启动之前,都需调用TCatIoOpen函数.

LRESULT TCatIoOpen();

参数

无

返回值

如果函数执行失败,返回数值 -1.

备注

TCatIoOpen函数将会分配内存和必要的系统资源.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoClose ()

请注意:

系统需求


TCatIoClose

TCatIoClose函数的作用是关闭一个到TwinCAT I/O服务器的连接. 在结束程序之前,需调用TCatIoClose函数来避免系统资源的丢失.

LRESULT TCatIoClose();

参数

无

返回值

如果函数执行失败,返回数值为非0.

备注

TCatIoClose函数将会释放被分配的内存和系统资源.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoOpen ()


TCatIoInputUpdate

TCatIoInputUpdate 函数的作用是更新输入内存中的数据.

LRESULT TCatIoInputUpdate(
  USHORT nPort
);

参数

nPort

nPort对应一个ADS设备,nPort是用来确定使用哪一个I/O任务的内存映射来存储输入数据.

更多相关信息请参见TwinCAT System Manager - Addtional Tasks.

返回值

如果函数执行失败,返回数值为非0.

错误码: -1

表示: 没有初始化I/O连接

IOERR_IOSTATEBUSY [0x2075]: I/O设备未准备就绪

备注

TCatIoInputUpdate函数检查输入设备是已准备就绪. 启动现场总线的I/O循环时需调用函数TCatIoOutputUpdate.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoOutputUpdate ()


TCatIoOutputUpdate

TCatIoOutputUpdate 函数的作用是更新输出内存中的数据.

LRESULT TCatIoOutputUpdate(
  USHORT nPort
);

参数

nPort

nPort对应着一个ADS设备,nPort是用来确定使用哪一个I/O任务的内存映射来传送输出数据.更多相关信息请参见TwinCAT System Manager - Addtional Tasks.

返回值

如果函数执行失败,返回数值为非0.

错误码:

-1: 没有初始化I/O连接

IOERR_IOSTATEBUSY [0x2075]: I/O设备未准备就绪

备注

TCatIoOutputUpdate 函数检查I/O设备是否处于准备状态. 如果I/O设备已经准备就绪, TCatIoOutputUpdate 函数将输出数据写入设备并且启动现场总线的I/O循环.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoInputUpdate()


TCatIoGetOutputPtr

TCatIoGetOutputPtr函数为输出内存映像分配一个输出缓冲区.

LRESULT TCatIoGetOutputPtr (
  USHORT nPort,
  VOID** ppOutp,
  int nSize
);

参数

nPort

nPort对应一个ADS设备,nPort是用来确定使用哪一个内存映射来传送输出数据.

更多相关信息请参见TwinCAT System Manager - Addtional Tasks.

ppOutp

得到输出缓冲区的地址.如果TCatIoGetOutputPtr函数执行成功,则ppOutp指针被赋予输出缓冲区的地址.

nSize

输出缓冲区的字节长度.

返回值

如果函数执行失败,返回数值为非0.

备注

TCatIoGetOutputPtr 函数为内存映像分配一个缓冲区并返回指针ppOutp,该指针指向所分配缓冲区的地址.如果缓冲区在之前已经分配,则TCatIoGetOutputPtr返回已分配缓冲区的地址.输出数据在缓冲区中传输.

当用户进程运行时,如果停止或重启TwinCAT,尽管此时I/O数据传输暂停了,但是数据传输缓冲区不受影响.TwinCAT重启完成后,不需任何额外处理,用户进程便可继续执行.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoGetInputPtr ()


TCatIoGetInputPtr

TCatIoGetInputPtr 函数为输入内存映像分配一个输入缓冲区..

LRESULT TCatIoGetInputPtr (
  USHORT nPort,
  VOID** ppInp,
  int nSize
);

参数

nPort

nPort对应一个ADS设备,nPort是用来确定使用哪一个内存映射来传送输出数据.

更多相关信息请参见TwinCAT System Manager - Addtional Tasks.

ppInp

得到输出缓冲区的地址.如果TCatIoGetInputPtr函数执行成功,则 ppInp指针被赋予输入缓冲区的地址.

nSize

输出缓冲区的字节长度.

返回值

如果函数执行失败,返回数值为非0.

备注

TCatIoGetInputPtr 函数为内存映像分配一个缓冲区并返回指针ppInp. 该指针指向所分配缓冲区的地址.如果缓冲区在之前已经分配,则TCatIoGetInputPtr 返回已分配缓冲区的地址. 输入数据在缓冲区中传输.

当用户进程运行时,如果停止或重启TwinCAT,尽管此时I/O数据传输暂停了,但是数据传输缓冲区不受影响.TwinCAT重启完成后,不需任何额外处理,用户进程便可继续执行.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoGetOutputPtr ()


TCatIoReset

TCatIoReset function函数的作用是重置TwinCAT System中的所有I/O设备.

LRESULT TCatIoReset();

参数

无

返回值

  • Win32: 如果函数执行失败,返回数值为非0.
  • Win CE: 如果返回值为:True (1),表示函数执行成功, FALSE (0)表示函数执行失败.

备注

TCatIoReset函数通过AdsWriteControl 命令来重置I/O设备.因此,仅当TwinCAT System处于运行模式时,TCatIoReset 函数才能执行成功.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.


TCatIoGetCpuTime

TCatIoGetCpuTime函数返回当前CPU时间.

LRESULT TCatIoGetCpuTime( __int64* pnTime );

参数

pnTime

该参数是一个64位整型变量,以100ns为单位,接收当前CPU时间.

返回值

如果函数执行失败,返回数值为非0.

备注

TCatIoGetCpuTime函数每隔100Ns,读取CPU的当前时间.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoGetCpuCounter ()


TCatIoGetCpuCounter

TCatIoGetCpuCounter 函数返回CPU当前计数值.

LRESULT TCatIoGetCpuCounter( __int64* pnCount );

参数

pnCount

该参数是一个64位整型变量,用来接收当前CPU计数值.

返回值

如果函数执行失败,返回数值为非0.

备注

TCatIoGetCpuCounter 函数读取CPU计数值.

相关信息

Windows NT:

Windows NT 版本: 4.0 或更高版本.

TwinCAT: 2.5或更高版本.

Header: TCatIoApi.h.

Library: TCatIoDrv.lib.

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Library: TCatIoDrvW32.lib.

另请参阅

TCatIoGetCpuTime ()


TcTimerInitialize

TcTimerInitialize函数的作用是初始化TwinCAT Timer.在使用TcTimer之前,必须先调用TcTimerInitialize函数.

DWORD TcTimerInitialize();

参数

无

返回值

STATUS_SUCCESS - 表示函数执行成功.

STATUS_UNSUCCESSFUL - 表示函数执行失败.

备注

无

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TcTimerAPI.h

Library: TcTimerWrap.lib


TcTimerDeinitialize

TcTimerDeinitialize函数的作用是复原TwinCAT Timer.

DWORD TcTimerDeinitialize();

参数

无

返回值

STATUS_SUCCESS - 表示函数执行成功.

STATUS_UNSUCCESSFUL - 表示函数执行失败.

备注

无

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TcTimerAPI.h

Library: TcTimerWrap.lib


TcTimerSetEvent

该函数启动一个timer事件.TwinCAT Timer运行于一个独立的线程.激活timer事件后,随之会产生一个对应的事件对象.

DWORD TcTimerSetEvent(
  UINT uDelay,
  LPTSTR lpEventName,
  UINT fuEvent
);

参数

uDelay

TcTimer 事件周期.

lpEventName

指向一个空字符串,该字符串存放事件对象的名字. 该名称最长不超过MAX_PATH 个字符,且可包含除了反斜杠路径分隔符（\）字符外的任意字符.该名称区分大小写.(注意:以"TC_ "开头的事件名称为Beckhoff保留使用）

fuEvent

Timer事件类型.下表所列出的事件类型均可作为fuEvent的参数.

Timer事件类型 | 描述
TIME_ONESHOT | 仅产生一次Timer Event.uDelay后仅触发一次Timer事件.
TIME_PERIODIC | 在每个uDelay tick后,产生一次Timer Event.
TIME_CALLBACK_EVENT_SET | 当timer到期时,系统通过lpEventName参数调用SetEvent()函数设置事件. 系统调用SetEvent()函数设置事件名称为参数lpEventName的值.
TIME_CALLBACK_EVENT_PULSE | 当timer到期时,系统通过lpEventName参数调用PulseEvent()函数设置事件.系统调用PulseEvent()函数设置事件名称为参数lpEventName的值.

返回值

如果函数执行成功,则函数返回timer事件的标识码.

如果函数执行失败或timer事件未被创建,则函数返回NULL.

备注

调用TcTimerSetEvent函数时,要同时调用相应的TcTimerKillEvent函数.

通常在 "TwinCAT System Service"中设置TcTimer Ticks.我们可以在"TwinCAT System Manager"中 Real Time Settings选项中的"Base Ticks"中设置.设置完毕后,要激活当前设置.

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TcTimerAPI.h

Library: TcTimerWrap.lib


TcTimerKillEvent

该函数的作用是注销timer事件.

DWORD TcTimerKillEvent(
  UINT uTimerId
);

参数

uTimerId

取消timer事件的标识符.当timer事件被创建后,TcTimerSetEvnet 函数会返回timer事件的标识符.

返回值

STATUS_SUCCESS - 表示函数执行成功.

STATUS_UNSUCCESSFUL - 表示函数执行失败.

备注

无

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TcTimerAPI.h

Library: TcTimerWrap.lib


TcTimerGetTickCount

TwinCAT I/O To Ring 3 Applications : TcTimer for Windows CE

TcTimerGetTickCount.

TcTimerGetTickCount 函数的作用是返回当前TwinCAT Timer的Tick计数值.

DWORD TcTimerGetTickCount();

参数

无

返回值

TwinCAT Timer的Tick计数值.

备注

通常在 "TwinCAT System Service"中设置TcTimer Ticks.我们可以在"TwinCAT System Manager"中 Real Time Settings选项中的"Base Ticks"中设置.设置完毕后,要激活当前设置.

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TcTimerAPI.h

Library: TcTimerWrap.lib


TcTimerGetTickTime

TcTimerGetTickTime函数返回TwinCAT Timer的tick时间.

DWORD TcTimerGetTickTime();

参数

无

返回值

以100ns为单位,返回TwinCAT Timer的tick时间.(例如,如果Base tick是500µs,则返回5000)

备注

通常在 "TwinCAT System Service"中设置TcTimer Ticks.我们可以在"TwinCAT System Manager"中 Real Time Settings选项中的"Base Ticks"中设置.设置完毕后,要激活当前设置.

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TcTimerAPI.h

Library: TcTimerWrap.lib


TCatGetState

TCatGetState函数返回当前TwinCAT系统中ADS的状态.

LRESULT TCatGetState();

返回值

当前TwinCAT系统中ADS的状态.

如果函数执行失败,函数返回值为ADSSTATE_ERROR.

相关信息

Windows CE:

Windows CE 版本: 4.2 或更高版本.

Image 版本: 1.90或更高版本.

Header: TCatIoW32Api.h.

Import Library: TCatIoDrvW32.lib.


TwinCAT I/0 Ring 3 DLL: Delphi Application

TwinCAT I/O

TwinCAT I/0 Ring 3 DLL: Delphi 应用程序

在该示例中,TcatIoDrv DLL中的函数被移植到 Pascal 中且被封装为TCatIoDrv.pas.

本例源程序: TwinCAT I/O Ring 3 DLL Delphi Application.

系统需求:

-TwinCAT 2.5 or higher

-TCatIoDrv.Dll

-Delphi 5.0

说明

Delphi 应用程序触发TwinCAT System Manager中一个additional task 的内存映像的输入/输出映射.循环时间为100ms,且其由多媒体时钟产生.两个列表框显示了内存映像的实时值.通过I/O Reset按钮可以复位TwinCAT I/O 设备. 通过Start I/O cycle和Stop I/O cycle按钮可以启停输入/输出映射.如果出现错误，则将其信息输出到另一个列表框.

TwinCAT System Manager 中的Task Configuration

TwinCAT System Manager中additional task 1的端口号为301.

其内存映像大小为:

输入映像: 2 bytes;

输出映像: 8 bytes;

端口号和内存映像的大小在TCatIoDrv.pas中被定义为常量,而且它们的值必须相对应.

链接 TCatIoDrv

在Pascal 组件TCatIoDrv.pas中有TCatIoDrv.DLL函数的声明.通过"uses"语句将其添加进工程.

unit TCatIoDrvDelphiUnit;

interface

uses
TCatIoDrv,
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, Grids;
...

TCatIoDrv.pas组件

unit TCatIoDrv;

interface
uses sysutils,windows;

{$A-}

const
    // TwinCAT® System Manager->Additional Tasks->Task 1 tab:
    TASK_1_PORTNUMBER           = 301;

    // TwinCAT® System Manager->Additional Tasks->Task 1-Image->Size/Offset tab:
    MAX_INPUT_IMAGE_BYTESIZE    = 2;
    MAX_OUTPUT_IMAGE_BYTESIZE   = 8;

type

    TInputImage     = ARRAY[ 0 .. MAX_INPUT_IMAGE_BYTESIZE - 1 ] OF Byte;
    TOutputImage    = ARRAY[ 0 .. MAX_OUTPUT_IMAGE_BYTESIZE - 1 ] OF Byte;
    PInputImage     = ^TInputImage;
    POutputImage    = ^TOutputImage;

    function TCatIoOpen:longint; stdcall; external 'TCatIoDrv.dll' name '_TCatIoOpen@0';
    function TCatIoClose:longint;stdcall; external 'TCatIoDrv.dll' name '_TCatIoClose@0';
    function TCatIoInputUpdate( nPort : WORD ):longint;  stdcall; external 'TCatIoDrv.dll' name '_TCatIoInputUpdate@4';
    function TCatIoOutputUpdate( nPort : WORD ):longint;  stdcall; external 'TCatIoDrv.dll' name  '_TCatIoOutputUpdate@4';
    function TCatIoGetInputPtr( nPort : WORD; var pInput :Pointer; nSize :longint ):longint;   stdcall; external 'TCatIoDrv.dll' name  '_TCatIoGetInputPtr@12';
    function TCatIoGetOutputPtr( nPort : WORD; var pOutput :Pointer; nSize :longint ):longint;  stdcall; external 'TCatIoDrv.dll' name  '_TCatIoGetOutputPtr@12';
    function TCatIoReset():longint;  stdcall; external 'TCatIoDrv.dll' name   '_TCatIoReset@0';
    function TCatIoGetCpuTime( var  pCpuTime : TFileTime ):longint;  stdcall; external 'TCatIoDrv.dll' name  '_TCatIoGetCpuTime@4';
    function TCatIoGetCpuCounter( var pCpuCount : int64 ):longint;  stdcall; external 'TCatIoDrv.dll' name  '_TCatIoGetCpuCounter@4';

implementation
initialization
finalization

end.

应用程序

事件函数 FormCreate 调用 DLL 函数 TcatIoOpen. 如果成功,函数返回为空,而且已经建立了一个与TwinCAT I/O子系统的连接.如果出现错误,通过列表框将其信息传给用户. 当应用程序关闭时,应将与TwinCAT I/O子系统的连接移除. 这在 FormDestroy 事件函数中调用 DLL函数 TCatIotClose来完成.一旦连接建立成功,应当调用另两个函数 : TCatIoGetInputPtr and TCatIoGetoutputPtr. 这些函数返回指向输入/输出内存映像的指针.这些指针可用于读输入或写输出.

unit TCatIoDrvDelphiUnit;

interface

uses
    TCatIoDrv,
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Grids;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Timer1: TTimer;
    StartButton: TButton;
    StopButton: TButton;
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    ListBox2: TListBox;
    ListBox3: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure StartButtonClick(Sender: TObject);
    procedure StopButtonClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure InitControls();
    procedure CalculateNewOutputs(pData   : Pointer; cbSize   :integer);
    procedure ViewData( var ListBox : TListBox; pData   : Pointer; cbSize   :integer);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

  InputImagePtr      :PInputImage;
  OutputImagePtr     :POutputImage;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var Result  : integer;
    InPtr, OutPtr :Pointer;
begin
    InitControls();

    Result := TCatIoOpen();
    if ( Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOpen() error:%d', [ Result ] ) )
    else
    begin
        {get/initialize pointer to the input image}
        InPtr := NIL;
        Result := TCatIoGetInputPtr( TASK_1_PORTNUMBER, InPtr, MAX_INPUT_IMAGE_BYTESIZE );
        if (  Result = 0 ) And ( InPtr <> NIL ) then
            InputImagePtr := InPtr
        else
            ListBox1.Items.Insert(0, Format('TCatIoGetInputPtr() error:%d', [ Result ] ) );

        {get/initialize pointer to the output image}
        OutPtr := NIL;
        Result := TCatIoGetOutputPtr( TASK_1_PORTNUMBER, OutPtr, MAX_OUTPUT_IMAGE_BYTESIZE );
        if (  Result = 0 ) And ( OutPtr <> NIL ) Then
            OutputImagePtr := OutPtr
        else
            ListBox1.Items.Insert(0, Format('TCatIoGetOutputPtr() error:%d', [ Result ] ) );
    end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var Result : integer;
begin
    Result := TCatIoClose();
    if ( Result <> 0 ) then
        MessageBox(0, 'TCatIoClose() error!', 'Error', MB_OK);
end;

当多媒体时钟处于激活状态时,Timer1Timer事件处理程序被周期地调用,其每次调用以下函数:

  • TCatIoInputUpdate (DLL 函数: 对输入内存映像进行一次更新（读输入）);
  • ViewData (过程: 在一个列表框内显示输入的当前值);
  • CalculateNewOutputs (过程: 计算/改变输出值);
  • TCatIoOutputUpdate (DLL 函数:对输出内存映像进行一次更新（写输出）);
  • ViewData (过程:在一个列表框内显示输出的当前值);
  • TCatIoGetCpuCounter (DLL 函数:读CPU计数器的当前值);
  • TCatIoGetCpuTime (DLL 函数:读 Pentium CPU的计数器状态);

procedure TForm1.Timer1Timer(Sender: TObject);
var Result : integer;
    CpuCounter  :int64;
    FileTime    :TFileTime;
    SystemTime  :TSystemTime;
begin
    Result := 0;

    try

        {Update input image}
        Result := TCatIoInputUpdate( TASK_1_PORTNUMBER );
        if ( Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoInputUpdate() error:%d', [ Result ] ) );

        {View inputs}
        ViewData( ListBox2, InputImagePtr, sizeof(TInputImage) );

        {Calculate new output values (running light)}
        CalculateNewOutputs( OutputImagePtr, sizeof(TOutputImage));

        {Update output image}
        Result := TCatIoOutputUpdate( TASK_1_PORTNUMBER );
        if ( Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoOutputUpdate() error:%d', [ Result ] ) );

        {View outputs}
        ViewData( ListBox3, OutputImagePtr, sizeof(TOutputImage) );

    except
        Timer1.Enabled := false;
        ListBox1.Items.Insert(0, Format('TCatIoDrv exception error:%d', [ Result ] ) );
    end;

    Result := TCatIoGetCpuCounter(CpuCounter);
    Label4.Caption := Format('TCatIoGetCpuCounter() result:%d, CPU counter:0x%x', [Result,CpuCounter] );

    Result := TCatIoGetCpuTime(FileTime);
    FileTimeToSystemTime( FileTime, SystemTime );
    Label5.Caption := Format('TCatIoGetCpuTime() result:%d, CPU time: %d:%d:%d:%d',
        [Result,SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds]);

end;

激活/禁用多媒体时钟:

procedure TForm1.StartButtonClick(Sender: TObject);
begin
    StartButton.Enabled := false;
    StopButton.Enabled := true;
    Timer1.Enabled := true;
end;

procedure TForm1.StopButtonClick(Sender: TObject);
begin
    StartButton.Enabled := true;
    StopButton.Enabled := false;
    Timer1.Enabled := false;
end;

复位 I/O :

procedure TForm1.Button1Click(Sender: TObject);
var Result : integer;
begin
    Result := TCatIoReset();
    if ( Result <> 0 ) Then
        ListBox1.Items.Insert( 0, Format('TCatIoReset() error:%d', [ Result ] ) );
end;

procedure TForm1.InitControls();
var Row   :integer;
begin
    StartButton.Enabled := true;
    StopButton.Enabled := false;

    Timer1.Enabled := false;
    Timer1.Interval := 100; {100 ms}

    for Row:= 0 To MAX_INPUT_IMAGE_BYTESIZE - 1 do
        ListBox2.Items.Add( Format( 'Byte[%d]', [Row] ) );

    for Row:= 0 To MAX_OUTPUT_IMAGE_BYTESIZE - 1 do
        ListBox3.Items.Add( Format( 'Byte[%d]', [Row] ) );
end;

procedure TForm1.CalculateNewOutputs(pData   : Pointer; cbSize   :integer);
var i:integer;
    pByte : ^Byte;
begin
    if ( pData <> NIL ) And (cbSize > 0) then
    begin
        for i:= 0 to  cbSize - 1 do
        begin
            pByte := Pointer(Integer(pData) + i);
            if ( pByte^ = 0 ) then pByte^ := 1
            else  pByte^ := pByte^ shl 1;
        end;
    end;
end;

procedure TForm1.ViewData( var ListBox : TListBox; pData   : Pointer; cbSize   :integer );
var
  ByteOff    : Integer;
  pByte      : ^Byte;
begin
    if ( pData <> NIL ) And (cbSize > 0) then
    begin
        for ByteOff := 0 to cbSize - 1 do
        begin
            pByte:=  Pointer( integer(pData) + ByteOff );
            ListBox.Items.Strings[ByteOff] := Format('Byte[%d] Value:0x%x',[ ByteOff, pByte^ ] );
        end;
    end;
end;

end.

本例源程序: TwinCAT I/O Ring 3 DLL Delphi Application.


TwinCAT I/O Ring 3 OCX Delphi Application.

TwinCAT I/O

TwinCAT I/O Ring 3 OCX Delphi 应用程序.

本例源程序: TwinCAT I/0 Ring 3 Ocx: Delphi Application.

系统需求:

-TwinCAT 2.7 or higher

-TCatIoOcx.Ocx

-Delphi 5.0

说明

Delphi 应用程序触发TwinCAT System Manager中一个additional task 的内存映像的输入/输出映射.循环时间为100ms,且其由多媒体时钟产生.两个列表框显示了内存映像的实时值.通过I/O Reset按钮可以复位TwinCAT I/O 设备. 通过Start I/O cycle和Stop I/O cycle按钮可以启停输入/输出映射.如果出现错误，则将其信息输出到另一个列表框.

TwinCAT System Manager 中的Task Configuration

TwinCAT System Manager中additional task 1的端口号为301.

其内存映像大小为:

输入映像: 2 bytes;

输出映像: 8 bytes;

端口号和内存映像的大小在TCatIoOcxDelphiUnit.pas中被定义为常量,而且它们的值必须相对应.

链接 TCatIoOcx ActiveX组件

将TCatIoOcx ActiveX组件导入到组件面板,以便其能在Delphi 应用程序中使用.通过菜单命令导入ActiveX组件Component->Import ActiveX Control ....从已安装组件列表中选择TCatIoOcx ActiveX Control Module,并确认Install...

导入成功后便会在 ActiveX组件面板中看到TCatIoOcx.

应用程序

事件函数 FormCreate 调用 DLL 函数 TCatIoOcxOpen. 如果成功,函数返回为空,而且已经建立了一个与TwinCAT I/O子系统的连接.如果出现错误,通过列表框将其信息传给用户. 当应用程序关闭时,应将与TwinCAT I/O子系统的连接移除. 这在 FormDestroy 事件函数中调用 DLL函数 TCatIoOcxClose来完成.一旦连接建立成功,应当调用另两个函数 : TCatIoOcxGetInputPtr and TCatIoOcxGetOutputPtr. 这些函数返回指向输入/输出内存映像的指针.这些指针可用于读输入或写输出.但在本例中并不使用这些指针访问过程数据.而开辟两个数据缓存: InputImage 和 OutputImage.当定义数据缓存时,必须为每个双字类型的过程数据保留 4字节的使用空间.本例中,输入数据缓存区大小为4字节（实际大小为2字节）,输出缓存区大小为12字节（实际大小为8字节）.数据缓存区可以更大,但不可更小.

unit TCatIoOcxDelphiUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Grids, OleCtrls, TCATIOOCXLib_TLB;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Timer1: TTimer;
    StartButton: TButton;
    StopButton: TButton;
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    Label3: TLabel;
    ListBox2: TListBox;
    ListBox3: TListBox;
    TCatIoOcx1: TTCatIoOcx;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure StartButtonClick(Sender: TObject);
    procedure StopButtonClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure InitControls();
    procedure CalculateNewOutputs(pData   : Pointer; cbSize   :integer);
    procedure ViewData( var ListBox : TListBox; pData   : Pointer; cbSize   :integer);
  public
    { Public declarations }
  end;

const
  TASK_1_PORTNUMBER = 301;
    MAX_INPUT_IMAGE_BYTESIZE = 2;
    MAX_OUTPUT_IMAGE_BYTESIZE = 8;

type
    TInputImage       = ARRAY[ 0..MAX_INPUT_IMAGE_BYTESIZE DIV 4 ] Of Integer;
    TOutputImage      = ARRAY[ 0..MAX_OUTPUT_IMAGE_BYTESIZE DIV 4 ] Of Integer;

var
    Form1: TForm1;
  InputImage        :TInputImage;
  OutputImage       :TOutputImage;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var Result  : integer;
    InPtr, OutPtr :Integer;
begin
    InitControls();

    Result := TCatIoOcx1.TCatIoOcxOpen();
    if ( Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOcxOpen() error:%d', [ Result ] ) )
    else
    begin
        {get/initialize pointer to the input image}
        Result := TCatIoOcx1.TCatIoOcxGetInputPtr( TASK_1_PORTNUMBER, InPtr, MAX_INPUT_IMAGE_BYTESIZE );
        if (  Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoOcxGetInputPtr() error:%d', [ Result ] ) );

        {get/initialize pointer to the output image}
        Result := TCatIoOcx1.TCatIoOcxGetOutputPtr( TASK_1_PORTNUMBER, OutPtr, MAX_OUTPUT_IMAGE_BYTESIZE );
        if (  Result <> 0 ) Then
            ListBox1.Items.Insert(0, Format('TCatIoOcxGetOutputPtr() error:%d', [ Result ] ) );
    end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var Result : integer;
begin
    Result := TCatIoOcx1.TCatIoOcxClose();
    if ( Result <> 0 ) then
        MessageBox(0, 'TCatIoOcxClose() error!', 'Error', MB_OK);
end;

当多媒体时钟处于激活状态时,Timer1Timer事件处理程序被周期地调用,其每次调用以下函数:

  • TCatIoOcxInputUpdate (方法: 对输入内存映像进行一次更新（读输入）);
  • TCatIoOcxGetInputData (方法: 将输入数据读入InputImage数据缓存区) ;
  • ViewData (过程: 在一个列表框内显示输入的当前值);
  • CalculateNewOutputs (过程: 计算/改变输出值);
  • TCatIoOcxSetOutputData (方法: 用 OutputImage中的数据设置输出过程数据) ;
  • TCatIoOcxOutputUpdate (方法:对输出内存映像进行一次更新（写输出）);
  • ViewData (过程:在一个列表框内显示输出的当前值);
procedure TForm1.Timer1Timer(Sender: TObject);
var Result : integer;
begin
    try

        {Update input image}
      Result := TCatIoOcx1.TCatIoOcxInputUpdate( TASK_1_PORTNUMBER );
        if ( Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoOcxInputUpdate() error:%d', [ Result ] ) );

        {read input values}
      Result := TCatIoOcx1.TCatIoOcxGetInputData( TASK_1_PORTNUMBER,  InputImage[0], MAX_INPUT_IMAGE_BYTESIZE );
        if ( Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoOcxGetInputData() error:%d', [ Result ] ) );

        {View inputs}
        ViewData( ListBox2, @InputImage, MAX_INPUT_IMAGE_BYTESIZE );

        {Calculate new output values (running light)}
        CalculateNewOutputs( @OutputImage, MAX_OUTPUT_IMAGE_BYTESIZE );

        {write output values}
      Result := TCatIoOcx1.TCatIoOcxSetOutputData( TASK_1_PORTNUMBER,  OutputImage[0], MAX_OUTPUT_IMAGE_BYTESIZE );
        if ( Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoOcxSetOutputData() error:%d', [ Result ] ) );

        {Update output image}
      Result := TCatIoOcx1.TCatIoOcxOutputUpdate( TASK_1_PORTNUMBER );
        if ( Result <> 0 ) then
            ListBox1.Items.Insert(0, Format('TCatIoOcxOutputUpdate() error:%d', [ Result ] ) );

        {View outputs}
        ViewData( ListBox3, @OutputImage, MAX_OUTPUT_IMAGE_BYTESIZE );

    except
        Timer1.Enabled := false;
        ListBox1.Items.Insert(0, 'TCatIoOcx exception error:%d' );
    end;

end;

激活/禁用多媒体时钟:

procedure TForm1.StartButtonClick(Sender: TObject);
begin
    StartButton.Enabled := false;
    StopButton.Enabled := true;
    Timer1.Enabled := true;
end;

procedure TForm1.StopButtonClick(Sender: TObject);
begin
    StartButton.Enabled := true;
    StopButton.Enabled := false;
    Timer1.Enabled := false;
end;

复位 I/O :

procedure TForm1.Button1Click(Sender: TObject);
var Result : integer;
begin
    Result := TCatIoOcx1.TCatIoOcxReset();
    if ( Result <> 0 ) Then
        ListBox1.Items.Insert( 0, Format('TCatIoOcxReset() error:%d', [ Result ] ) );
end;

procedure TForm1.InitControls();
var Row   :integer;
begin
    StartButton.Enabled := true;
    StopButton.Enabled := false;

    Timer1.Enabled := false;
    Timer1.Interval := 100; {100 ms}

    for Row:= 0 To MAX_INPUT_IMAGE_BYTESIZE - 1 do
        ListBox2.Items.Add( Format( 'Byte[%d]', [Row] ) );

    for Row:= 0 To MAX_OUTPUT_IMAGE_BYTESIZE - 1 do
        ListBox3.Items.Add( Format( 'Byte[%d]', [Row] ) );
end;

procedure TForm1.CalculateNewOutputs(pData   : Pointer; cbSize   :integer);
var i:integer;
    pByte : ^Byte;
begin
    if ( pData <> NIL ) And (cbSize > 0) then
    begin
        for i:= 0 to  cbSize - 1 do
        begin
            pByte := Pointer(Integer(pData) + i);
            if ( pByte^ = 0 ) then pByte^ := 1
            else  pByte^ := pByte^ shl 1;
        end;
    end;
end;

procedure TForm1.ViewData( var ListBox : TListBox; pData   : Pointer; cbSize   :integer );
var
  ByteOff    : Integer;
  pByte      : ^Byte;
begin
    if ( pData <> NIL ) And (cbSize > 0) then
    begin
        for ByteOff := 0 to cbSize - 1 do
        begin
            pByte:=  Pointer( integer(pData) + ByteOff );
            ListBox.Items.Strings[ByteOff] := Format('Byte[%d] Value:0x%x',[ ByteOff, pByte^ ] );
        end;
    end;
end;

end.

本例源程序: TwinCAT I/0 Ring 3 Ocx: Delphi Application.


Configuration

TwinCAT I/O To Ring 3 Applications

配置

在TwinCAT System Manager中配置TwinCAT system.

在该例程中,需要完成如下配置:

- I/O device (e.g. Beckhoff FC2001)

- 2个 I/O tasks

- I/O任务的内存映射

- I/O任务和I/O设备之间的 映射


The I/O Device

TwinCAT I/O To Ring 3 Applications

I/O设备

如下图所示添加设备和端子模块: TwinCAT System Manager - I/O Configuration.


The I/O Tasks

TwinCAT I/O To Ring 3 Applications

I/O任务

现在,我们要加载I/O任务.最多可以加载5个任务. 在属性页的右边输入端口号301~305 (5个任务). 请确保"Auto-Start"未被选中.在本例中,实时任务并没有运行,我们仅仅使用了这些任务的内存映射. 更多相关信息请参见TwinCAT System Manager - Addtional Tasks.


The Process Image

TwinCAT I/O To Ring 3 Applications

内存映像

添加了I/O任务后, 要配置每个任务的内存映射. 关于I/O任务的详细信息请参见TwinCAT System Manager文档.

请确保在System Manager中配置的相关数据类型和C++应用程序中的数据相兼容.

System Manager可为每个配置的任务生成一个头文件.

生成的头文件例子如下图所示:

头文件中包含了I/O任务的 ADS端口号, 输入/输出内存映射的大小(以字节)以及内存映射的结构体成员变量.


The Mappings

TwinCAT I/O To Ring 3 Applications

映射

连接变量到相应的现场总线I/O,变量映射的的详细信息请参考 TwinCAT System Manager - Link Variables.

配置完成后,保存至注册表

启动/重启TwinCAT系统.


TwinCAT Sample Program

TwinCAT I/O To Ring 3 Applications

TwinCAT 示例程序

///////////////////////////////////////////////////////////////////////////////
//
// Beckhoff Automation
//
// TwinCAT® I/O sample program
//
///////////////////////////////////////////////////////////////////////////////
#include "stdio.h"
#include "conio.h"
#include "windows.h"
#include "mmsystem.h"
#include "TCatIoApi.h" 	// header file shipped with TwinCAT® I/O
#include "Task1ms.h" 	// TwinCAT® System Manager generated
#include "Task10ms.h" 	// TwinCAT® System Manager generated

#define IOERR_IOSTATEBUSY 			0x2075
#define TASK1MS_DELAY 			1 // ms
#define TASK10MS_DELAY 			10 // ms
#define DELAY_IN_100NS(x) 			(x*10000)

PTask1ms_Outputs 		pT1msOut;
PTask1ms_Inputs 		pT1msIn;
PTask10ms_Outputs 		pT10msOut;
PTask10ms_Inputs 		pT10msIn;

///////////////////////////////////////////////////////////////////////////////
void CALLBACK TimerProc1( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD
dw2 )
{
    static int i=0;
    long nError;
    static __int64 nLastCpuTime=0, nActCpuTime=0;

    TCatIoGetCpuTime( &nActCpuTime ); 	// 0.5 ms, filter extra events
    if ( (nActCpuTime - nLastCpuTime) > (DELAY_IN_100NS(TASK1MS_DELAY)/2) )
    {
        nLastCpuTime =nActCpuTime;
        // first try to get the inputs and test if input update succeeded
        if ( (nError = TCatIoInputUpdate( TASK1MS_PORTNUMBER )) == 0 )
        {
            // do your calculation and logic
            pT1msOut->AnalogOut = pT1msIn->AnalogIn;
            // start the I/O update and field bus cycle
            TCatIoOutputUpdate( TASK1MS_PORTNUMBER );
            }
            else
                printf( "TCatInputUpdate(%d) %d failed with 0x%x !\n",
                    TASK1MS_PORTNUMBER, i++, nError );
        }
    }
///////////////////////////////////////////////////////////////////////////////
void CALLBACK TimerProc2( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD
dw2 )
{
    static int i=0;
    long nError;
    static __int64 nLastCpuTime=0, nActCpuTime=0;

    TCatIoGetCpuTime( &nActCpuTime ); // 5 ms, filter extra events
    if ( (nActCpuTime - nLastCpuTime) > (DELAY_IN_100NS(TASK10MS_DELAY)/2) )
    {
        nLastCpuTime = nActCpuTime;
        // try to get the inputs and test if input update succeeded
        if ( (nError = TCatIoInputUpdate( TASK10MS_PORTNUMBER )) == 0 )
        {
            // optionally test the device state, zero is ok.
            if ( pT10msIn->IoDeviceState != 0 )
                printf( "I Device Error !\n" );
            else
            {
                // do your calculation and logic
                pT10msOut->DigitalOut[0] = pT10msIn->DigitalIn[0];
                pT10msOut->DigitalOut[1] = pT10msIn->DigitalIn[1];
                // start the I/O update and field bus cycle
                TCatIoOutputUpdate( TASK10MS_PORTNUMBER );
            }
        }
        else
            printf("TCatInputUpdate(%d) %d failed with 0x%x !\n",
            TASK10MS_PORTNUMBER, i++, nError );
    }
}
///////////////////////////////////////////////////////////////////////////////
void main()
{
    MMRESULT idTimer1, idTimer2;
    timeBeginPeriod(1);
    // always call TCatIoOpen first.
    if ( TCatIoOpen() == 0 )
    {
        // get the process image pointer
        if ( TCatIoGetOutputPtr(TASK1MS_PORTNUMBER, (void**)&pT1msOut,sizeof(Task1ms_Outputs) ) == 0 &&
            TCatIoGetInputPtr( TASK1MS_PORTNUMBER,(void**)&pT1msIn, sizeof(Task1ms_Inputs) ) == 0 &&
            TCatIoGetOutputPtr(TASK10MS_PORTNUMBER,(void**)&pT10msOut, sizeof(Task10ms_Outputs) )== 0 &&
            TCatIoGetInputPtr(TASK10MS_PORTNUMBER,(void**)&pT10msIn, sizeof(Task10ms_Inputs) )== 0 )
        {
            //do some other initialization
            idTimer1 = timeSetEvent( TASK1MS_DELAY, 0,TimerProc1, 0, TIME_PERIODIC|TIME_CALLBACK_FUNCTION );
            idTimer2 = timeSetEvent(TASK10MS_DELAY, 0, TimerProc2, 0, TIME_PERIODIC|TIME_CALLBACK_FUNCTION );
            //just wait for the end
            getch();
            // events are no longer needed
            timeKillEvent(idTimer1 );
            timeKillEvent( idTimer2 );
        }
        // free resources
        TCatIoClose();
    }
    timeEndPeriod(1);
}


TwinCAT Sample Program

TwinCAT I/O To Ring 3 Applications

TwinCAT 示例程序

TwinCAT I/O - TcTimer Sample Project

///////////////////////////////////////////////////////////////////////////////
//
// Beckhoff Automation
//
// TwinCAT CE ® I/O sample program using TwinCAT Timer.
//
///////////////////////////////////////////////////////////////////////////////
// TcTimerWrap.lib and TCatIoDrvW32.lib must be linked for this project
//
#include "stdio.h"
#include "windows.h"
#include "TCatIoW32Api.h" 	// header file shipped with TwinCAT® I/O
#include "TcTimerApi.h" 	// header file shipped with TwinCAT® I/O
#include "Task1ms.h" 	// TwinCAT® System Manager generated
#include "Task10ms.h" 	// TwinCAT® System Manager generated

#define IOERR_IOSTATEBUSY 			0x2075
#define TASK1MS_DELAY 			1 // Ticks
#define TASK10MS_DELAY 			10 // Ticks
#define DELAY_IN_100NS(x) 			(x*10000)

PTask1ms_Outputs 		pT1msOut;
PTask1ms_Inputs 		pT1msIn;
PTask10ms_Outputs 		pT10msOut;
PTask10ms_Inputs 		pT10msIn;
HANDLE			g_hEvent1, g_hEvent2;
BOOL			g_bExit;

///////////////////////////////////////////////////////////////////////////////
static DWORD WINAPI TimerProc1( LPVOID lpParam)
{
    static int i=0;
    long nError;

    while(!g_bExit)
    {
        WaitForSingleObject(g_hEvent1,INFINITE);
        // first try to get the inputs and test if input update succeeded
        if ( (nError = TCatIoInputUpdate( TASK1MS_PORTNUMBER )) == 0 )
        {
            // do your calculation and logic
            pT1msOut->AnalogOut = pT1msIn->AnalogIn;
            // start the I/O update and field bus cycle
            TCatIoOutputUpdate( TASK1MS_PORTNUMBER );
        }
        else
            printf( "TCatInputUpdate(%d) %d failed with 0x%x !\n",
                    TASK1MS_PORTNUMBER, i++, nError );
     }
    return 0;
}
///////////////////////////////////////////////////////////////////////////////
static DWORD WINAPI TimerProc2( LPVOID lpParam)
{
    static int i=0;
    long nError;

    while(!g_bExit)
    {
        WaitForSingleObject(g_hEvent2,INFINITE);
        if ( (nError = TCatIoInputUpdate( TASK10MS_PORTNUMBER )) == 0 )
        {
            // optionally test the device state, zero is ok.
            if ( pT10msIn->IoDeviceState != 0 )
                printf( "I Device Error !\n" );
            else
            {
                // do your calculation and logic
                pT10msOut->DigitalOut[0] = pT10msIn->DigitalIn[0];
                pT10msOut->DigitalOut[1] = pT10msIn->DigitalIn[1];
                // start the I/O update and field bus cycle
                TCatIoOutputUpdate( TASK10MS_PORTNUMBER );
            }
        }
        else
            printf("TCatInputUpdate(%d) %d failed with 0x%x !\n",
            TASK10MS_PORTNUMBER, i++, nError );
    }
    return 0;
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine,
    int nCmdShow)
{
    INT idTimer1, idTimer2;
    TCHAR pszTask1Name[MAX_PATH] = TEXT("evt_TASK_1_TICK");
    TCHAR pszTask2Name[MAX_PATH] = TEXT("evt_TASK_10_TICK");
    HANDLE hThreadTask1, hThreadTask2;
    long m_nAdsState;
    DWORD m_dwBaseTime;
    // always call TCatIoOpen first.
    if ( TCatIoOpen() == 0 )
    {
        m_nAdsState = TCatGetState();
        if( (TcTimerInitialize() != STATUS_SUCCESS) || (m_nAdsState != ADSSTATE_RUN) )
        {
            TCatIoClose();
            printf("Failed to Initialize TcTimer or TwinCAT not in RUN mode\n");
            getchar();
            return FALSE;
        }
        m_dwBaseTime = TcTimerGetTickTime()/10;
        printf("Base Time = %d microseconds\n", m_dwBaseTime);

        // get the process image pointer
        if ( TCatIoGetOutputPtr(TASK1MS_PORTNUMBER, (void**)&pT1msOut,sizeof(Task1ms_Outputs) ) == 0 &&
            TCatIoGetInputPtr( TASK1MS_PORTNUMBER,(void**)&pT1msIn, sizeof(Task1ms_Inputs) ) == 0 &&
            TCatIoGetOutputPtr(TASK10MS_PORTNUMBER,(void**)&pT10msOut, sizeof(Task10ms_Outputs) )== 0 &&
            TCatIoGetInputPtr(TASK10MS_PORTNUMBER,(void**)&pT10msIn, sizeof(Task10ms_Inputs) )== 0 )
        {
            // Create Events for the Tasks
            g_hEvent1 = CreateEvent(NULL,FALSE,FALSE,pszTask1Name);
            g_hEvent2 = CreateEvent(NULL,FALSE,FALSE,pszTask2Name);
            // Set Events in TcTimer
            idTimer1 = TcTimerSetEvent( TASK1MS_DELAY,  pszTask1Name, TIME_CALLBACK_EVENT_PULSE|TIME_PERIODIC );
            idTimer2 = TcTimerSetEvent( TASK10MS_DELAY, pszTask2Name, TIME_CALLBACK_EVENT_PULSE|TIME_PERIODIC );
            g_bExit = FALSE;
            // Create thread
            hThreadTask1 = CreateThread( NULL, 0, TimerProc1, NULL, CREATE_SUSPENDED, NULL);
            hThreadTask2 = CreateThread( NULL, 0, TimerProc2, NULL, CREATE_SUSPENDED, NULL);
            // Set the Priorities of the Thread
            if (CeSetThreadPriority(hThreadTask1, 26) && CeSetThreadPriority(hThreadTask2, 27))
            {
                ResumeThread(hThreadTask1);
                ResumeThread(hThreadTask2);
            }

            // Wait for the end
            printf("Press any key to End !!\n");
            getchar();
            g_bExit = TRUE;
            SetEvent(g_hEvent1);
            SetEvent(g_hEvent2);
            // events are no longer needed
            TcTimerKillEvent( idTimer1 );
            TcTimerKillEvent( idTimer2 );
            CloseHandle( g_hEvent1 );
            CloseHandle( g_hEvent2 );
        }
        // free resources
        TCatIoClose();
        TcTimerDeinitialize();
    }
    return 0;
}

注意：

请注意系统需求.


TwinCAT Sample Program

TwinCAT I/O To Ring 3 Applications

在VisualStudio 2005中使用TcTimer

TwinCAT I/O - 在VisualStudio2005中使用TcTimer

  • 在VS2005中创建一个新的Console Application工程.
  • 在工程中链接TcTimerWrap.lib和TCatIoDrvW32.lib
  • 向项目中添加如下代码:
    ///////////////////////////////////////////////////////////////////////////////
//
// Beckhoff Automation
//
// TwinCAT CE ® I/O sample program using TwinCAT Timer.
//
///////////////////////////////////////////////////////////////////////////////
// TcTimerWrap.lib and TCatIoDrvW32.lib must be linked for this project
#include "stdio.h"
#include "windows.h"
#include "TCatIoW32Api.h" 	// header file shipped with TwinCAT® I/O
#include "TcTimerApi.h" 	// header file shipped with TwinCAT® I/O
#include "Task1ms.h" 	// TwinCAT® System Manager generated
#include "Task10ms.h" 	// TwinCAT® System Manager generated

#define IOERR_IOSTATEBUSY 			0x2075
#define TASK1MS_DELAY 			1 // Ticks
#define TASK10MS_DELAY 			10 // Ticks
#define DELAY_IN_100NS(x) 			(x*10000)

PTask1ms_Outputs 		pT1msOut;
PTask1ms_Inputs 		pT1msIn;
PTask10ms_Outputs 		pT10msOut;
PTask10ms_Inputs 		pT10msIn;
HANDLE			g_hEvent1, g_hEvent2;
BOOL			g_bExit;

///////////////////////////////////////////////////////////////////////////////
static DWORD WINAPI TimerProc1( LPVOID lpParam)
{
    static int i=0;
    long nError;

    while(!g_bExit)
    {
        WaitForSingleObject(g_hEvent1,INFINITE);
        // first try to get the inputs and test if input update succeeded
        if ( (nError = TCatIoInputUpdate( TASK1MS_PORTNUMBER )) == 0 )
        {
            // do your calculation and logic
            pT1msOut->AnalogOut = pT1msIn->AnalogIn;
            // start the I/O update and field bus cycle
            TCatIoOutputUpdate( TASK1MS_PORTNUMBER );
        }
        else
            printf( "TCatInputUpdate(%d) %d failed with 0x%x !\n",
                    TASK1MS_PORTNUMBER, i++, nError );
     }
    return 0;
}
///////////////////////////////////////////////////////////////////////////////
static DWORD WINAPI TimerProc2( LPVOID lpParam)
{
    static int i=0;
    long nError;

    while(!g_bExit)
    {
        WaitForSingleObject(g_hEvent2,INFINITE);
        if ( (nError = TCatIoInputUpdate( TASK10MS_PORTNUMBER )) == 0 )
        {
            // optionally test the device state, zero is ok.
            if ( pT10msIn->IoDeviceState != 0 )
                printf( "I Device Error !\n" );
            else
            {
                // do your calculation and logic
                pT10msOut->DigitalOut[0] = pT10msIn->DigitalIn[0];
                pT10msOut->DigitalOut[1] = pT10msIn->DigitalIn[1];
                // start the I/O update and field bus cycle
                TCatIoOutputUpdate( TASK10MS_PORTNUMBER );
            }
        }
        else
            printf("TCatInputUpdate(%d) %d failed with 0x%x !\n",
            TASK10MS_PORTNUMBER, i++, nError );
    }
    return 0;
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI _tmain( HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine,
    int nCmdShow)
{
    INT idTimer1, idTimer2;
    TCHAR pszTask1Name[MAX_PATH] = TEXT("evt_TASK_1_TICK");
    TCHAR pszTask2Name[MAX_PATH] = TEXT("evt_TASK_10_TICK");
    HANDLE hThreadTask1, hThreadTask2;
    long m_nAdsState;
    DWORD m_dwBaseTime;	// always call TCatIoOpen first.
    if ( TCatIoOpen() == 0 )
    {

        m_nAdsState = TCatGetState();
        if( (TcTimerInitialize() != STATUS_SUCCESS) || (m_nAdsState != ADSSTATE_RUN) )
        {
            TCatIoClose();
            printf("Failed to Initialize TcTimer or TwinCAT not in RUN mode\n");
            getchar();
            return FALSE;
        }
        m_dwBaseTime = TcTimerGetTickTime()/10;
        printf("Base Time = %d microseconds\n", m_dwBaseTime);

        // get the process image pointer
        if ( TCatIoGetOutputPtr(TASK1MS_PORTNUMBER, (void**)&pT1msOut,sizeof(Task1ms_Outputs) ) == 0 &&
            TCatIoGetInputPtr( TASK1MS_PORTNUMBER,(void**)&pT1msIn, sizeof(Task1ms_Inputs) ) == 0 &&
            TCatIoGetOutputPtr(TASK10MS_PORTNUMBER,(void**)&pT10msOut, sizeof(Task10ms_Outputs) )== 0 &&
            TCatIoGetInputPtr(TASK10MS_PORTNUMBER,(void**)&pT10msIn, sizeof(Task10ms_Inputs) )== 0 )
        {
            // Create Events for the Tasks
            g_hEvent1 = CreateEvent(NULL,FALSE,FALSE,pszTask1Name);
            g_hEvent2 = CreateEvent(NULL,FALSE,FALSE,pszTask2Name);
                    // Set Events in TcTimer
            idTimer1 = TcTimerSetEvent( TASK1MS_DELAY,  pszTask1Name, TIME_CALLBACK_EVENT_PULSE|TIME_PERIODIC );
            idTimer2 = TcTimerSetEvent( TASK10MS_DELAY, pszTask2Name, TIME_CALLBACK_EVENT_PULSE|TIME_PERIODIC );
                        g_bExit = FALSE;
            // Create thread
            hThreadTask1 = CreateThread( NULL, 0, TimerProc1, NULL, CREATE_SUSPENDED, NULL);
            hThreadTask2 = CreateThread( NULL, 0, TimerProc2, NULL, CREATE_SUSPENDED, NULL);
            // Set the Priorities of the Thread
            if (CeSetThreadPriority(hThreadTask1, 26) && CeSetThreadPriority(hThreadTask2, 27))
            {
                ResumeThread(hThreadTask1);
                ResumeThread(hThreadTask2);
            }

            // Wait for the end
            printf("Press any key to End !!\n");
            getchar();
            g_bExit = TRUE;
            SetEvent(g_hEvent1);
            SetEvent(g_hEvent2);
            // events are no longer needed
            TcTimerKillEvent( idTimer1 );
            TcTimerKillEvent( idTimer2 );
            CloseHandle( g_hEvent1 );
            CloseHandle( g_hEvent2 );
        }
        // free resources
        TCatIoClose();
        TcTimerDeinitialize();
    }
    return 0;
}
  • 如果在编译程序时出现链接错误(linker error),请确认此时程序的编译选项"/Zc:wchar_t-" 是否已经设置为"Yes". (Project properties -> Configuration Properties->C++->Language->Treat wchar_t as Built-in Type)

调试示例程序

按如下步骤调试示例程序:
  • 在Vs2005 IDE中选择Tools -> Options -> Device Tools -> Devices.
  • 在属性框中选择你所用的设备.
  • 在属性框中选择"TCP Connect Transport" 并键入所用设备的ip地址
  • 在Cx设备中, 进入路径 \Hard Disk\System
  • 运行 "conmanclient2" 和 "cmaccept"
  • 在IDE中选择Tools -> Connect to Device.
  • 运行程序.