Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: Windows time
I have an application that I need to run at a 10mSec rate (100hz) on a Windows 7/32 bit computer (that will also be running other applications at the same time). I know that windows is not an RTOS, but we are sort of stuck with a requirement to use it. This interrupt can have some minimally late (100uSec) responses, but must not drift over a prolonged time. I have a program where I have loaded and used the NtSetTimerResolution to set the timers to 10msec resolution, and then created a timer using the CreateTimerQueue/CreateTimereQueueTimer functions with a callback routine that toggles a GPIO pin (for the time being) - this produces the expected square wave, so long as I am not doing anything else with the system. When I start a couple of other processes, the accuracy of my square wave goes out the window. Is there any way to get a higher priority level on the timer interrupt (or is there another timer that I can use) that will produce a more stable output (perhaps the SMI)? My code is below, and is built using the x86 checked build environment of the Windows DDK, and run from a command shell with administrator rights:
 

/*
Abstract:
 
Simple console test app for a 10mSec timer interrupt service
 
Enviroment:
 
Administrator Mode
 
*/
 

/* INCLUDES */
 
#include     <windows.h>
#include     <winioctl.h>
#include     <stdio.h>
#include     <string.h>
#include     <stdlib.h>
#include     <conio.h>
#include     <strsafe.h>

#include     <stdlib.h>
#include     <stdio.h>

#include     <winsock2.h>
#include     <mswsock.h>

#pragma warning(disable:4127)   // condition expression is constant

FARPROC pNtQueryTimerResolution;
FARPROC pNtSetTimerResolution;
 
static  HANDLE    NTDLLModuleHandle;
static  HINSTANCE hInpOutDll;
 
typedef         void  (   __stdcall  *lpOut32 )( short , short );
typedef  short        (   __stdcall  *lpInp32 )( short );
typedef  BOOL         (   __stdcall  *lpIsInpOutDriverOpen )( void );
 
//Some global function pointers (messy but fine for an example)
lpOut32              gfpOut32;
lpInp32              gfpInp32;
lpIsInpOutDriverOpen gfpIsInpOutDriverOpen;
 

void CALLBACK TimerProc(void* lpParameter,
    BOOLEAN TimerOrWaitFired);
 
// MAIN

VOID  __cdecl    main( void )
{
    ULONG ulMinRes = 0;
    ULONG ulMaxRes = 0;
    ULONG ulCurRes = 0;
 
    HANDLE phNewQueue;
    HANDLE phNewTimer;
 
    phNewQueue        = CreateTimerQueue( );
 
    NTDLLModuleHandle = LoadLibrary( "NTDLL.DLL" );
 
    if( NULL == NTDLLModuleHandle )
    {
        return;
    }
 
    // Get the function pointers,
    pNtQueryTimerResolution = GetProcAddress( NTDLLModuleHandle, "NtQueryTimerResolution" );
    pNtSetTimerResolution = GetProcAddress( NTDLLModuleHandle, "NtSetTimerResolution" );
 
    if( ( pNtQueryTimerResolution == NULL ) || ( pNtSetTimerResolution == NULL ) )
    {
        printf( "unable to link to ddl\n\n\n\n\n\n" );
        return;
    }
 
    pNtQueryTimerResolution( &ulMinRes, &ulMaxRes, &ulCurRes );
    printf( "MMR:  %d   %d   %d\n", ulMinRes, ulMaxRes, ulCurRes );
 
    ulMaxRes = 100000;
    pNtSetTimerResolution( ulMaxRes, TRUE, &ulCurRes );
 
    pNtQueryTimerResolution( &ulMinRes, &ulMaxRes, &ulCurRes );
    printf( "MMR:  %d   %d   %d\n", ulMinRes, ulMaxRes, ulCurRes );
 
    //Dynamically load the DLL at runtime (not linked at compile time)
    hInpOutDll = LoadLibrary( "InpOut32.DLL" );
    if( hInpOutDll != NULL )
    {
        gfpOut32 = ( lpOut32 )GetProcAddress( hInpOutDll, "Out32" );
        gfpInp32 = ( lpInp32 )GetProcAddress( hInpOutDll, "Inp32" );
        gfpIsInpOutDriverOpen
            = ( lpIsInpOutDriverOpen )GetProcAddress( hInpOutDll, "IsInpOutDriverOpen" );
 
        if( gfpIsInpOutDriverOpen( ) )
        {
            gfpOut32( 0xA01, 0x00 );
        }
        else
        {
            printf( "unable to create timer system\n\n\n\n\n\n" );
            return;
        }
    }
 
    CreateTimerQueueTimer( &phNewTimer, phNewQueue, TimerProc, NULL, 0, 10,
                       WT_EXECUTEINTIMERTHREAD );
 
    do
    {
        Sleep( 1 );
    } while( TRUE );
}
 
void CALLBACK TimerProc(void* lpParameter,
    BOOLEAN TimerOrWaitFired)
{
    WORD wData;
 
    UNREFERENCED_PARAMETER  ( lpParameter );
    UNREFERENCED_PARAMETER  ( TimerOrWaitFired );
 
    wData = gfpInp32( 0xA00 );
    wData++;
    gfpOut32( 0xA00, wData );
}
Posted 23-May-12 15:01pm
Edited 24-May-12 5:29am
v3
Comments
TRK3 at 23-May-12 21:01pm
   
Added code tags.
 
I don't think you have any guarantees under windows about latency once you relinquish the processor. (Unless somebody else knows some trick I'm not aware of.)
 
Do you have to run this on a Windows platform?
lewax00 at 24-May-12 11:03am
   
You can set a program's priority to real time in Windows.
Richard MacCutchan at 24-May-12 10:39am
   
You and TRK3 are both correct: Windows is not a real-time OS so it makes no guarantees about timings. If you want guaranteed timer resolution then you need some other operating system. Even if you connected an external timer to your system there is still no guarantee that Windows would service it at the required intervals.

1 solution

Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

I don't know about the accuracy of the timer, but you can use SetThreadPriority to get the closest thing to true real time on Windows like so:
HANDLE hThread = GetCurrentThread(void);
SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
(from here[^])
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
0 OriginalGriff 320
1 DamithSL 265
2 CPallini 235
3 Maciej Los 190
4 Sergey Alexandrovich Kryukov 184
0 OriginalGriff 5,415
1 DamithSL 4,422
2 Maciej Los 3,820
3 Kornfeld Eliyahu Peter 3,470
4 Sergey Alexandrovich Kryukov 2,911


Advertise | Privacy | Mobile
Web02 | 2.8.141216.1 | Last Updated 24 May 2012
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100