none
WriteFile from a timer callback function RRS feed

  • Question

  • Hello,

    I am having some issues with calling my WriteFile function from within a timer callback routine.  First, I have an application and in the application's main() method I am able to do the ReadFile and WriteFile functions just fine and I can verify they work correctly with the board's RAM.

    Now, in the main function instead of doing the WriteFile I do this:

    uiTimerRet = timeSetEvent(1, 0, TimerCb, dwTestData, TIME_PERIODIC);
    

    and my callback looks like this:

    void CALLBACK TimerCb(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
    {
    	cbCounts++;
    
    	numToWrite = 1;
    	wBuf[0] = 'E';
    
    	if (WriteFile(hDriver, wBuf, numToWrite, &numWritten, NULL)) {
    		dwCbSuccess++;
    		if (numWritten > numWrittenCb)
    			numWrittenCb = numWritten;
    	}
    	else
    		dwCbFailure++;
    }
    

    Now I took the variables numToWrite, wBuf, hDriver, numWritten and made them global so that the WriteFile is EXACTLY the same as it is in main().

    This never works.  Is it not possible to write from a timer callback function?

     

    Thanks,

     

     

    Jeff

    Tuesday, July 13, 2010 3:06 PM

Answers

  • Just for future reference in case anyone else reads this, the general idea worked.  I haven't found out what didn't work, but this did:

     

    uiTimerRet = timeSetEvent(1,	//delay, in milliseconds
           0,
           TimerCb,
    			 dwTestData,
    			 TIME_PERIODIC | TIME_CALLBACK_FUNCTION
    			 );
    
    
    void CALLBACK TimerCb(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
    {
    	cbCounts++;
    	//printf("Success!\n")
    	timer_val = 'C';
    
    	if (glob_write[0] == 0x00)
    	{
    		glob_write[0] = 0x07;
    		glob_write[1] = 0x07;
    	}
    	else
    	{
    		glob_write[0] = 0x00;
    		glob_write[1] = 0x00;
    	}
    
    	WriteFile(hDriver, glob_write, 2, &numTimerWritten, NULL);
    }
    

     


    Of course, it should be noted that there were other suggestions as well that might produce even more accurate timers.  I haven't tried those yet.
    • Edited by mr_jeff Tuesday, April 12, 2011 1:51 PM comment about other poster's suggestions
    • Marked as answer by mr_jeff Tuesday, April 12, 2011 1:51 PM
    Tuesday, April 12, 2011 1:50 PM

All replies

  • I should add that dwCbSuccess is a large number (over 100 each time I try this) and dwCbFailure is always 0, so the WriteFile is at least returning success.
    Tuesday, July 13, 2010 3:08 PM
  • How is wBuf defined?  The 'w' tells me that it is a wide character, if that is correct you are writing a zero (not the character but 0x0) to the file each time this is called.  What happens if you change numToWrite to sizeof( wBuf[0] );
    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Tuesday, July 13, 2010 4:16 PM
    Moderator
  • BYTE wBuf[100];
    

    My "w" actually means "write", although I should probably change that habit.  But this code for WriteFile works fine when it is in main().  It's only a problem when it appears in the callback function.

    Tuesday, July 13, 2010 5:04 PM
  • Just to add more information, I create a different function to write from (this function calls WriteFile) and that works as well.  So having it in a function is ok, the global variables are ok, etc.  Must be something unique to callback functions?
    Tuesday, July 13, 2010 6:14 PM
  • I don't know if it can be done or not, but if we accept that it cannot then the solution is to post a message back to your application and write the data then.  Another solution would be to skip the timer altogether and use a thread with a delay like WaitForSingleObject() or Sleep()
    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Tuesday, July 13, 2010 9:32 PM
    Moderator
  • The source code of timeSetEvent is in PUBLIC\COMMON\OAK\DRIVERS\MMTIMER\mmtimer.c
    By tracing the code, the timer call back function is called by TimerThread. The default priority of TimerThread is 248 which equals to THREAD_PRIORITY_TIME_CRITICAL, maybe the higher than normal priority have side-effect to your driver?

    Wednesday, July 14, 2010 4:13 AM
  • I am not sure but may be timer callback's are atomic in nature and functions line readfile and WriteFile are blocking calls, hence the problem could be because of this. May be rescheduling it or by using WaitForSingleObject() insted of timer could be a solution.

     

    Please comment if my assumptions are wrong .

     

    With regard

    Misbah

    Wednesday, July 14, 2010 11:57 AM
  • I don't know if it can be done or not, but if we accept that it cannot then the solution is to post a message back to your application and write the data then.  Another solution would be to skip the timer altogether and use a thread with a delay like WaitForSingleObject() or Sleep()


    I can't use Sleep() because the deviance from one millisecond will be too high.  What I'd like is something that will allow me to execute my ReadFiles and WriteFiles every 1ms with only a few hundred microseconds of deviance.  I've done some reading and Sleep() has too much variance.

    WaitForSingleObject() could work.  It seems similar to this other idea I was going to try.  The pseudo-code looks like this:

    while (1) {
    	QueryPerformanceCounter(&curTick);
    	while (curTick < millsecond_number)
    		QueryPerformanceCounter(&curTick);
    
    	//do the ReadFiles and Writefiles here
    }
    

    It seems like WaitForSingleObject() would block my app's execution anyways, so maybe I could get by with something like this, although a callback would have been more elegant.  :)

    Wednesday, July 14, 2010 6:42 PM
  • If you need such 1ms granularity, you need a higher thread priority to ensure the scheduling is always on-time.

    But for most of designs consider to pump and queue the data inside driver's IST so application just read the data from driver's software FIFO.

    Please refer to the usage of CeSetThreadPriority http://msdn.microsoft.com/en-us/library/ee488041.aspx and http://msdn.microsoft.com/en-us/library/ee483150.aspx

    And if you still prefer to use mmtimer callback, the priority can be adjusted by modifying [HKEY_LOCAL_MACHINE\SYSTEM\MMTIMER\Priority256] http://msdn.microsoft.com/en-us/library/ee482734.aspx

     

    Wednesday, July 14, 2010 7:43 PM
  • Maybe you need to tell us more about your requirements.  An interrupt would be the most accurate for this kind of timing.

    Then I wonder if it wouldn't be much better to use WriteFile to queue data in your driver and have the interrupt put a byte in your RAM every ms and read a byte for later retrieval by ReadFile.   It would be much more effecient, leaving more CPU available for applications and using less battery, if that is important.


    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Wednesday, July 14, 2010 8:25 PM
    Moderator
  • Maybe you need to tell us more about your requirements.  An interrupt would be the most accurate for this kind of timing.

    Then I wonder if it wouldn't be much better to use WriteFile to queue data in your driver and have the interrupt put a byte in your RAM every ms and read a byte for later retrieval by ReadFile.   It would be much more effecient, leaving more CPU available for applications and using less battery, if that is important.

    I will need to read approximately 100 bytes (the number varies with how the user is using the program, but it is in this range) and then write approximately 100 bytes.  Something else is expecting to see 100 "fresh" bytes every millisecond, and that something else is very deterministic on it's timing (within 100 microseconds or so).

    In the down time I will process the 100 bytes that I read and then generate "fresh" 100 bytes to write at the next millisecond.  My processing won't take long, so that's what that tight polling loop could work.

    In your second paragraph are you suggesting that the interrupt be in the driver then?  In other words, my XXX_Write would write to a buffer instead of RAM, and then when the interrupt occurred the interrupt handler would take from the buffer and write to RAM?

    The best I found on doing this with interrupts was this:

    http://msdn.microsoft.com/en-us/library/ms836797.aspx

    but I didn't want to go messing that deeply if this polling method could do it.

    Wednesday, July 14, 2010 9:59 PM
  • If you need such 1ms granularity, you need a higher thread priority to ensure the scheduling is always on-time.

    But for most of designs consider to pump and queue the data inside driver's IST so application just read the data from driver's software FIFO.

    Ok, so far I haven't created a thread yet, but it sounds like my driver DLL should make a thread and:

    1. The driver DLL sets up that thread to have high priority
    2. The driver DLL assigns an interrupt to this thread?
    3. The XXX_Write of the driver DLL buffers the data somewhere and the thread takes from this buffer and writes to RAM.

    This sounds like a good path to follow, but please comment if I mis-interpreted what you meant.

    Thanks.

    Wednesday, July 14, 2010 10:03 PM
  • To change your driver into interrupt driven style, you need a suitable interrupt source. Does your PCI device trigger IRQ periodly?
    Thursday, July 15, 2010 6:29 PM
  • To change your driver into interrupt driven style, you need a suitable interrupt source. Does your PCI device trigger IRQ periodly?

    If I understand correctly, this would mean the following:

    • There is a processor on the pci device
    • Software for this processor executes periodically, say every millisecond
    • Part of the execution of this software is to drive the IRQ line of the PCI Bus high

    If those three bullet points are correct, then it would seem like the processor on the pci device is generating the interrupt for CE.  That would be perfectly fine, actually.

    Right now the processor on the board has no idea that pci exists, but I suppose I could figure out a way to introduce pci to the processor.

    In the meantime I will try my polling method of

    QueryPerformanceCounter(&curTick);
    while (curTick < millsecond_number)
    QueryPerformanceCounter(&curTick);
    Or if my above assumptions are wrong, can you please comment on correct ones?
    Friday, July 16, 2010 3:32 PM
  • Depending on the CPU, it may have internal timers that you can use also.
    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Friday, July 16, 2010 3:55 PM
    Moderator
  • Besides to generate IRQ from your device, another choice is modifying OAL to use hardware timer to trigger IRQ periodically.
    Also the thread priority is an important factor, burst the thread priority may help a lot.
    Friday, July 16, 2010 4:03 PM
  • Just for future reference in case anyone else reads this, the general idea worked.  I haven't found out what didn't work, but this did:

     

    uiTimerRet = timeSetEvent(1,	//delay, in milliseconds
           0,
           TimerCb,
    			 dwTestData,
    			 TIME_PERIODIC | TIME_CALLBACK_FUNCTION
    			 );
    
    
    void CALLBACK TimerCb(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
    {
    	cbCounts++;
    	//printf("Success!\n")
    	timer_val = 'C';
    
    	if (glob_write[0] == 0x00)
    	{
    		glob_write[0] = 0x07;
    		glob_write[1] = 0x07;
    	}
    	else
    	{
    		glob_write[0] = 0x00;
    		glob_write[1] = 0x00;
    	}
    
    	WriteFile(hDriver, glob_write, 2, &numTimerWritten, NULL);
    }
    

     


    Of course, it should be noted that there were other suggestions as well that might produce even more accurate timers.  I haven't tried those yet.
    • Edited by mr_jeff Tuesday, April 12, 2011 1:51 PM comment about other poster's suggestions
    • Marked as answer by mr_jeff Tuesday, April 12, 2011 1:51 PM
    Tuesday, April 12, 2011 1:50 PM