Solved

Timer routines

Posted on 2002-03-22
10
505 Views
Last Modified: 2006-11-17
I need to measure the time in microseconds between state changes on the serial port's DSR pin.

I am reading the state of the DSR pin by executing as fast as possible the following code:

#define COM1_UART       0x3f8         // Port address of COM1
#define COM2_UART       0x2f8         // Port address of COM2

#define MSR                 6         // Modem Status register offset
#define CTS_DELTA        0x01         // Clear To Send Changed
#define DSR_DELTA        0x02         // Data Set Ready Changed
#define RI_DELTA         0x04         // Ring Indicator Changed
#define CD_DELTA         0x08         // Carrier Detect Changed
#define CTS              0x10         // Clear To Send
#define DSR              0x20         // Data Set Ready
#define RI               0x40         // Ring Indicator
#define CD               0x80         // Carrier Detect

// get start time here
while ( ((msr=inportb(COM1_UART+MSR))&DSR_DELTA)==0 )
  ;    
// get end time here

Anyone care to write an assembler routine that I can easily invoke from 'C' that returns the elapsed time in microseconds - from the time the routine is invoked until the state of the DSR pin changes. Call this routine "waitDSR"

Also need a very similar routine that sets the state of the DSR pin and then waits for the specified number of microseconds. call this routine "setDSR".

With the following conditions:

I want to include the assembly code in my C program using the asm {...} inline compiler directive.

The time delay must be specified/returned in microseconds as 32 bit value.
Since the embedded C compiler I am using does not support 32 bit long values it needs to be set/returned as a C data type declared as: char usTime[4];

With 32 bits the largest measurable delay is about 4294 seconds. The largest time delay I will need to measure is about 5 seconds. So ignore any wrapping/overflow of the 32 bit value. The smallest time delay I need to measure is 40 microseconds.

I would prefer the Pentium processor time stamp be used to calculate the time delay.

I do not know x86 assembly so lots of comments would be much appreciated.

Regards
Paul

P.S. The application for this code is to encode/decode a variable pulse width electronic signal, using the DSR pin to sense the waveform. The current application source code, which does not use the embedded C compiler just normal VC++, can be downloaded from http://www.blacky.co.nz/free/vpw.zip if anyone is interested.

If anyone has any suggestions to improve the accuracy of the timing I'd be happy to hear them.
0
Comment
Question by:zebada
  • 6
  • 4
10 Comments
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
What if DSR never changes?  Do you want a timeout to occur?  

Can you be certain that the processor is Pentium?  Are ou running under Windows OS or some embedded OS I've never heard of (if Windows, the port I/O will likely fail unless this is part of a device driver... also under a preemprive multi-tasking O/S, a tight wait loop might be interrupted and mess up the timing).

-- Dan
0
 
LVL 6

Author Comment

by:zebada
Comment Utility
Hi Dan,

Oops, yes I forgot to mention a timeout value, in milliseconds, or cpu cycles, is necessary.

The software is a currently only for prototyping, I have a pentium so it's not an issue - yet.

It will be running under an embedded operating system that boots from floppy - so no DOS, no Windows, no Nothing - just the hadrware, the pentium and my code (in .com file format).

What I am hoping to acheive is to figure out the following:

I will be receiving a square wave pulse that may be as short as 40us. I need to time the response of the DRS as seen by the software. If it is close enough to the 40us (+/-10us) then I will go ahead and try to develop the software to use the DSR pin.
If it is not close enough I will not be able to use the DSR pin - I may need to look at using the parallel port pins instead.

Basically what I am trying to find is a communications pin on the PC that I can use that will respond in real-time within 10us of the actual state change on that pin.

It may be that the DSR line signal takes too long to filter through the UART and into the "port" io-byte. Maybe it is only a few microseconds - I need to know, that's all.

Paul

0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
>>I would prefer the Pentium processor time stamp be used to calculate the time delay.

That could be a problem: The Pentiun Timestamp obtained via RDTSC does not directly yield a value in milliseconds -- it just gets an arbitrary tick-count.  It's Windows that provides the conversion to let you equate the counter to an actual time value.  

Will you be running under a standard PC BIOS?  There is a BIOS fn that will yield a duration in milliseconds.  It may not be accurate enough.  By default (at least, the last time I checked), it is too inaccurate since it only updates in 55-ms increments.  However, given the two, it should be possible to equate a timer tick to a number of RDTSC ticks and thus get a good time duration.

Have you thought about using the LPT port?  I have heard of people using, e.g., the paper-out pin for simple external device communications.

-- Dan
0
 
LVL 6

Author Comment

by:zebada
Comment Utility
The conversion from CPU clock cycles to microseconds is simple - divide the CPU clock cycles by the Mhz of the CPU.
Or is it not that simple?

You're right, the motherboard ctc (clock timer/counter) chip is what I am currently using its about 1.1Mhz so I can measure sub-usec timings but I think it is not accurate/stable for what I am trying to do.

I don't want to go chasing other pins until I *know* that the DSR pin will *not* do what I want.
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
My solution is a blend of asm and C.  There is no performance penalty, and it is easier to understand.  

Note that I could not test this code because Windows does not let me do an IN from port (this is a protected opcode, allowed only by ring-0 device drivers).

Furthermore, you will need to determine CNUM_TicksPerSec for the target computer.

#define COM1_UART       0x3f8         // Port address of COM1
#define COM2_UART       0x2f8         // Port address of COM2

#define MSR                 6         // Modem Status register offset
#define DSR              0x20         // Data Set Ready

#define CNUM_TicksPerSec 0x10000

// WaitForDSR( COM1_UART+MSR, 3 );

//--------------------------------------------------------------
// returns ticks until DSR becomes set
// ticks are arbitrary units, and differ depending upon CPU and speed
// you will need to set CNUM_TicksPerSec for your computer
//
LONGLONG qwCntStart= 0;
LONGLONG qwCntCur=   0;
LONGLONG qwCntMax=   0;
BOOL fDone= 0;

int WaitForDSR( int nPort, int nMaxSecs )
{
      //----------------------- get starting tick
      __asm {
            mov ecx,0
            RDTSC
            mov DWORD PTR qwCntStart, eax
            mov DWORD PTR qwCntStart+4, edx
      }
      qwCntMax= qwCntStart + (LONGLONG)CNUM_TicksPerSec * nMaxSecs;
      qwCntCur= qwCntStart;

      fDone= 0;
      while ( !fDone && (qwCntCur < qwCntMax) ) {
            __asm {
            RDTSC
            mov DWORD PTR qwCntCur, eax
            mov DWORD PTR qwCntCur+4, edx
            mov dx, WORD PTR nPort

            in al,dx  // <<<<<< err when using this (protected OS)
            // mov al, 0x20 // <<<<<<<<<<< i used this for testing
            and al, BYTE PTR DSR
            jnz keepGoing
            mov fDone, 1
            keepGoing:
            }
      }
      int nRetVal= (int)(qwCntCur - qwCntStart);
      if ( !fDone ) nRetVal= -1;

      return( nRetVal );
}

//In case your compiler does not support LONGLONG (aka, _int64)
//here is an example of how to subtract one from another in ASM
//
//mov  edx,dword ptr [qwCntStart+4]
//sub  edx,dword ptr [qwCntCur+4]
//mov  eax,dword ptr [qwCntStart]
//sbb  eax,dword ptr [qwCntCur]
//mov  dword ptr [nRetVal],edx

=-=-=-=-=-=-
Infact, here is the ASM for the whole fn:

rdtsc
mov         [qwCntStart (00416878)],eax
mov         dword ptr [qwCntStart+4 (0041687c)],edx

142:      qwCntMax= qwCntStart + (LONGLONG)CNUM_TicksPerSec * nMaxSecs;
mov         eax,dword ptr [ebp+0Ch]
cdq
push        0
push        1000h
push        edx
push        eax
call        _allmul (004018b0)
mov         ecx,dword ptr [qwCntStart (00416878)]
add         ecx,eax
mov         eax,[qwCntStart+4 (0041687c)]
adc         eax,edx
mov         dword ptr [qwCntMax (00416888)],ecx
mov         [qwCntMax+4 (0041688c)],eax
143:      qwCntCur= qwCntStart;
mov         ecx,dword ptr [qwCntStart (00416878)]
mov         dword ptr [qwCntCur (00416880)],ecx
mov         edx,dword ptr [qwCntStart+4 (0041687c)]
mov         dword ptr [qwCntCur+4 (00416884)],edx
145:      fDone= 0;
mov         dword ptr [fDone (00416890)],0
146:      while ( !fDone && (qwCntCur < qwCntMax) ) {
cmp         dword ptr [fDone (00416890)],0
jne         keepGoing+2 (00402206)
mov         eax,[qwCntCur+4 (00416884)]
cmp         eax,dword ptr [qwCntMax+4 (0041688c)]
jg          keepGoing+2 (00402206)
jl          WaitForDSR+9Eh (004021de)
mov         ecx,dword ptr [qwCntCur (00416880)]
cmp         ecx,dword ptr [qwCntMax (00416888)]
jae         keepGoing+2 (00402206)

mov         ecx,0
rdtsc
mov         [qwCntCur (00416880)],eax
mov         dword ptr [qwCntCur+4 (00416884)],edx
mov         dx,word ptr [ebp+8]
154:          //in al,dx
mov         al,20h
and         al,20h
jne         keepGoing (00402204)
mov         dword ptr [fDone (00416890)],1
159:          keepGoing:
160:          }
161:      }
jmp         WaitForDSR+78h (004021b8)
162:      int nRetVal= (int)(qwCntCur - qwCntStart);
mov         edx,dword ptr [qwCntCur (00416880)]
sub         edx,dword ptr [qwCntStart (00416878)]
mov         eax,[qwCntCur+4 (00416884)]
sbb         eax,dword ptr [qwCntStart+4 (0041687c)]
mov         dword ptr [ebp-4],edx
163:      if ( !fDone ) nRetVal= -1;
cmp         dword ptr [fDone (00416890)],0
jne         keepGoing+2Ch (00402230)
mov         dword ptr [ebp-4],0FFFFFFFFh
165:      return( nRetVal );
mov         eax,dword ptr [ebp-4]
166:  }


-- Dan
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
Ignore that part about "here is how to subtract one QWORD from another"  Correct code is as shown in the last part of the post:

mov   edx,dword ptr [qwCntCur]
sub   edx,dword ptr [qwCntStart]
mov   eax,[qwCntCur+4]
sbb   eax,dword ptr [qwCntStart+4]
mov   dword ptr [nRetVal],edx

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
hi zebada,
Do you have any additional questions?  Do any comments need clarification?

-- Dan
0
 
LVL 6

Author Comment

by:zebada
Comment Utility
No, I just haven't had time to look at it yet. Hopefully make some time in the next few weeks.
I have to set up all the hardware stuff and wire it all up before I can start testing.
Thanks for the code I'll try to get it working/conpiled and tested - I'll probably be asking q's about it later.
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 100 total points
Comment Utility
hi zebada,
Do you have any additional questions?  Do any comments need clarification?

-- Dan
0
 
LVL 6

Author Comment

by:zebada
Comment Utility
I still haven't found time to test out the code but when I do get time I will give it a go. It's a good starting point anyway.
Cheers
Paul
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

This article explains in simple steps how to renew expiring Exchange Server Internal Transport Certificate.
This article explains how to prepare an HTML email signature template file containing dynamic placeholders for users' Azure AD data. Furthermore, it explains how to use this file to remotely set up a department-wide email signature policy in Office …
This video discusses moving either the default database or any database to a new volume.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now