Real-Time Testing of Windows 95 and NT
Windows 95 and NT are not hard real-time operating systems. Win32 programs running on the fastest Pentium hardware can experience delays of around 15 milliseconds (much worse on 95). These delays occur when only simple operations are run in the background. Complex hardware driver interactions could possibly increase the delay times. While this level of delay is not serious on some 60 Hz alternating current control systems, it is far too great for direct current control systems and especially when tight control loops are necessary. If you were to ask me to design a sorter control system using NT today, I would give it a try. Ask for a hydraulic servo control system and the answer would be a definite no. Ask for a hydraulic servo on DOS and the answer would be no problem. I routinely run interrupts at 8000 Hz on DOS systems without any problems.
To test the latency problems in Windows NT, I created a test program. This Delphi program is very simple. It creates a thread at the highest system priority which periodically outputs a character on the serial port. There is a main form that sets these priority levels when it is created:
SetPriorityClass(GetCurrentProcess,REALTIME_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread,THREAD_PRIORITY_IDLE);
This ensures that the main form will run at a lower priority than the output thread. The output thread runs at the highest possible system priority. The output thread looks like this:
procedure TComThread.Execute; const ch:char=#0; tName='COM_Timer'; var hTE:THandle; SetP:integer; tLT:TLargeInteger; begin SetThreadPriority(GetCurrentThread,THREAD_PRIORITY_TIME_CRITICAL); COM_Control.TP:=GetThreadPriority(GetCurrentThread); hTE:=THandle(CreateWaitableTimer(NIL,BOOL(false),tName)); if hTE<>0 then with COM_Control do begin SetP:=Update_Rate.Position; fillchar(tLT,sizeof(tLT),0); SetWaitableTimer(hTE,tLT,SetP*TMul,NIL,NIL,LongBool(true)); end; with COM_Control do repeat if hTE=0 then Sleep(Update_Rate.Position*TMul) else begin WaitForSingleObject(hTE,1000); if ChangeUpdate or (Update_Rate.Position<>SetP) then begin ChangeUpdate:=false; SetP:=COM_Control.Update_Rate.Position; fillchar(tLT,sizeof(tLT),0); SetWaitableTimer(hTE,tLT,SetP*TMul,NIL,NIL,LongBool(true)); end; end; if SendStrings.Down then begin inc(ch); if (ch<' ') or (ch>'~') then ch:=' '; ComP.PutCom(0,ch); end; until ExitThread; if hTE<>0 then CloseHandle(hTE); end;
The value of TMul is normally 10, but can be set to 1 when the system clock tick rate is increased by using the multimedia timeBeginPeriod(1) call. The WaitableTimer calls are not available on Windows 95 and must be removed for that version of the program. ComP is an object I use for serial communications. It uses standard Win32 WriteFile commands in the asynchronous mode to perform the output:
procedure TComPort.PutCom( {send a character to the communications port} TimeOut:integer; {msec ticks to timeout transfer (0 for no timeout)} c:char {the character to send} ); var CS:TComStat; ne:DWORD; T1,T2:DWORD; NMoved:DWORD; begin if hCom<>INVALID_HANDLE_VALUE then begin T1:=GetTickCount; repeat NMoved:=0; if not WriteFile(hCom,c,1,NMoved,nil) then begin ClearCommError(hCom,ne,@CS); ComError:=ComError or (ne and $1e); end; T2:=GetTickCount; if T2<T1 then T1:=T2; until (NMoved=1) or (TimeOut=0) or (T2-T1>TimeOut); if not (NMoved=1) and (TimeOut>0) then ComError:=ComError or $80; end; end;
Due to internal buffering in the communications drivers, the call to WriteFile will return immediately without any timing delay. The output thread then goes back to sleep for the selected time.
It should be noted that the Sleep function is not the best way to perform periodic timing, but is the only option available for Windows 95. If for some reason the WriteFile output call takes a little time to execute, the call to Sleep may not time accurately because it is not synchronized with the clock ticks (NT ticks every 10 milliseconds on the Intel platform). There will always be an uncertainty of one clock tick in the actual timing with the Sleep function. Microsoft has added a different form of periodic timing starting with NT 4.0. The CreateWaitableTimer, SetWaitableTimer and WaitForSingleObject calls are used on NT to get the best possible timing.
To complete the test, a computer running DOS was used to time the output from the Windows system. The DOS program ran the real-time clock interrupt (part of the battery backup clock chip) at 1024 Hz and counted the number of ticks between characters. These were then used to count an array of values for each timing increment. When the test was done, the array was output to a file for analysis.
In these examples, the system clock was speeded up to 1 millisecond increments with the timeBeginPeriod(1) call and the Sleep or SetWaitableTimer calls were set for 5 millisecond periods. Due to the fact that the DOS system's clock is interrupting at 1024 Hz, it will normally count 5 millisecond operations as 4 milliseconds. The tables that follow show a column of millisecond counts. The count is the number of times the delay between characters was timed at the value in the first column. These tests were done on a 166 MHz Pentium with 32 MB of RAM. Here are the results for Windows 95:
M-Sec Count (no interface operations) 3 9 4 3575 5 462 6 2 M-Sec Count (mouse movement) 3 22 4 1376 5 206 6 2 M-Sec Count (move open Explorer window around with mouse) 3 98 4 2069 5 568 6 19 92 1 M-Sec Count (open Windows Explorer) 3 55 4 1334 5 358 6 16 9 1 10 1 13 1 15 1 19 1 55 1 M-Sec Count (open Delphi compiler) 3 167 4 2569 5 999 6 64 7 11 8 4 9 3 10 1 12 1 17 1 25 1 28 1 29 9 30 6 32 1 33 3 34 1 35 1 63 1 72 1 73 1 75 1 76 1 77 1 89 1 143 1 195 1 401 1 663 1 1018 1 M-Sec Count (run Delphi build all command) 3 55 4 2188 5 489 6 59 7 10 8 4 9 1 10 3 15 1 29 1 M-Sec Count (run complex Mathcad calculation) 3 1675 4 18787 5 9978 6 730 7 126 8 75 9 7 10 4 11 8 12 6 13 1 14 1 15 1 16 1 19 1 22 1 26 1 27 1 29 1 31 2 32 1 73 1 77 1 108 1 125 1 M-Sec Count (run Curves & Colors screen saver) 3 20 4 3286 5 442
If you don't touch anything, Windows 95 appears to give fairly accurate timing. Note that our timing accuracy is plus or minus 1 millisecond and we will normally see a steady 5 millisecond period split between the 4 and 5 millisecond timing counts due to sampling quantums. Moving the mouse or running the screen saver has little effect on the timing. Operater interaction with open windows is seen to cause delays of about 0.1 seconds. The biggest delay is seen when the Delphi compiler is loaded into memory. Running the compiler or doing a large Mathcad calculation are seen to create delays on the order of 0.1 seconds. The 1 second delays encountered when loading large programs will keep Windows 95 from being used in most real-time applications.
Here are the timings for Windows NT 4.0:
M-Sec Count (no interface operations) 4 3468 5 489 M-Sec Count (mouse movement) 4 1752 5 247 M-Sec Count (move window around with mouse) 3 13 4 2511 5 426 6 65 7 24 8 1 M-Sec Count (open Windows Explorer) 3 4 4 2230 5 323 6 2 M-Sec Count (open Delphi compiler) 0 7 1 1 2 13 3 69 4 3180 5 495 6 38 7 22 8 23 9 4 10 3 11 1 12 1 13 4 16 1 M-Sec Count (run Delphi compiler) 1 1 3 9 4 1838 5 267 6 3 8 1 9 1 11 1 12 2 M-Sec Count (open Borland C++ Builder) 0 1 2 12 3 68 4 3758 5 583 6 43 7 21 8 14 9 6 M-Sec Count (run C++ build all command) 0 13 1 3 2 14 3 242 4 10269 5 1636 6 124 7 69 8 74 9 29 10 12 11 10 12 8 13 4 14 2 16 3 18 2 M-Sec Count (run complex Mathcad calculation) 0 28 1 4 2 32 3 291 4 25925 5 3933 6 99 7 87 8 256 9 78 10 26 11 25 12 17 13 12 14 7 15 3 16 3 17 4 18 7 19 3 M-Sec Count (Beziers screen saver) 3 1 4 3113 5 439 M-Sec Count (3D FlowerBox screen saver) 4 3062 5 429
The worse case Windows NT delay is seen to be 19 milliseconds, just 14 milliseconds longer than the target delay. It should also be noted that some delays were less than 1 millisecond. This could possibly cause problems with some applications. From what can be seen, Windows NT appears good enough for use with alternating circuit control systems where delays on the order of 10 milliseconds should not cause problems unless they happen often.
The timings produced by Windows 95 are much worse with delays up to 1 second. The I/O functions in 95 are not fully multi-tasking and it is possible that the WriteFile function may wait for disk operations before allowing serial output. Other factors can affect the timing in Windows NT, such as delays caused by operations inside the kernel. This means that your program will only function as well as the worst device driver present in your system.
You may wonder why I chose this serial output program for the test. The reason is that if Windows NT will ever be good enough for real-time applications it must be able to operate serial I/O systems. One of the most common industrial I/O systems is the Opto-22 OptoMux serial network. If you want to run alternating current controls it is necessary to have updates to the I/O on the order of every 10 milliseconds. This gives you a total delay of about 20 to 30 milliseconds from reading an input and operating an output. The actual output is switched by a solid state relay which adds another 9 milliseconds of uncertainty to the timing. The type of control systems I work with require timing accuracy similar to this in order to function correctly.
Opto-22 provides two types of I/O systems. The PAMUX system is a parallel bus with a 50 wire ribbon cable running from the computer to each relay card in a daisy chain format. This type of network is very simple to operate. Their interface card only requires simple I/O instructions to operate the relays and the driver mentioned in Tip #1 should allow you to run one of these networks at very high speeds. The only drawback to the parallel PAMUX system is the cards can't be too far from the computer due to noise and other problems with using ribbon cable in an industrial environment.
The Opto-22 OptoMux system uses RS-422 signals and the relay card can be up to about 3000 feet from the computer. The only drawback to this system is the high overhead in the serial protocol used to communicate commands to the relays. This means that in order to operate such a system at rates allowing precision control of alternating current systems will require that you use one communications port per relay card (16 relays per card or communications port). This timing test was designed for use on one communications port. Extensions to a real world system would require a multi-port serial interface and associated driver. The test would then need to be run with I/O on several channels at the same time. This will be a future project that I may report on.
There are also many other industrial I/O systems that can be used with a personal computer. This example of the Opto-22 system was only an attempt to explain the problems associated with operation under Windows 95 and NT.