Reputation: 41
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). 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 );
}
Upvotes: 4
Views: 1633
Reputation: 120
My experience with Windows and milli second is that it is not reliable. I measured the Sleep api with an Oscilloscope via the Nusbio device. And Sleep(0) is different from not calling the method at all. Sleep(5) and Sleep(15) give inconsistent result sometime some time the wait is the same.
If you want this accuracy you need a micro controller, that can talk to your Windows application.
Upvotes: -1
Reputation: 36308
You can use SetThreadPriority
to give priority to the critical thread. In this case, you'll probably need to create a thread explicitly and use CreateWaitableTimerEx
, SetWaitableTimerEx
, and WaitForSingleObjectEx
instead of CreateTimerQueueTimer
. Make sure the critical thread never spends too long executing between waits, or Windows may stop working properly.
This may not be enough, if the maximum lag is 100 microseconds. You might need to set your process priority class to REALTIME_PRIORITY_CLASS using the SetPriorityClass
function, but make sure your program never holds the CPU for long or Windows will stop working properly. In particular, if your program hangs, the entire OS will hang; in this situation, there is no way to stop the program short of turning the power off.
Even this may not be enough. Windows is not a real-time operating system, and it may not be possible to get it do what you're asking for.
Upvotes: 2