基于PLC的PC機的分布式監控系統_第1頁
基于PLC的PC機的分布式監控系統_第2頁
基于PLC的PC機的分布式監控系統_第3頁
基于PLC的PC機的分布式監控系統_第4頁
基于PLC的PC機的分布式監控系統_第5頁
已閱讀5頁,還剩22頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

基于PLC和PC的分布式計算機監控系統的設計摘

要:供電系統是工廠的重要組成部分,對其可能發生的故障及生產故障進行及時的監控和報警是生產運作順利進行的有力保障。本文介紹了歐姆龍PLC和PC上位機在石油化工廠的電路監控系統中的應用,詳細介紹了該系統的硬件、軟件設計,以及通訊連接。關鍵詞:PLC、PC上位機、串口通信Abstract:This

paper

setting

out

from

an

actual

problem

introduces

a

circuit-alarm

monitoring

system

of

petrochemical

factory

based

on

PLC

and

PC.

It

introduces

the

system’s

design

of

hardware,

software

and

the

connection

of

network

in

detail.KEYWORDS:PLC、PC、Serial

communication一、引言在復雜的電網供電系統中,傳統的故障報警一般采用報警烽鳴器、故障指示燈等硬件方式報警。這種方式存在很多缺點,例如如果電網分布分散,則故障報警點也分散,需要安排一定人力巡邏檢查,造成人力資源浪費,且報警、維修不及時;故障報警點太多則需要大量報警器,且造成線路復雜化;硬件報警得到的故障信息太少等等。隨著計算機和自動化技術在工廠中的大量應用,可以將故障信息通過PLC進行初步監控,然后再由PLC上傳到計算機中,由計算機處理故障信息,進行報警、記錄、顯示故障信息。具有可進行集中監控,節省人力,故障信息直觀、豐富,便于分析等優點。二、系統介紹系統由PLC檢測48個故障繼電器的狀態變化,上位機PC定時查詢并讀取PLC內部4個通道61位的狀態信息(包括48個輸入點和13個歸類輸出點),進行分析并用數據庫加以管理和記錄。程序采用VisualBasic6.0開發,數據庫采用Microsoft的Access2003。每個故障點按照對應的位置標識于車間電路分布圖上,如圖1所示。程序運行進入監控狀態以后,PC將檢測與PLC的通信連接。如果通訊連接正常,程序將檢測現場信號變化。如果信號由正常變為報警,對應指示燈及總狀態指示燈將閃爍并聲音報警,右上方顯示故障信息,運行狀態信息及故障信息將存入數據庫。單擊閃爍指示燈,將停止閃爍,并顯示當前狀態,報警為紅色,正常為淺黃色;如果信號由報警變正常,對應指示燈停止閃爍,運行狀態信息及故障修復信息將存入數據庫。當前狀態由紅色變為淺黃色。如果要查看各個節點信息,則單擊指示燈,右上方圖框會顯示該節點的位號,對應PLC位以及該報警點名稱和觸點狀態。右上方文本框顯示當前系統總運行狀態,有相應的指示燈標示,分為“正?!?/p>

和“報警”兩種狀態,紅色為報警綠色為正常。

圖1

報警監控畫面(正常狀態)報警時畫面如圖2所示。

圖2

報警監控畫面(報警狀態)三、監控系統構成1.系統的結構系統配置如圖3所示。本系統主要由上位機PC和下位機PLC組成,監控48個故障點并將其分為13大類,然后將故障信息顯示在計算機屏幕上。PLC使用的是歐姆龍的C200H,采用3個輸入模塊,一個輸出模塊,一個通訊模塊。將故障繼電器兩端引出作為PLC的干接點,通過輸入模塊將故障信息轉化為開關量輸入PLC,并由PLC對故障進行分類,然后PLC通過RS232-422轉換器和RS232C串口與上位機通信,由上位機讀取并處理故障信息進行監控報警。

3

系統結構2.軟件設計本系統PLC的編程使用CX-ProgrammerV3.1編寫,實現PLC對故障繼電器的初步監控;上位機監控使用VisualBasic6.0編寫主監控程序,實現故障實時監控報警、顯示、記錄、故障點信息查詢修改以及運行狀態查詢等功能。(1)PLC程序:該程序包括三個輸入模塊和一個輸出模塊,用于對48個故障信息的采集和分類,并對PLC后備電池狀態監控。將故障信息轉存到20通道:將故障分類:PLC后備電池狀態轉存至23通道:(2)上位機程序:主要負責與PLC進行串口通信,讀取PLC內部通道狀態加以分析,在主界面上實現故障報警、顯示、故障點信息查詢、故障信息記錄查詢以及節點信息修改查詢等功能。上位機PC與PLC通訊時,按應答方式進行,由上位計算機發給PLC一組ASCⅡ碼字符數據,這一數據稱為命令塊。PLC收到命令塊后經分析認為命令正常,則按照命令進行操作,將操作結果返回給上位計算機,PLC返回給上位計算機的這一組數據稱為響應塊。若PLC收到命令后經分析確認命令不正常,則返回給上位計算機錯誤命令響應塊。上位計算機和PLC通訊時,PLC是被動的,必須由上位計算機給PLC發出命令塊,PLC作出響應發還給上位計算機響應塊。本程序采用多重通訊方式,多重通訊時首幀以單元號(即HOST

LINK的機號)開始,然后為報頭、報文、校驗碼、結束符(只有一幀時)或分隔符(多幀時)。中間幀以報文開始,然后為校驗碼、分隔符,中間幀報文每幀最多125個字符。尾幀以報文開始,然后為校驗碼、結束符,尾幀報文最多124個字符。校驗碼FCS(Frame

Check

Sequence)是8位(bit)二進制數轉換成的2位字符。這8位二進制數是一幀中校驗碼前的所有字符的ASCⅡ碼按位異或的結果。轉換成字符時按照2位十六進制數字轉換成對應的數字字符。PLC收到上位計算機發出的命令塊后,經分析操作返回給上位計算機響應塊,在響應塊中含有響應碼。如果PLC正常完成上位計算機的命令,則響應碼為00,否則,響應碼中含有出錯信息。上位機PC與PLC的串口通信部分:Timer2的值設為2000,即每隔2s上位機PC向PLC發送一次讀取命令,讀取信息。Private

Sub

Timer2_Timer()Dim

a(10)

As

Stringa(0)

=

"@00RR00200004"

’單元號和報頭報文a(1)

=

FCS(a(0,,))

’命令格式中的校驗位a(2)

=

a(0)

+

a(1)

+

"*"

+

chr$(13)

’命令碼MSComm1.Output

=

a(2)

’向PLC發送命令a(3)

=

MSComm1.Inputa(4)

=

mid$(a(3),

6,

2)

’響應碼Call

Message(a(4))

’調用響應碼的錯誤信息If

errortime

>

3

ThenTimer2.Enabled

=

Fr

=

MsgBox(“通訊錯誤,是否退出檢查連接?”,

vbYesNoCancel

+

vbExclamation,

“通訊連接中”)If

intr

=

vbYes

Then。。。。。。。。。Else。。。。。。。。。End

IfEnd

If’如果通訊連接正常則讀取數據:a(6)

=

mid$(a(3),

24,

2)

’響應塊中的校驗碼a(7)

=

mid$(a(3),

1,

23)a(8)

=

FCS(a(7))

’計算校驗碼If

a(8)

=

a(6)

Then

’比較校驗碼是否相同a(9)

=

mid$(a(3),

8,

16)a(10)

=

Hex2Bin$(a(9))z

=

a(10)

’得到PLC內部通道數據ElseMsgBox

(“校驗碼不正確”)。。。。。。。End

If校驗碼計算函數:Function

FCS(ByVal

inputstr

As

String)

As

StringDim

slen,

n,

xorresult

As

IntegerDim

tempfcs

As

Stringslen

=

Len(inputstr)xorresult

=

0For

n

=

1

To

slenxorresult

=

xorresult

Xor

Asc(mid$(inputstr,

n,

1))Next

ntempfcs

=

Hex$(xorresult)If

Len(tempfcs)

=

1

Thentempfcs

=

"0"

+

tempfcsEnd

IfFCS

=

tempfcsEnd

Function將響應碼中的數據轉換為二進制數據:Function

Hex2Bin$(HexValue$)Const

BinTbl

=

"0000000100100011010001010110011110001001101010111100110111101111"Dim

X,

Work$Work$

=

""For

X

=

1

To

Len(HexValue$)Work$

=

Work$

+

mid$(BinTbl,

Val("&h"

+

mid$(HexValue$,

X,

1))

4

+

1,

4)NextHex2Bin$

=

Work$End

Function監控程序功能:故障信息及節點查詢如圖4;PLC電池故障顯示如圖5;數據庫將記錄系統運行狀態,記錄故障發生的時間及相關信息,故障修復的時間及相關信息并提供查詢功能如圖7;可以查看主界面上位號所對應的PLC位、名稱、觸點狀態等信息以便于故障分析,如圖8;系統運行總狀態指示如圖6所示。

6

系統運行狀態指示

7

數據庫查詢

8

位號對應信息查詢四、監控程序完成的功能1.故障指示:有報警時,對應指示燈及總狀態指示燈將閃爍并聲音報警,右上方顯示故障信息,單擊閃爍指示燈,將停止閃爍,并顯示當前狀態,報警為紅色,正常為淺黃色;如果故障修復,則對應指示燈停止閃爍,當前狀態由紅色變為淺黃色。單擊指示燈,則可以查看各個節點信息,右上方圖框會顯示該節點的位號,對應PLC位以及該報警點名稱和觸點狀態。右上方文本框有相應的指示燈顯示當前系統總運行狀態,紅色為報警綠色為正常。2.歷史記錄:數據庫記錄系統運行狀態,記錄報警的位號、名稱、PLC位、觸點狀態、以及報警時間等內容,當故障解除后同樣記錄解除的位號、名稱、PLC位、觸點狀態、以及故障解除時間。數據庫中有位號名稱對應表,以方便查詢每個點的對應關系。3.通訊連接:用于重新連接上位機和下位機間的通訊,當單擊工具欄中連接按鈕時,程序檢測通信連接。4.聲音測試:用于測試聲音報警是否正常。若正常,點擊此按鈕時系統發出報警聲音。5.消音:用于消除系統報警時的聲音,不影響指示燈閃爍報警。五、結束語本系統是為齊魯石化氯堿廠生產車間設計的電路故障報警系統,現已投入運行。在實際應用中提供了真實可靠的生產實時數據,運行情況良好。為企業減少了人力資源浪費,化工廠生產車間環境較差,改善了工人的工作環境降低了工作強度,并實現了集中監控。本系統的可擴展性也很好的適應了車間改良的需求,可根據用戶的要求增加監控點并將本車間故障分類輸出到上一級監控站,受到了用戶的好評。參考文獻:1.徐德

孫同景

《可編程序控制器(PLC)應用技術》

山東科技出版社

20002.李勇帆

《Visual

Basic

6.0程序設計與應用》

人民郵電出版社

20063.李長林

《Visual

Basic串口通信技術與典型實例》

清華大學出版社

2006附錄資料:不需要的可以自行刪除C語言中如何獲取時間?精度如何?1使用time_ttime(time_t*timer)精確到秒2使用clock_tclock()得到的是CPU時間精確到1/CLOCKS_PER_SEC秒3計算時間差使用doubledifftime(time_ttimer1,time_ttimer0)4使用DWORDGetTickCount()精確到毫秒5如果使用MFC的CTime類,可以用CTime::GetCurrentTime()精確到秒6要獲取高精度時間,可以使用BOOLQueryPerformanceFrequency(LARGE_INTEGER*lpFrequency)獲取系統的計數器的頻率BOOLQueryPerformanceCounter(LARGE_INTEGER*lpPerformanceCount)獲取計數器的值然后用兩次計數器的差除以Frequency就得到時間。7MultimediaTimerFunctionsThefollowingfunctionsareusedwithmultimediatimers.timeBeginPeriod/timeEndPeriod/timeGetDevCaps/timeGetSystemTime//*********************************************************************//用標準C實現獲取當前系統時間的函數一.time()函數time(&rawtime)函數獲取當前時間距1970年1月1日的秒數,以秒計數單位,存于rawtime中。#include"time.h"voidmain(){time_trawtime;structtm*timeinfo;time(&rawtime);timeinfo=localtime(&rawtime);printf("\007Thecurrentdate/timeis:%s",asctime(timeinfo));exit(0);}=================#include--必須的時間函數頭文件time_t--時間類型(time.h定義是typedeflongtime_t;追根溯源,time_t是long)structtm--時間結構,time.h定義如下:inttm_sec;inttm_min;inttm_hour;inttm_mday;inttm_mon;inttm_year;inttm_wday;inttm_yday;inttm_isdst;time(&rawtime);--獲取時間,以秒計,從1970年1月一日起算,存于rawtimelocaltime(&rawtime);--轉為當地時間,tm時間結構asctime()--轉為標準ASCII時間格式:星期月日時:分:秒年-----------------------------------------------------------------------------二.clock()函數,用clock()函數,得到系統啟動以后的毫秒級時間,然后除以CLOCKS_PER_SEC,就可以換成“秒”,標準c函數。clock_tclock(void);#includeclock_tt=clock();longsec=t/CLOCKS_PER_SEC;他是記錄時鐘周期的,實現看來不會很精確,需要試驗驗證;---------------------------------------------------------------------------三.gettime(&t);據說tc2.0的time結構含有毫秒信息#include#includeintmain(void){structtimet;gettime(&t);printf("Thecurrenttimeis:-:d:d.d\n",t.ti_hour,t.ti_min,t.ti_sec,t.ti_hund);return0;}time是一個結構體,,其中成員函數ti_hund是毫秒。。。--------------------------------------------------------------------------------四.GetTickCount(),這個是windows里面常用來計算程序運行時間的函數;DWORDdwStart=GetTickCount();//這里運行你的程序代碼DWORDdwEnd=GetTickCount();則(dwEnd-dwStart)就是你的程序運行時間,以毫秒為單位這個函數只精確到55ms,1個tick就是55ms。--------------------------------------------------------------------------------五.timeGetTime()t,imeGetTime()基本等于GetTickCount(),但是精度更高DWORDdwStart=timeGetTime();//這里運行你的程序代碼DWORDdwEnd=timeGetTime();則(dwEnd-dwStart)就是你的程序運行時間,以毫秒為單位雖然返回的值單位應該是ms,但傳說精度只有10ms。=========================================//*****************************************************************Unix##unix時間相關,也是標準庫的//*********************************************************************1.timegm函數只是將structtm結構轉成time_t結構,不使用時區信息;time_ttimegm(structtm*tm);2.mktime使用時區信息time_tmktime(structtm*tm);timelocal函數是GNU擴展的與posix函數mktime相當time_ttimelocal(structtm*tm);3.gmtime函數只是將time_t結構轉成structtm結構,不使用時區信息;structtm*gmtime(consttime_t*clock);4.localtime使用時區信息structtm*localtime(consttime_t*clock);1.time獲取時間,stime設置時間time_tt;t=time(&t);2.stime其參數應該是GMT時間,根據本地時區設置為本地時間;intstime(time_t*tp)3.UTC=true表示采用夏時制;4.文件的修改時間等信息全部采用GMT時間存放,不同的系統在得到修改時間后通過localtime轉換成本地時間;5.設置時區推薦使用setup來設置;6.設置時區也可以先更變/etc/sysconfig/clock中的設置再將ln-fs/usr/share/zoneinfo/xxxx/xxx/etc/localtime才能重效time_t只能表示68年的范圍,即mktime只能返回1970-2038這一段范圍的time_t看看你的系統是否有time_t64,它能表示更大的時間范圍//***************************************************************windows##Window里面的一些不一樣的//*********************************************************************一.CTime()類VC編程一般使用CTime類獲得當前日期和時間CTimet=GetCurrentTime();SYSTEMTIME結構包含毫秒信息typedefstruct_SYSTEMTIME{WORDwYear;WORDwMonth;WORDwDayOfWeek;WORDwDay;WORDwHour;WORDwMinute;WORDwSecond;WORDwMilliseconds;}SYSTEMTIME,*PSYSTEMTIME;SYSTEMTIMEt1;GetSystemTime(&t1)CTimecurTime(t1);WORDms=t1.wMilliseconds;SYSTEMTIMEsysTm;::GetLocalTime(&sysTm);在time.h中的_strtime()//只能在windows中用chart[11];_strtime(t);puts(t);//*****************************獲得當前日期和時間CTimetm=CTime::GetCurrentTime();CStringstr=tm.Format("%Y-%m-%d");在VC中,我們可以借助CTime時間類,獲取系統當前日期,具體使用方法如下:CTimet=CTime::GetCurrentTime();//獲取系統日期,存儲在t里面intd=t.GetDay();//獲得當前日期inty=t.GetYear();//獲取當前年份intm=t.GetMonth();//獲取當前月份inth=t.GetHour();//獲取當前為幾時intmm=t.GetMinute();//獲取當前分鐘ints=t.GetSecond();//獲取當前秒intw=t.GetDayOfWeek();//獲取星期幾,注意1為星期天,7為星期六二.CTimeSpan類如果想計算兩段時間的差值,可以使用CTimeSpan類,具體使用方法如下:CTimet1(1999,3,19,22,15,0);CTimet=CTime::GetCurrentTime();CTimeSpanspan=t-t1;//計算當前系統時間與時間t1的間隔intiDay=span.GetDays();//獲取這段時間間隔共有多少天intiHour=span.GetTotalHours();//獲取總共有多少小時intiMin=span.GetTotalMinutes();//獲取總共有多少分鐘intiSec=span.GetTotalSeconds();//獲取總共有多少秒------------------------------------------------------------------------------三._timeb()函數_timeb定義在SYS\TIMEB.H,有四個fieldsdstflagmillitmtimetimezonevoid_ftime(struct_timeb*timeptr);struct_timebtimebuffer;_ftime(&timebuffer);取當前時間:文檔講可以到ms,有人測試,好象只能到16ms!四.設置計時器定義TIMERID#defineTIMERID_JISUANFANGSHI2在適當的地方設置時鐘,需要開始其作用的地方;SetTimer(TIMERID_JISUANFANGSHI,200,NULL);在不需要定時器的時候的時候銷毀掉時鐘KillTimer(TIMERID_JISUANFANGSHI);對應VC程序的消息映射voidCJisuan::OnTimer(UINTnIDEvent){switch(nIDEvent)}---------------------------------------------------------------------------------------##如何設定當前系統時間---------------------------------------windowsSYSTEMTIMEm_myLocalTime,*lpSystemTime;m_myLocalTime.wYear=2003;m_myLocalTime.wM;m_myLocalTime.wDay=1;m_myLocalTime.wHour=0;m_myLocalTime.wMinute=0;m_myLocalTime.wSec;m_myLocalTime.wMillisec;lpSystemTime=&m_myLocalTime;if(SetLocalTime(lpSystemTime))//此處換成SetSystemTime()也不行MessageBox("OK!");elseMessageBox("Error!");SYSTEMTIMEm_myLocalTime,*lpSystemTime;m_myLocalTime.wYear=2003;m_myLocalTime.wM;m_myLocalTime.wDay=1;lpSystemTime=&m_myLocalTime;if(SetDate(lpSystemTime))//此處換成SetSystemTime()也不行MessageBox("OK!");elseMessageBox("Error!");本文來自CSDN博客,轉載請標明出處:/khuang2008/archive/2008/12/09/3483274.aspx一種制作微秒級精度定時器的方法當使用定時器時,在很多情況下只用到毫秒級的時間間隔,所以只需用到下面的兩種常用方式就滿足要求了。一是用SetTimer函數建立一個定時器后,在程序中通過處理由定時器發送到線程消息隊列中的WM_TIMER消息,而得到定時的效果(退出程序時別忘了調用和SetTimer配對使用的KillTimer函數)。二是利用GetTickCount函數可以返回自計算機啟動后的時間,通過兩次調用GetTickCount函數,然后控制它們的差值來取得定時效果,此方式跟第一種方式一樣,精度也是毫秒級的。用這兩種方式取得的定時效果雖然在許多場合已經滿足實際的要求,但由于它們的精度只有毫秒級的,而且在要求定時時間間隔小時,實際定時誤差大。下面介紹一種能取得高精度定時的方法。在一些計算機硬件系統中,包含有高精度運行計數器(high-resolutionperformancecounter),利用它可以獲得高精度定時間隔,其精度與CPU的時鐘頻率有關。采用這種方法的步驟如下:1、首先調用QueryPerformanceFrequency函數取得高精度運行計數器的頻率f。單位是每秒多少次(n/s),此數一般很大。2、在需要定時的代碼的兩端分別調用QueryPerformanceCounter以取得高精度運行計數器的數值n1,n2。兩次數值的差值通過f換算成時間間隔,t=(n2-n1)/f。下面舉一個例子來演示這種方法的使用及它的精確度。在VC6.0下用MFC建立一個對話框工程,取名為HightTimer.在對話框面板中控件的布局如下圖:其中包含兩個靜態文本框,兩個編輯框和兩個按紐。上面和下面位置的編輯框的ID分別為IDC_E_TEST和IDC_E_ACTUAL,通過MFCClassWizard添加的成員變量也分別對應為DWORDm_dwTest和DWORDm_dwAct.“退出”按紐的ID為IDOK,“開始測試”按紐ID為IDC_B_TEST,用MFCClassWizard添加此按紐的單擊消息處理函數如下:voidCHightTimerDlg::OnBTest(){//TODO:AddyourcontrolnotificationhandlercodehereUpdateData(TRUE);//取輸入的測試時間值到與編輯框相關聯的成員變量m_dwTest中LARGE_INTEGERfrequence;if(!QueryPerformanceFrequency(&frequence))//取高精度運行計數器的頻率,若硬件不支持則返回FALSEMessageBox("Yourcomputerhardwaredoesn'tsupportthehigh-resolutionperformancecounter","NotSupport",MB_ICONEXCLAMATION|MB_OK);LARGE_INTEGERtest,ret;test.QuadPart=frequence.QuadPart*m_dwTest/1000000;//通過頻率換算微秒數到對應的數量(與CPU時鐘有關),1秒=1000000微秒ret=MySleep(test);//調用此函數開始延時,返回實際花銷的數量m_dwAct=(DWORD)(1000000*ret.QuadPart/frequence.QuadPart);//換算到微秒數UpdateData(FALSE);//顯示到對話框面板}其中上面調用的MySleep函數如下:LARGE_INTEGERCHightTimerDlg::MySleep(LARGE_INTEGERInterval)///////////////////////////////////////////////////////////////////////////////////////////////////////////////功能:執行實際的延時功能

//參數:Interval參數為需要執行的延時與時間有關的數量

//返回值:返回此函數執行后實際所用的時間有關的數量

///////////////////////////////////////////////////////////////////////////////////////////////////////////{LARGE_INTEGERprivious,current,Elapse;QueryPerformanceCounter(&privious);current=privious;while(current.QuadPart-privious.QuadPart<Interval.QuadPart)QueryPerformanceCounter(¤t);Elapse.QuadPart=current.QuadPart-privious.QuadPart;returnElapse;}注:別忘了在頭文件中為此函數添加函數聲明。至此,可以編譯和執行此工程了,結果如上圖所示。在本人所用的機上(奔騰366,64M內存)測試,當測試時間超過3微秒時,準確度已經非常高了,此時機器執行本身延時函數代碼的時間對需要延時的時間影響很小了。上面的函數由于演示測試的需要,沒有在函數級封裝,下面給出的函數基本上可以以全局函數的形式照搬到別的程序中。BOOLMySleep(DWORDdwInterval)///////////////////////////////////////////////////////////////////////////////////////////////////////////////功能:執行微秒級的延時功能

//參數:Interval參數為需要的延時數(單位:微秒)

//返回值:若計算機硬件不支持此功能,返回FALSE,若函數執行成功,返回TRUE

///////////////////////////////////////////////////////////////////////////////////////////////////////////{BOOLbNormal=TRUE;LARGE_INTEGERfrequence,privious,current,interval;if(!QueryPerformanceFrequency(&frequence)){::MessageBox(NULL,"Yourcomputerhardwaredoesn'tsupportthehigh-resolutionperformancecounter","NotSupport",MB_ICONEXCLAMATION|MB_OK);//或其它的提示信息returnFALSE;}interval.QuadPart=frequence.QuadPart*dwInterval/1000000;bNormal=bNormal&&QueryPerformanceCounter(&privious);current=privious;while(current.QuadPart-privious.QuadPart<interval.QuadPart)bNormal=bNormal&&QueryPerformanceCounter(¤t);returnbNormal;}需要指出的是,由于在此函數中的代碼很多,機器在執行這些代碼所花費的時間也很長,所以在需要幾個微秒的延時時,會影響精度。實際上,讀者在熟悉這種方法后,只要使用QueryPerformanceFrequency和QueryPerformanceCounter這兩個函數就能按實際需要寫出自己的延時代碼了。使用CPU時間戳進行高精度計時對關注性能的程序開發人員而言,一個好的計時部件既是益友,也是良師。計時器既可以作為程序組件幫助程序員精確的控制程序進程,又是一件有力的調試武器,在有經驗的程序員手里可以盡快的確定程序的性能瓶頸,或者對不同的算法作出有說服力的性能比較。在Windows平臺下,常用的計時器有兩種,一種是timeGetTime多媒體計時器,它可以提供毫秒級的計時。但這個精度對很多應用場合而言還是太粗糙了。另一種是QueryPerformanceCount計數器,隨系統的不同可以提供微秒級的計數。對于實時圖形處理、多媒體數據流處理、或者實時系統構造的程序員,善用QueryPerformanceCount/QueryPerformanceFrequency是一項基本功。本文要介紹的,是另一種直接利用PentiumCPU內部時間戳進行計時的高精度計時手段。以下討論主要得益于《Windows圖形編程》一書,第15頁-17頁,有興趣的讀者可以直接參考該書。關于RDTSC指令的詳細討論,可以參考Intel產品手冊。本文僅僅作拋磚之用。在IntelPentium以上級別的CPU中,有一個稱為“時間戳(TimeStamp)”的部件,它以64位無符號整型數的格式,記錄了自CPU上電以來所經過的時鐘周期數。由于目前的CPU主頻都非常高,因此這個部件可以達到納秒級的計時精度。這個精確性是上述兩種方法所無法比擬的。在Pentium以上的CPU中,提供了一條機器指令RDTSC(ReadTimeStampCounter)來讀取這個時間戳的數字,并將其保存在EDX:EAX寄存器對中。由于EDX:EAX寄存器對恰好是Win32平臺下C++語言保存函數返回值的寄存器,所以我們可以把這條指令看成是一個普通的函數調用。像這樣:inlineunsigned__int64GetCycleCount(){__asmRDTSC}但是不行,因為RDTSC不被C++的內嵌匯編器直接支持,所以我們要用_emit偽指令直接嵌入該指令的機器碼形式0X0F、0X31,如下:inlineunsigned__int64GetCycleCount()

{

__asm_emit0x0F

__asm_emit0x31

}

以后在需要計數器的場合,可以像使用普通的Win32API一樣,調用兩次GetCycleCount函數,比較兩個返回值的差,像這樣:

unsignedlongt;

t=(unsignedlong)GetCycleCount();

//DoSomethingtime-intensive...

t-=(unsignedlong)GetCycleCount();

《Windows圖形編程》第15頁編寫了一個類,把這個計數器封裝起來。有興趣的讀者可以去參考那個類的代碼。作者為了更精確的定時,做了一點小小的改進,把執行RDTSC指令的時間,通過連續兩次調用GetCycleCount函數計算出來并保存了起來,以后每次計時結束后,都從實際得到的計數中減掉這一小段時間,以得到更準確的計時數字。但我個人覺得這一點點改進意義不大。在我的機器上實測,這條指令大概花掉了幾十到100多個周期,在Celeron800MHz的機器上,這不過是十分之一微秒的時間。對大多數應用來說,這點時間完全可以忽略不計;而對那些確實要精確到納秒數量級的應用來說,這個補償也過于粗糙了。

這個方法的優點是:

1.高精度。可以直接達到納秒級的計時精度(在1GHz的CPU上每個時鐘周期就是一納秒),這是其他計時方法所難以企及的。

2.成本低。timeGetTime函數需要鏈接多媒體庫winmm.lib,QueryPerformance*函數根據MSDN的說明,需要硬件的支持(雖然我還沒有見過不支持的機器)和KERNEL庫的支持,所以二者都只能在Windows平臺下使用(關于DOS平臺下的高精度計時問題,可以參考《圖形程序開發人員指南》,里面有關于控制定時器8253的詳細說明)。但RDTSC指令是一條CPU指令,凡是i386平臺下Pentium以上的機器均支持,甚至沒有平臺的限制(我相信i386版本UNIX和Linux下這個方法同樣適用,但沒有條件試驗),而且函數調用的開銷是最小的。

3.具有和CPU主頻直接對應的速率關系。一個計數相當于1/(CPU主頻Hz數)秒,這樣只要知道了CPU的主頻,可以直接計算出時間。這和QueryPerformanceCount不同,后者需要通過QueryPerformanceFrequency獲取

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論