Author |
Topic: Serial port pin driving an interrupt (Read 6280 times) |
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Serial port pin driving an interrupt
« Reply #15 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.
|
|
Logged
|
|
|
|
manxman
Developer
member is offline


Gender: 
Posts: 21
|
 |
Re: Serial port pin driving an interrupt
« Reply #16 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
|
| « Last Edit: Jan 7th, 2013, 09:15am by manxman » |
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Serial port pin driving an interrupt
« Reply #17 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.
|
| « Last Edit: Jan 6th, 2013, 10:10pm by admin » |
Logged
|
|
|
|
manxman
Developer
member is offline


Gender: 
Posts: 21
|
 |
Re: Serial port pin driving an interrupt
« Reply #18 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
|
| « Last Edit: Jan 7th, 2013, 09:15am by manxman » |
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Serial port pin driving an interrupt
« Reply #19 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.
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Serial port pin driving an interrupt
« Reply #20 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.
|
| « Last Edit: Jan 10th, 2013, 08:18am by admin » |
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Serial port pin driving an interrupt
« Reply #21 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.
|
| « Last Edit: Jan 11th, 2013, 08:42am by admin » |
Logged
|
|
|
|
manxman
Developer
member is offline


Gender: 
Posts: 21
|
 |
Re: Serial port pin driving an interrupt
« Reply #22 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
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Serial port pin driving an interrupt
« Reply #23 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.
|
|
Logged
|
|
|
|
manxman
Developer
member is offline


Gender: 
Posts: 21
|
 |
Re: Serial port pin driving an interrupt
« Reply #24 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
|
|
Logged
|
|
|
|
|