Saturday, April 09, 2005

High-Speed Timers with Windows CE

A paradox that has always fascinated me about computers is the way that they do so much while being so stupid. At their heart computers have only a primitive bit of intelligence:
     is it a 1?
     is it a 0?
That's it. However, the redeeming quality of this neolithic process is the computer's ability to make more of these determinations in one second than you or I can make in our lifetimes. I've heard it called the dopeler effect: the tendency of dumb things to appear smarter when happen rapidly.

Primitive paradoxes aside, what this means in the real world is that computers work so well because they can do things amazingly faster than we can do them. Windows CE, for example, provides a timer that lets you track up to 1,000 things per second. For nearly all of the day-to-day programming tasks this is fast enough. But if you're working with high-speed machinery or programming servos on a robotic arm or, like us, watching cartons fly by at about 40 ft/sec (12 m/s) it's nowhere near fast enough.

For our purposes, we needed a timer that would fire 20,000 times per second, or about 50 µs. So, we stole the timer that Windows CE uses for the kernel profiler. It means that we lost any kernel profiling abillity but I don't know that we would've used it anyway.

Disclaimer: This is how we did it. This doesn't mean that it's the "best practices" way of doing it. (But boy, does it work!)

Setting up a high-speed timer isn't very hard. There are just three things to do. First, we need to modify timer.c. (Do make a backup.) For an X86 platform, we find this in %_WINCEROOT%\PUBLIC\COMMON\OAK\CSP\I486\OAL\timer.c. We'll make as few changes as possible.

// Our own interrupt task
extern void PolledInterruptTask(void);
...and later on...
int ProfileInterrupt(void) 
{
PolledInterruptTask();

dwReschedCount++;

if (dwReschedCount < dwProfilingMultiple)
return SYSINTR_NOP;

dwReschedCount = 0;
return SYSINTR_RESCHED;
}
Now we have a have an interrupt that will fire our custom task, whatever it is. We still need a way to start the profiler, which we can do using a custom IOCTL.

In oemctl.c we need to add the following case to OEMIoControl():
   switch (dwIoControlCode) { 
case IOCTL_START_CUSTOM_TIMER:
#ifndef DEBUG
// Start up the profiler timer
OEMProfileTimerEnable(50);
#endif

retval = TRUE;
break;
With the OAL parts working, we should build the platform. At this point we can also export a custom SDK to use with eVC (more on that in a later post).

Our last step is to call the new IOCTL as our application starts up. In our initialization code we might have something like this:
   if (!KernelIoControl(IOCTL_START_CUSTOM_TIMER,
NULL, 0, NULL, 0, NULL)) {
LOG(__FILE__, __LINE__,1,
"High-speed timer startup failed!");
}

It took us a little while to get everything working right, but once we did we tested it on a prototype and could verify that everything was working correctly. But was it running at 50 µs? It would be a little while before we could test it and find out.

Note 1: The MSDN lists a more in-depth way of implementing timers in CE in this article: Implementing Rock-Solid Windows CE Timers On Windows CE .NET 4.1 Platforms

Note 2: Modifying timer.c is also known as cloning the public code, which this MSDN article talks more about: Cloning the Microsoft Public Code

4 Comments:

At 10/13/2005 12:36 PM, Blogger Jossie said...

I appreciate your information on Msdn. I just bookmarked your site and will be back regulalry to keep on top of it. Please check out my blog on Msdn Exposed - I'd really appreciate it

 
At 2/05/2007 12:17 PM, Anonymous Anonymous said...

This is very interesting site... » » »

 
At 3/16/2007 4:04 PM, Anonymous Anonymous said...

Very cool design! Useful information. Go on! film editing schools

 
At 4/24/2007 10:47 AM, Anonymous Anonymous said...

Enjoyed a lot! John jacobs golf academy

 

Post a Comment

<< Home