BBC BASIC for Windows
Programming >> Communication and Input/Output >> Serial port pin driving an interrupt
http://bb4w.conforums.com/index.cgi?board=communication&action=display&num=1356810824

Serial port pin driving an interrupt
Post by manxman on Dec 29th, 2012, 6:53pm

I would very much appreciate advice on how to incorporate an interrupt in my program whose purpose is to timestamp seismic data arriving via an RS232 port.

The 1 pulse per second (PPS) signal from a GPS receiver is connected to pin 6 (DSR) on the same serial port. Each time the rising edge of the PPS is detected, the value of the Windows millisecond timer is logged as Then%. When each seismic data packet arrives the millisecond time value Now% is logged, giving me the interval (Now%-Then%) which is added to the number of PPS accumulated (Tick%) to obtain the time since program start, with a theoretical accuracy of a millisecond. At the moment my code comprises a continuous loop which detects when the PPS signal or the data packet arrives and then does timestamping of the latter.

In a reply to another question, Richard suggested that I use an interrupt as a possible solution. From my limited understanding this would seem a better solution here too, since an interrupt that set Then% immediately the PPS signal was detected, would prevent pulses from being missed. The listing shows the essence of what I would like to achieve. Having no experience of how to configure interrupts is the reason for my humble query!

Regards,

Manxman


Code:
 
SYS "timeBeginPeriod", 1
GPS = OPENUP "COM4: baud=9600 parity=N data=8 stop=1 xon=off"
PinHigh=FALSE
Tick%=0

----------------- INTERRUPT FUNCTION -----------------
SYS "GetCommModemStatus", @hfile%(GPS), ^modemstatus%
IF modemstatus%=32 AND PinHigh=FALSE THEN
PinHigh=TRUE
Tick%+=1
SYS "timeGetTime" TO Then%
ENDIF
IF modemstatus%<>32 AND PinHigh=TRUE THEN
PinHigh=FALSE
ENDIF
----------------- INTERRUPT FUNCTION -----------------

REM Remainder of program .........
REM When data is received from the port do: SYS "timeGetTime" TO Now%
REM Then use Tick%+(Now%-Then%)/1000 to do timestamping of the data
REM etc .......

Re: Serial port pin driving an interrupt
Post by admin on Dec 29th, 2012, 10:00pm

on Dec 29th, 2012, 6:53pm, manxman wrote:
Having no experience of how to configure interrupts is the reason for my humble query!

The first thing to note is that, as far as I know, there are no hardware serial port interrupts that you can trap in user code. Therefore you will need to create your own software interrupt.

The essence of what you would do is as follows. Start a new thread which most of the time sits in the WaitCommEvent API awaiting an EV_DSR event. On seeing that event, generate a software interrupt to your BASIC program.

The only way to achieve this in pure BBC BASIC code is to run two separate programs in two separate processes. The first waits for the EV_DSR event and sends a message to the second, which can be trapped using ON SYS.

However this is rather overkill - and the timing is uncertain because it depends on context switches between processes etc. Therefore my preferred method would be to do the waiting for the EV_DSR event in assembler code, which is faster and neater.

Unless you are experienced at writing BB4W assembler code you will probably need some help with this.

Richard.

Re: Serial port pin driving an interrupt
Post by manxman on Dec 30th, 2012, 8:11pm

Richard,

Thanks for your advice. Indeed, I have no experience whatsoever of using assembler and would very much appreciate your help with the coding of this problem. From the various tests I have been carrying out, the combination of 1PPS GPS triggering and PC interval timing has the potential for a resolution and jitter of only a few milliseconds in UTC time.

Regards,

Manxman
Re: Serial port pin driving an interrupt
Post by admin on Dec 30th, 2012, 9:22pm

on Dec 30th, 2012, 8:11pm, manxman wrote:
From the various tests I have been carrying out, the combination of 1PPS GPS triggering and PC interval timing has the potential for a resolution and jitter of only a few milliseconds in UTC time.

I have previously explained, in the thread Setting Windows Tick, that this is not the case. Windows is not a Real Time Operating System and can never achieve a guaranteed resolution of only a few milliseconds. If that is what you need there is only one way to achieve it - time-stamp your data before it even reaches the PC (e.g. using a hardware unit designed for the purpose).

In the other thread I stated "If a resolution in the order of 20 milliseconds is acceptable then you may be able to get away with a purely software solution (on a statistical basis - it cannot be guaranteed)". I don't think I've seen a comment from you on whether that is or isn't an acceptable level of performance.

Until it has been established that a purely software solution can achieve the performance you need there's no point in either of us wasting time writing code!

Richard.


Re: Serial port pin driving an interrupt
Post by manxman on Dec 31st, 2012, 11:04am

Richard,

For my application a resolution of 20 milliseconds is acceptable (this would locate a seismic wavefront to within about 60m). The main reason for trying to implement a pin-triggered interrupt, of the type you outlined, is to reduce the probability that pulses on the pin may be missed, which can corrupt the timing of data.

Of course I do realise that an external GPS timestamping unit is the ideal solution but it would be good to avoid making more hardware if a software timing solution can be achieved with acceptable resolution.

This software project is key to my creating an array of detectors for which the cost of additional hardware would be prohibitive.

Regards,

Manxman
Re: Serial port pin driving an interrupt
Post by admin on Dec 31st, 2012, 5:10pm

on Dec 31st, 2012, 11:04am, manxman wrote:
For my application a resolution of 20 milliseconds is acceptable

OK, just so long as you understand that there can be no guarantee of performance, and that occasionally there could be much larger errors.

Richard.

Re: Serial port pin driving an interrupt
Post by manxman on Jan 1st, 2013, 10:37am

Richard,

I do appreciate your involvement.

You might be interested to know the context of this problem. I operate a continuous seismic recording station on the Isle of Man using my own design of instrument which, to date has detected 432 earthquakes (most along the Pacific rim). My plan now is to create an array of seismometers on the Island, the data from which can be correlated to determine the direction of arrival of seismic waves and thus pinpoint an event's location. For this it is essential that the data are timestamped to a relative precision of about 20-30 milliseconds. I have considered various options: radio atomic clocks, mains frequency clocks, stabilised crystal oscillators and GPS. The 1Hz pulse from a GPS module is generally stable to better than a microsecond and linked exactly to UTC (=GMT). This option has therefore been selected as the most cost-effective and suitable, provided that the 1 Hz pulse intervals can be captured and subdivided with sufficient precision. It would be great to show that this can be done using BBC4W, rather than in hardware.

Best wishes for the New Year

Manxman smiley
Re: Serial port pin driving an interrupt
Post by admin on Jan 1st, 2013, 4:15pm

on Jan 1st, 2013, 10:37am, manxman wrote:
For this it is essential that the data are timestamped to a relative precision of about 20-30 milliseconds.

You use the term "essential", and yet you also say "about". All one can say, with confidence, is that a Windows software-based timing system is likely to exhibit a classic bell-shaped statistical distribution, where the larger the timing error the less frequently it will occur.

So the question you have to ask yourself is: what would be the consequences of an occasional timing error much larger than the 20-30 milliseconds range? Would it cause an obviously anomalous reading (which you can therefore ignore) or could it give rise to a false reading that might be indistinguishable from a genuine event?

Edit: Here are a couple of 'scholarly articles', which completely contradict each other! My advice would be to assume that the more pessimistic of the two (Myors) is nearer the mark:

http://link.springer.com/content/pdf/10.3758%2FBF03207727
http://psych.cf.ac.uk/home2/chambers/Chambers_2003_BRMIC.pdf

Richard.
Re: Serial port pin driving an interrupt
Post by manxman on Jan 2nd, 2013, 10:29am

Ground motion data at each seismometer is being logged at a rate of approximately 20Hz, and each data set will be interpolated, using the GPS-derived timestamps, to a UTC-disciplined synchronous rate of 20Hz. The interpolation process will look for and reject any data items with anomalous spikes in timing. The phase relationship between the instruments' data will then be obtained by cross-correlating timeslices of typically 10 minutes (1200 readings): the result being a set of phase differences within the array from which the bearing of an arriving seismic wavefront can be derived.

Apart from earthquake detection, my primary research interest is investigating the character and mechanism of microseismic noise, which is thought to be generated by weather systems passing over the ocean.

Suppose we have a pair of instruments spaced 10km apart East-West, logging microseismic surface waves from the North Atlantic, travelling with a velocity of 3km/s. Then a 100ms difference in their data phase corresponds to a wave arriving with a bearing of 1.7 degrees from north. Thus, even if the timing error is around 100ms, the bearing resolution towards northern sources in this configuration is acceptable for my purpose. Obviously, the resolution is zero with an E-W array for waves travelling E-W, but this would be the case no matter how good the timing. For this reason I will be deploying at least 5 instruments in a broad geographic pattern to ensure sufficient bearing resolution over the angular swathe of interest.

Regards,

Manxman
Re: Serial port pin driving an interrupt
Post by manxman on Jan 4th, 2013, 4:51pm

I thought that I would reply to myself with the results of some timing tests I am carrying out!

The 20Hz, RS232 data stream from a single seismometer is being split into two feeds that go to a pair of PCs, each of which has an identical GPS module. The 1 Pulse Per Second (PPS) output from each module is converted to an RS232 voltage level and connected to Pin 6 (DSR) of the port that receives the seismometer data. A second (USB virtual) serial port on each PC receives NMEA sentences from the GPS module.

My program begins by capturing the UTC (=GMT) start time from an NMEA sentence, then adds a second each time the PPS is detected. This PPS event also starts a Windows millisecond counter, so that the interval between PPS signals is divided, yielding UTC time to a (theoretical) resolution of one millisecond. This process is essentially what I have described in earlier posts.

My program logs pairs of data: UTC time as seconds since midnight (e.g. 45678.123) together with the seismic velocity (e.g. -435). Obviously, each PC's log will contain an identical list of velocities, providing an exact basis for comparing corresponding UTC times.

In my experiments, data have been recorded for several minutes, first with a pair of identical Asus PCs, then with an Asus PC and an HP Brio, both running Windows XP.

Comparing the data sets from the pair of Asus machines the UTC timestamps are all identical to the millisecond. In the second experiment I find that about 80% of the times are identical to the millisecond, with the maximum deviation in the remainder being 3 milliseconds.

So far, so good in terms of progress towards my objective!

Regards,

Manxman

smiley
Re: Serial port pin driving an interrupt
Post by admin on Jan 5th, 2013, 1:23pm

on Jan 4th, 2013, 4:51pm, manxman wrote:
Comparing the data sets from the pair of Asus machines the UTC timestamps are all identical to the millisecond.

Have you attempted to provoke the kind of event which is likely to upset the timing? For example, have you arranged for context-switches to take place? Have you performed disk accesses? Have you tried to access a network?

According to this Microsoft article a 'quantum', i.e. the time-slice for which a thread will run until a context switch occurs, is either 3.3 ms (single CPU) or 5 ms (multiple CPU):

http://support.microsoft.com/kb/259025

That would imply that timestamps "identical to the millisecond" would be expected only if no context switches take place at all, which is obviously unrealistic. Even BBC BASIC, running on its own with no other applications loaded, has two or three threads!

There's also a relevant discussion at the link below, where somebody states: "Thus knowing how accurate I could get a time-stamp became important. Knowing that Windows would eat 10 or 20 mSec here and there became my limit of accuracy":

http://discuss.joelonsoftware.com/default.asp?joel.3.66319.13

And another relevant comment at the same place: "As for windows being used in control applications, it may not have any true RT facilities, but with a fast enough processor and a low enough workload, you can get some soft RT performance along the lines of 99% of events happen within their time limit.... It also helps if you put in some effort to trap those 1% of errors well and handle them properly".

Richard.
Re: Serial port pin driving an interrupt
Post by manxman on Jan 5th, 2013, 4:13pm

Q: Have you attempted to provoke the kind of event which is likely to upset the timing?

A: Yes. I found that when my program's MODE 7 window is dragged about the screen, the timing can miss a PPS count, leading to a 1s step in timing. Hence, I need to find a way to fix the window position and size. With regard to disk access, the program is continually writing pairs of seismic velocity and timestamp data to disk. None of the array PCs will be on a network and no other programs, such as virus checking, will be running, deliberately to reduce the OS's work and minimise the chance of timing glitches.

Q: That would imply that timestamps "identical to the millisecond" would be expected only if no context switches take place at all, which is obviously unrealistic. Even BBC BASIC, running on its own with no other applications loaded, has two or three threads!

A: All I can report is what I have observed by experiment! So far the timing discrepancies are well within the requirements of my project.

Incidentally, when it comes to a real field deployment there are some other interesting effects to consider. The most important factor is that the local geology and topography at each array station can cause a timing offset that may depend on the arrival direction of the seismic wave. This phenomenon is well known and must be taken into consideration during array processing.

Regards,

Manxman


Re: Serial port pin driving an interrupt
Post by admin on Jan 5th, 2013, 9:45pm

on Jan 5th, 2013, 4:13pm, manxman wrote:
I found that when my program's MODE 7 window is dragged about the screen, the timing can miss a PPS count, leading to a 1s step in timing. Hence, I need to find a way to fix the window position and size.

I really think you're looking at this from the wrong angle! If you have observed that dragging a window can lead to an unacceptable timing error, there are probably many other circumstances which can too. Trying to prevent the one specific situation you have noticed (e.g. by fixing the window) whilst blissfully ignoring the possibility (nay, likelihood) that there are others you haven't yet noticed, is in my opinion reckless.

If there is to be any hope that Windows can reliably meet your timing requirements (and I remain sceptical) you need to find a way to make your program tolerant of dragging the window, not prevent the window being dragged! I would have expected that using an interrupt-based method, as previously discussed, ought to mean that missing (rather than delaying) a Pulse Per Second count is extremely unlikely.

Richard.

Re: Serial port pin driving an interrupt
Post by admin on Jan 6th, 2013, 10:05am

on Jan 5th, 2013, 9:45pm, Richard Russell wrote:
I would have expected that using an interrupt-based method, as previously discussed, ought to mean that missing (rather than delaying) a Pulse Per Second count is extremely unlikely.

Further, how do you currently detect the PPS input? If you poll the DSR pin (using for example SYS "GetCommModemStatus") then the likelihood of missing a pulse entirely is quite high, whereas if you monitor the EV_DSR event (using for example SYS "WaitCommEvent") then the likelihood of missing a pulse is comparatively lower. The code complexity is not significantly different (except that the event method doesn't tell you directly in which direction the DSR input changed!).

In either case, what method do you use for preventing excessive use of CPU time? Normally one would use WAIT or SYS "Sleep" to suspend the CPU, but of course they are both deliberately triggering a task switch, which may be exactly what you don't want! But equally sitting in a tight loop using 100% CPU time isn't acceptable either, so I wonder what you are currently doing.

Richard.
Re: Serial port pin driving an interrupt
Post by manxman on Jan 6th, 2013, 5:04pm

My program does indeed run around a tight loop monitoring the status of the DSR pin (using SYS GetCommModemStatus), while also checking the RS232 buffer to check whether there are data to receive. If there are, then the UTC time stamp is created by summing: (UTC start time in seconds + number of PPS ticks + milliseconds since last PPS). This process was described in my previous posts.

I note the concerns you raise. However, as I mentioned before I have no experience in assembly programming and would therefore welcome your help in coding an EV_DSR detector to reduce the chance of missing PPS events, for example when the window is dragged or resized. Alternatively, how do I use the SYS WaitCommEvent function you describe to make my DSR detection more reliable?

My program is currently configured to detect the rising edge of DSR but the alternative of falling edge detection would also be OK. Because the PPS is 500ms wide it is important that the same edge is always used.

Regards,

Manxman
Re: Serial port pin driving an interrupt
Post by admin on Jan 6th, 2013, 6:00pm

on Jan 6th, 2013, 5:04pm, manxman wrote:
My program does indeed run around a tight loop monitoring the status of the DSR pin (using SYS GetCommModemStatus)

But how tight?! Obviously you shouldn't sit in a loop using 100% CPU time for an extended period (on a modern PC that could even risk hardware damage) so my question remains: how do you keep the CPU usage under control without sacrificing the timing performance?

Being (apparently) unprepared to let me see your code doesn't make it any easier for me to help you!

Quote:
I note the concerns you raise. However, as I mentioned before I have no experience in assembly programming and would therefore welcome your help in coding an EV_DSR detector to reduce the chance of missing PPS events

I think you may be conflating (horrible word!) two separate issues here. The first is how to ensure you never miss a PPS event entirely, and the second is how to maximise the accuracy with which you measure the timing of the PPS event.

At this stage I am proposing that you concentrate only on not missing any PPS events, which is what you reported happening when you moved the window. To that end I am suggesting that using WaitCommEvent to detect the PPS transitions might be beneficial: that doesn't involve any assembly language or other specialised techniques.

Only once you've succeeded in reliably detecting every PPS event (assuming that's possible) does it make sense to tackle the secondary issue of how accurately you can time their arrival. That might need interrupts, which in turn might need the use of some assembler code, but we haven't reached that stage yet!

Quote:
how do I use the SYS WaitCommEvent function you describe to make my DSR detection more reliable?

The advantage of using WaitCommEvent is that it will store the fact that a transition on the DTR input has occurred, for you to detect later at your leisure.

Richard.
Re: Serial port pin driving an interrupt
Post by manxman on Jan 6th, 2013, 7:34pm

Richard,

I've no problem at all listing my code:

Code:
REM Data logging with GPS unit and 1PPS timing
      MODE 7
      VDU3 : REM Disable printer
      OFF  : REM Cursor off
      INSTALL @lib$+"DATELIB"
      SYS "timeBeginPeriod", 1
      VS1 = OPENUP "COM1: baud=19200 parity=N data=8 stop=1 xon=off"
      REM Flush VS1 buffer
      *FX 15,1
      PRINTTAB(2,5) CHR$(130); " SSM     VELOCITY     BEAM"
      PRINTTAB(0,6) CHR$(130);"_____________________________"
      PROCInitTiming
      PROCStartDayFile
      
      REPEAT
        REM Check PPS pin
        SYS "GetCommModemStatus", @hfile%(VS1), ^modemstatus%
        IF modemstatus%=32 AND PinHigh=FALSE THEN
          PinHigh=TRUE
          SYS "timeGetTime" TO Then%
          SSM=(SSM+1) MOD 86400
          IF SSM=0 THEN
            PROCCloseDayFile
            DayNumber%+=1
            PROCStartDayFile
          ENDIF
        ENDIF
        IF modemstatus%<>32 AND PinHigh=TRUE THEN PinHigh=FALSE
      ENDIF
      REM VS1 data collecting part
      IF EXT#VS1>0 THEN
        SYS "timeGetTime" TO Now%
        A$=FNGetStringFromPort(VS1)
        SSM$=STR$(SSM+(Now%-Then%)/1000) : Velocity$=FNExtractString(A$,1): Beam$=FNExtractString(A$,2)
        Info$="                                "
        MID$(Info$,1)=SSM$ : MID$(Info$,14)=Velocity$ : MID$(Info$,24)=Beam$
        PRINTTAB(0,7) CHR$(130); Info$
        PRINT#DayFileHandle,SSM$+" "+Velocity$: BPUT#DayFileHandle,10
      ENDIF
      UNTIL FALSE
      
      END
      
      DEFPROCStartDayFile
      DayFileName$=FN_date$(DayNumber%, "yyy MM dd")+".txt"
      REM Replace gaps with "-"
      MID$(DayFileName$,5,1)="-"
      MID$(DayFileName$,8,1)="-"
      PRINTTAB(8,0) CHR$(134); DayFileName$
      DayFileHandle=OPENOUT DayFileName$
      FileOpen=TRUE
      ENDPROC
      
      DEFPROCCloseDayFile
      CLOSE#DayFileHandle
      FileOpen=FALSE
      ENDPROC
      
      DEFPROCInitTiming
      REM Initialises the timing using PC clock. Start by waiting for the seconds to change.
      Then$=TIME$
      REPEAT
      Now$=TIME$
      UNTIL Now$<>Then$
      SSM=3600*VAL(MID$(Now$,17,2)) + 60*VAL(MID$(Now$,20,2)) + VAL(MID$(Now$,23,2))
      SYS "timeGetTime" TO Then%
      DayNumber%=FN_today
      PinHigh=FALSE
      ENDPROC
      
      DEF FNExtractString(InStr$,WhichStr%)
      REM Extracts sub-string WhichStr% (counting from left) in InStr$. NB: Assumes single spaces between sub-strings
      LOCAL StrIndex%, Left$, Work$
      Work$=InStr$
      IF WhichStr%=1 THEN
      Left$=LEFT$(Work$,INSTR(Work$," ")-1)
      ELSE
      FOR StrIndex%=2 TO WhichStr%
        Work$=MID$(Work$, INSTR(Work$," ")+1)
        Left$=LEFT$(Work$,INSTR(Work$," ")-1)
      NEXT
      ENDIF
      =Left$
      
      DEF FNDayToString(Offset%)
      REM Formats today's date (with offset) to a string of form yyyy-mm-dd
      REM Get Julian Day Number
      Day%=FN_today+Offset%
      REM Format date string
      Date$=FN_date$(Day%, "yyy MM dd")
      REM Replace gaps with "-"
      MID$(Date$,5,1)="-"
      MID$(Date$,8,1)="-"
      =Date$
      
      DEF FNGetStringFromPort(Where%)
      LOCAL Data%, Reply$
      Reply$=""
      REPEAT
      IF EXT#Where%>0 THEN
        Data%=BGET#Where%
        IF Data%<>10 AND Data%<>13 THEN Reply$=Reply$+CHR$(Data%)
      ENDIF
      UNTIL Data%=10
      =Reply$ 



In the program "VS1" refers to the seismometer, "Velocity" is the seismic velocity and "Beam" is a status value output from the seismometer. Velocity and Beam are sent as data pairs at about 20Hz from the instrument, followed by <CR><LF>. "SSM" means Seconds Since Midnight. You can see that the program produces a single file for each day's recording.

In the code you will see that the time is initiated by reference to PC's OS time (TIME$). Originally I was using the GPS time code contained in NMEA strings to initiate the UTC timing. The GPS unit communicates via a USB-RS232 virtual driver on "virtual" COM3 whereas the VS1 uses COM1. I found that there seems to be a problem such that when COM3 has been opened and closed once, it cannot be opened again. Thus, I now set the PC clock to UTC time using the NMEATime utility before starting the program I have listed.

My use of SYS timeGetTime follows your suggestion in an earlier post, while SYS GetCommModemStatus is used as per the BB4W documentation. I am doing what I can to the best of my knowledge! Any help you can provide to improve matters would be appreciated.

Regards,

Manxman
Re: Serial port pin driving an interrupt
Post by admin on Jan 6th, 2013, 10:07pm

on Jan 6th, 2013, 7:34pm, manxman wrote:
I've no problem at all listing my code

For future reference, please put listings in 'code' tags (that's what they're for!): it will result in the use of a monospaced font and preserve indentation, making the code much easier to read, like this:

Code:
      REPEAT
        REM Check PPS pin
        SYS "GetCommModemStatus", @hfile%(VS1), ^modemstatus%
        IF modemstatus%=32 AND PinHigh=FALSE THEN
          PinHigh=TRUE
          SYS "timeGetTime" TO Then%
          SSM=(SSM+1) MOD 86400
          IF SSM=0 THEN
            PROCCloseDayFile
            DayNumber%+=1
            PROCStartDayFile
          ENDIF
        ENDIF
        IF modemstatus%<>32 AND PinHigh=TRUE THEN PinHigh=FALSE
      ENDIF 

Preserving indentation is particularly valuable for spotting errors in the program's structure, which evidently there is here because the final ENDIF lines up with the REPEAT! Did you not spot that yourself? It's also evident that you didn't run the Cross Reference utility, since that reports 'Unexpected ENDIF'! Try to make full use of the facilities provided to check the correctness of your code.

I asked how (and whether) you are avoiding using up 100% CPU time; on a quick glance I can't see anything in your program to do that, which worries me. If you haven't already done so, use Task Manager to see what the CPU usage is when your program is 'idling' - i.e. doing nothing except waiting for the next PPS event or the next received serial character. The usage should be close to zero, with nearly all the time spent in the System Idle Process.

As discussed previously the usual ways of releasing the CPU - WAIT or SYS "Sleep" - may not be ideal in your application, given its critical timing requirements. In that case you might be better off using one of the dedicated Windows waiting functions, such as SYS "WaitForSingleObject", which are designed to reactivate the waiting task as soon as an event occurs.

That's potentially another reason to consider using WaitCommEvent, because that would need to be used with WaitForSingleObject, and therefore kill two birds with one stone. So in your position I would want to change from a 'polling' method to one based on events, specifically the EV_DSR event, thereby both slashing the CPU time used and potentially reducing the likelihood of missing a PPS count.

Quote:
I found that there seems to be a problem such that when COM3 has been opened and closed once, it cannot be opened again

Have you contacted the vendor of the GPS unit about that? It would seem to be quite a serious fault, which you may find is fixed in a firmware or driver update.

Richard.

Re: Serial port pin driving an interrupt
Post by manxman on Jan 7th, 2013, 09:14am

Q: So in your position I would want to change from a 'polling' method to one based on events, specifically the EV_DSR event, thereby both slashing the CPU time used and potentially reducing the likelihood of missing a PPS count.

A: Once again, please show me how to do this.

Q: Have you contacted the vendor of the GPS unit about that?

A: Yes.

Regards,

Manxman


Re: Serial port pin driving an interrupt
Post by admin on Jan 7th, 2013, 11:56am

on Jan 7th, 2013, 09:14am, manxman wrote:
Once again, please show me how to do this.

All the information you could possibly need, and more, can be found at MSDN under the various API functions:

http://msdn.microsoft.com/en-gb/library/windows/desktop/aa363479.aspx
http://msdn.microsoft.com/en-gb/library/windows/desktop/aa363435.aspx
http://msdn.microsoft.com/en-gb/library/windows/desktop/ms687032.aspx

The BB4W structure declarations can be got from API Viewer, and the Windows Constants utility will take care of defining the constants. You can either create the event you need using CreateEvent or, more easily, use the @hevent% System Variable.

It's a technique which is new to me too, but I'm confident it is the right way to go in your application. If you get stuck with the BBC BASIC translation, just ask here again.

Richard.

Re: Serial port pin driving an interrupt
Post by admin on Jan 9th, 2013, 1:41pm

on Jan 7th, 2013, 11:56am, Richard Russell wrote:
It's a technique which is new to me too

Although I've not got any easy way of simulating a PPS input to a modem-status line, I realised that on one of my other PCs I'm receiving a demodulated MSF signal that way:

http://www.rtrussell.co.uk/msf/msf.html

The pulse widths are very variable, but it has allowed me to demonstrate that using WaitCommEvent in conjunction with WaitForSingleObject do indeed allow me to monitor the input transitions (in my case they're on RLSD rather than DSR).

I can't easily tell whether I'm detecting the edge timings any more accurately than I would by simple polling, but I'm pretty confident that waiting in WaitForSingleObject rather than Sleep should give benefits. After all, Sleep is effectively saying to Windows: "I've got nothing to do right now, go off and do something else and return to me at your leisure" whereas WaitForSingleObject is saying "I'm very interested in this event, return control to me as soon after it happens as you can"!

Later: Moving the window around doesn't seem to affect the timing significantly.

Richard.

Re: Serial port pin driving an interrupt
Post by admin on Jan 11th, 2013, 08:40am

The lack of feedback is somewhat disconcerting. If you are no longer interested in the results of my experiments, or you have abandoned the project, please let me know so I don't waste further time on it.

This is the latest version of my 'proof of principle' program, where WaitCommEvent and associated APIs have been used in the most straightforward fashion, exactly as documented on MSDN:

Code:
      REM Monitor incoming Serial Data and a Pulse Per Second signal.
      REM By Richard Russell, http://www.rtrussell.co.uk/, 10-Jan-2013
      
      REM!WC Windows Constants:
      EV_RLSD = 32
      EV_RXCHAR = 1
      MS_RLSD_ON = &80
      HIGH_PRIORITY_CLASS = &80
      NORMAL_PRIORITY_CLASS = 32
      WAIT_TIMEOUT = 258
      
      REM Settings:
      SerialPort$ = "COM1:19200,N,8,1"
      PPS_Status% = MS_RLSD_ON
      PPS_Event% = EV_RLSD
      PPS_Edge% = FALSE
      
      REM Initialise:
      MODE 7
      *FONT BBCWin
      SYS "timeBeginPeriod", 1
      SYS "GetCurrentProcess" TO hprocess%
      SYS "SetPriorityClass", hprocess%, HIGH_PRIORITY_CLASS
      DIM overlapped{Internal%, InternalHigh%, Offset%, OffsetHigh%, hEvent%}
      
      REM Boilerplate:
      ON ERROR PROCcleanup : SYS "MessageBox", @hwnd%, REPORT$, 0, 0 : QUIT
      ON CLOSE PROCcleanup : QUIT
      
      REM Open serial port:
      port% = OPENUP(SerialPort$)
      IF port% = 0 ERROR 100, "Cannot open port"
      overlapped.hEvent% = @hevent%
      
      REM Set event(s) to be monitored:
      SYS "SetCommMask", @hfile%(port%), PPS_Event% OR EV_RXCHAR TO res%
      IF res% = 0 ERROR 100, "SetCommMask failed"
      
      REM Main loop:
      SSM% = 0
      Was% = 0
      Reply$ = ""
      REPEAT
        REM Await serial event:
        SYS "WaitCommEvent", @hfile%(port%), ^evtmask%, overlapped{} TO res%
        IF res% = 0 THEN
          REPEAT
            SYS "WaitForSingleObject", @hevent%, 2000 TO res%
          UNTIL res% <> WAIT_TIMEOUT
          SYS "GetOverlappedResult", @hfile%(port%), overlapped{}, ^temp%, 0
        ENDIF
        
        REM Process Pulse Per Second events:
        IF evtmask% AND PPS_Event% THEN
          SYS "GetCommModemStatus", @hfile%(port%), ^status%
          IF (status% AND PPS_Status%)==0 EOR PPS_Edge% THEN
            SYS "timeGetTime" TO Was%
            SSM% = (SSM% + 1) MOD 86400
          ENDIF
        ENDIF
        
        REM Process received data events:
        IF evtmask% AND EV_RXCHAR THEN
          WHILE EXT#port%
            Reply$ += CHR$BGET#port%
          ENDWHILE
          IF RIGHT$(Reply$) = CHR$10 THEN
            SYS "timeGetTime" TO Now%
            PRINT SSM% + (Now%-Was%)/1000 " " Reply$ ;
            Reply$ = ""
          ENDIF
        ENDIF
      UNTIL FALSE
      PROCcleanup
      END
      
      REM Cleanup before exit:
      DEF PROCcleanup
      LOCAL hprocess%
      SYS "GetCurrentProcess" TO hprocess%
      SYS "SetPriorityClass", hprocess%, NORMAL_PRIORITY_CLASS
      ENDPROC 

Richard.
Re: Serial port pin driving an interrupt
Post by manxman on Jan 15th, 2013, 11:03am

Having been away from my desk for some time I have now had a chance to evaluate your alternative solution to this timing problem.

For those new to this thread, the circa 20Hz data stream from my seismometer circuit board is input via the COM1 serial port, with 1 pulse per second TTL pulses from a GPS module also being applied to pin 6 (DSR). Since this bare circuit board does not sense ground motion, the seismic signals remain fixed at 1027 and 0 in this instance. I have changed your program to write data to file and a sample of the results of a test are:

Seconds Velocity Beam
3743.913 1027 0
3743.964 1027 0
3744.015 1027 0
3744.064 1027 0
3744.115 1027 0
3744.166 1027 0
3744.217 1027 0
3744.267 1027 0
3744.317 1027 0
3744.368 1027 0
3744.419 1027 0
3744.469 1027 0
3744.52 1027 0
3744.57 1027 0
3744.621 1027 0
3744.671 1027 0
3744.722 1027 0
3744.772 1027 0
3744.823 1027 0
3744.873 1027 0
3744.924 1027 0
3744.975 1027 0
3745.025 1027 0
3745.075 1027 0
3745.126 1027 0
3745.177 1027 0
3745.228 1027 0
3745.277 1027 0
3745.328 1027 0
3745.379 1027 0
3745.43 1027 0
3745.479 1027 0
3745.53 1027 0
3745.581 1027 0
3745.632 1027 0
3745.682 1027 0
3745.732 1027 0
3745.783 1027 0
3745.834 1027 0
3745.884 1027 0
3745.935 1027 0
3745.985 1027 0
3746.036 1027 0
3746.086 1027 0
3746.137 1027 0
3746.187 1027 0
3746.238 1027 0

Excellent! The milliseconds accumulate linearly between PPS pulses in the manner expected. I don't fully understand the workings of your code, however, since many of the instructions are unfamiliar. Where, for example, is the DSR pin assignment made?

On another tack, I have struggled to configure @% to ensure 3 decimal places, including instances when the third significant figure is zero. With @%=&20305 the number 123.45 converts to the string using STR$ as 123.450 but when this string is written to a disk file it appears as 123.45

Many thanks for you expert help with this timing topic.

Regards,

Manxman

Re: Serial port pin driving an interrupt
Post by admin on Jan 15th, 2013, 1:10pm

on Jan 15th, 2013, 11:03am, manxman wrote:
many of the instructions are unfamiliar

I do hope you will make an effort to understand them. The Windows APIs are used exactly as they are documented on MSDN.

Quote:
Where, for example, is the DSR pin assignment made?

Here (both the input signal PPS_Status% and the event PPS_Event% must be changed):

Code:
      PPS_Status% = MS_RLSD_ON
      PPS_Event% = EV_RLSD 

I set it to RLSD because that's where my MSF data arrives and I certainly didn't want to re-wire the plug!

Quote:
With @%=&20305 the number 123.45 converts to the string using STR$ as 123.450

I don't think that can be right. If you don't set the STR$ bit in @% STR$ should use the default G9 format, which is 123.45, as it says here:

http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin6.html#strformat

"Byte 3 affects the format of the string generated by the STR$ function. If Byte 3 is 1 the string will be generated according to the format set by @%, otherwise the G9 format will be used".

Richard.
Re: Serial port pin driving an interrupt
Post by manxman on Jan 15th, 2013, 5:29pm

Thanks for the clarification.

I will continue experimenting with your improved timing code which, I expect, will now form the basis of timestamping for my seismometer array data.

I'll have another look at the use of @% in order to try and force all string output to format to 3 decimal places.

Regards,

Manxman