Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 09:08 03 May 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Continuation from cog31s GPS issue.

     Page 2 of 3    
Author Message
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 12:23pm 08 Oct 2019
Copy link to clipboard 
Print this post

  matherp said  The search funmction is difficult on the new BB but ages ago I wrote a Cfunction that worked as a monostable. i.e. an external interrupt on one pin (the PPS output from the GPS) could trigger a timed pulse on another pin to a very high level of accuracy. If you search for it you should find it and I seem to remember it worked on MM2 and possibly MM+


thank. Yes it is a good idea to use your function for my relays. In this case, I have always same time for external pulse.
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2290
Posted: 12:29pm 08 Oct 2019
Copy link to clipboard 
Print this post

the following piece of code may be helpful, it is adapted from something i wrote a while back:

SetPin 26, inth, trimISR

Do
 Print count, Str$(E,3,2), Str$(I,3,2),, trim, Time$, Timer

 Do: Loop Until flag

 flag = 0             ' comment out this line to consume computrons
Loop


trimISR:
 count = Timer Mod 1000
 E = 500 - count
 I = ((I * 0.8) + (E * 0.2))

 trim = Fix((E * 0.3) + (I * 0.7))
 If trim < -31 Then trim = -31
 If trim > 31 Then trim = 31

 Option clocktrim trim
 flag = 1
IReturn


wire up the 1pps output line from your GPS module to pin 26. after running for a few tens of seconds the micromite's internal timer functions will become synchronized to the 1pps pulse from the GPS module. you can then just read the value of timer in your main loop and take action on it. something like:


do
 print "waiting"
loop until button is pressed

turn on relay and set t0%=timer

do
 print timer-t0%
 if timer>(t0%+1800000) then turn off relay
loop until relay is off



cheers,
rob   :-)
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 12:43pm 08 Oct 2019
Copy link to clipboard 
Print this post

  JohnS said  

Why not get the code and study it?  Geoff does allow that, and readily.

John




uart_interrupt.zip
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 01:12pm 08 Oct 2019
Copy link to clipboard 
Print this post

Hi Robert.rozee

Thanks for your help.
Your program is very interesting (I just try it) but not quite adapted to my needs. In fact I especially need my relay to switch to "00 sec" of the GPS and it displays the time immediately after (currently it displays the time with several hundreds of miiliseconds after), and finally that it fall back the relay after 20ms. Peter's function is excellent for that.
however, I keep your idea and try to understand it so that I can use it for another application that I have in mind.
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3661
Posted: 05:42pm 08 Oct 2019
Copy link to clipboard 
Print this post

  goc30 said  
  JohnS said  

Why not get the code and study it?  Geoff does allow that, and readily.

John




uart_interrupt.zip


How does that deal with the issues raised?  Is it the code used by MMBasic?

John
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1095
Posted: 12:41am 10 Oct 2019
Copy link to clipboard 
Print this post

Regarding the interrupt timing, I ran the following code on a 170 and fed characters into it from a terminal.

The delay between the end of the stop bit and the Rx interrupt service routine activity starting was around 80uS - it varied a little by 5uS or so and this is because there is no way of determining EXACTLY where in the do/loop sequence the interrupt occurs.

This is exactly in line with what you would expect as each MMBasic line interpretted takes on average 30uS so the jump to the interrupt service routine then get the character with INPUT then pulse the pin ~ jump time + 30uS + 30us.
If you omit the INPUT statement, the lag is less than 10uS between the end of the stop bit in the received character and the leading edge of the pulse



This is absolutely repeatable so any delays in servicing an interrupt are solely due to what other activities the 170/MMBasic is trying to do at the time the interrupt occurs.

Looking at the interrupt priority order, only higher priorities will break into the processing of the COM interrupt service routine.


SETPIN 18,DOUT
PIN(18) = 0

OPEN "COM1:9600,512,COM_Int" AS #1

DO
LOOP

SUB COM_Int
 A$ = INPUT$(1,#1) ' with this statement in, lag is roughly 80uS
 PIN(18) = 1       '  - with it out, lag is less than 10uS
 PIN(18) = 0
END SUB



panky
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 02:24am 10 Oct 2019
Copy link to clipboard 
Print this post

Hi Panky

very good job! thanks!

my problem is that I receive 470 to 500 bytes each second. And I must do something with this byte (not only read). Minimum to do is to store, and after that, background tasks can work with frames stored in asynchronuis time, they have time to do that. In industrial realtime process it work (very well) like that (see modbus protocol with 128 byte max for each frame)

my minmal subroutine

sub spcomrec4
Local tx$ As string 'length 3
Local i1 As integer
Local i2 As integer
Local a$ As string length 1
local vai as integer
 i1=Loc(#2)
 if i1=0 then
   exit sub
 else
   tx$=Input$(i1,#2)
   For i2=1 To i1
     a$=Mid$(tx$,i2,1)
     vai=asc(a$)
     if a$="$" then
       ptWrite=ptWrite+1
       if ptWrite>cnbbuf then ptWrite=1
       tmbuf(ptWrite)=""
       flgtmbuf(ptWrite)=1
     end if
     if vai=10 then  
       flgtmbuf(ptWrite)=2
     else
       if vai>31 then tmbuf(ptWrite)=tmbuf(ptWrite)+a$
     end if
   next i2
 end if
end sub


if time to execute 1 line basic, is 30us, I don't understand.
You can see that in recept, I have just to know where are begin and end of frame.
It mean that for first byte, I "use" 18 lines (18*30=540us) each millisec, for last byte i "use" 12 lines (13*30=360us) each millisec, and for all others byte I "use" 240us (8*30us) each millisec . It mean that during reception of all frames, I have 50% to 75% time free to continue my others tasks. In this case, why main loop is executed in 600ms??
remenber that in realtime, execution time of 2 tasks is not an simple time addition of each, but it depend of free time given by most priority task. If main loop task execute routine in 100ms and priority task use 50% of CPU time, that mean that main prog is executed in 200ms, if priority task use 10%, main task execut in 110ms. In my case com sub give (max) 50% free time. it mean that my main loop must be executed in 100ms when i receiv frames, and in 50ms when there is not frames.

tomoroow I connect my scope, and I put pin relay at "on" in begin of routine com, and at "off" on exit sub, to know how many time I spend in my sub
Edited 2019-10-10 12:29 by goc30
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3661
Posted: 07:48am 10 Oct 2019
Copy link to clipboard 
Print this post

Some thoughts:
1. each individual Local takes time that could be reduced by combining them (a sensible thing to do in time-critical code)
2. for testing I would check whether Loc(#2) is ever zero - it should not be!
3. for testing I would check whether Loc(#2) is ever more than one - it should not be!
4. however, coding for both is fair to include in a working system
5. even so, the code can be made faster by avoiding Mid$ - remove the tx$=Input$(i1,#2), remove a$=Mid$(tx$,i2,1) and replace it with a$=Input$(1,#2)
6. that means you do not need tx$ so can remove it completely and it will also speed up its Local
7. the code is not optimal for input of "$" - you could change tmbuf(ptWrite)="" to tmbuf(ptWrite)=a$ and add an exit sub
8. also, it is likely faster to change the if a$="$" then into if vai=36 then - but less readable and probably not important

John
Edited 2019-10-10 17:49 by JohnS
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1095
Posted: 08:24am 10 Oct 2019
Copy link to clipboard 
Print this post

Right on on the money John. Further to that, it might be worth considering  using the long string cfunction - that way, you could set the interrupt to trigger at something like 470 characters or so (which is around a full frame of the 8 gps records), use the interrupt to just grab them into a long string and return,
. Then you can analyse the complete stream in your main loop.

panky.
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3661
Posted: 10:01am 10 Oct 2019
Copy link to clipboard 
Print this post

I think he's trying to do precision timing so that probably rules that out.

It occurs to me that the string concatenation
tmbuf(ptWrite)=tmbuf(ptWrite)+a$
may take a while.  I don't know how long but the worst case (length 10 it seems) ought to be checked.

John
Edited 2019-10-10 20:03 by JohnS
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 11:15am 10 Oct 2019
Copy link to clipboard 
Print this post

hi John

  JohnS said  Some thoughts:
1. each individual Local takes time that could be reduced by combining them (a sensible thing to do in time-critical code)
2. for testing I would check whether Loc(#2) is ever zero - it should not be!
3. for testing I would check whether Loc(#2) is ever more than one - it should not be!
4. however, coding for both is fair to include in a working system
5. even so, the code can be made faster by avoiding Mid$ - remove the tx$=Input$(i1,#2), remove a$=Mid$(tx$,i2,1) and replace it with a$=Input$(1,#2)
6. that means you do not need tx$ so can remove it completely and it will also speed up its Local
7. the code is not optimal for input of "$" - you could change tmbuf(ptWrite)="" to tmbuf(ptWrite)=a$ and add an exit sub
8. also, it is likely faster to change the if a$="$" then into if vai=36 then - but less readable and probably not important

John


the problem of the management of a gps is not in the most general case, but in the particular cases which do not have to leave the program on an error.

(2) when we start the program, we have either a chain too long in the uart buffer (too much time between setup and the first interruption), or an incomplete string starting in the middle of a frame, or an incomprehensible string (frame error).

(3-4-5-6) indeed this is the problem, why when receiving an interrupt "character in buffer" I have several characters and not one. In the case of the module that I put above, I have 3 or 4 characters in the buffer, from which the obligation of the $ tx and the loop of reading and storage. And if I'm just reading (without doing anything else), I sometimes have 2 characters in the buffer.

(7). no it is not ideal, because in your solution, I would have to add a test to know if I treat the first character (which in your example, would already be in the buffer), and in this case, I do not add it, or not and there I add it, so 1 "if / then" for each character, to add

(8), the conversion of the character to its ascii code is used twice (vai> 31, and vai = 10), so it is simpler and faster to convert it once at the reception, and to use the value already converted in the 2 tests, rather than converting each time.
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 11:24am 10 Oct 2019
Copy link to clipboard 
Print this post

hi panky

  panky said  Right on on the money John. Further to that, it might be worth considering  using the long string cfunction - that way, you could set the interrupt to trigger at something like 470 characters or so (which is around a full frame of the 8 gps records), use the interrupt to just grab them into a long string and return,
. Then you can analyse the complete stream in your main loop.

panky.


"GPS" frames are not sames. It depend of:
1 - sat signal level: If you have no sat, frames are very shorts (max 50/70 bytes)
2 - if you have an complete news modules, you can receive more frames (gps and clonass, and galileo, and chines "gps")
3 - with best gps (and more expensives) modules GPs, you can change com speed, and, more important, you can say what type of frame you want to receive (ex just RMC frames and not others)

it is for thoses reasons, that I must work byte by byte an frames by frames. After that I have time to process frames. I want to have only one subroutine who can process all of my app (who use gps signal)
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2290
Posted: 12:39pm 10 Oct 2019
Copy link to clipboard 
Print this post

the fundamental problem seems to be that:
1. while the 1pps pulse is generated accurately on the second,
2. the NMEA data strings occur and are decoded some indeterminate time after this pulse. there is no way to tame this indeterminacy, as it arises from several different sources, none of which you can control - the GPS receiver, the MMbasic interpreter, the other hardware attached to the mx170.

i feel you should be using the 1pps signal to obtain an accurate marker for the start of each second (one way being to trim the micromite millisecond timer using this pulse as shown earlier), and then - if you really do need to - use the NMEA string to obtain the H:M:S data.

is it the case that you want to build several boxes that are synchronized? if so, you can combine the NMEA values for H:M:S with timer mod 1000 (for the milliseconds). this should allow you to obtain synchronization within +/-1ms between units as long as they have a good GPS time signal.


cheers,
rob   :-)
Edited 2019-10-10 22:41 by robert.rozee
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3661
Posted: 01:43pm 10 Oct 2019
Copy link to clipboard 
Print this post

To fetch any chars on program start, just call spcomrec4 as long as loc(#2) is not zero.

I really would worry if you ever find after that the situations I mentioned (call with loc as zero or greater than one) for both mean you have a serious fault.

Regardless, it makes sense to test if vai=36 rather than a$="$", combine the Locals and check the worst case string concatenation.

If it still isn't fast enough then I suspect you have a problem in the rest of your code (maybe a higher priority interrupt taking too long) or you should do less work in spcomrec4.

If you can make sure loc(#2) is only ever one in spcomrec4 (when used as an interrupt) you can then remove the FOR loop and simplify the code.

The initial situation on program start where loc(#2) may be greater than one can then be a loop until it's zero.

John
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 08:07am 11 Oct 2019
Copy link to clipboard 
Print this post

YESSSSS

   

stop all!!!, I have found problem



I send (with other mx170) 2 frames equals, first with 80* char "U", and second with 80* chr$(170). That make 1 frame with all hex$=55 and second frame with all hex=AA
time between 2 frame is 10ms (pause(10)) and each 2 frames are send each second.

my subroutine com
first versus (sub 1)
sub spcomrec4
pin(26)=1
Local tx$ As string 'length 3
Local i1 As integer
Local i2 As integer
Local a$ As string length 1
local vai as integer
 i1=Loc(#2)
 if i1=0 then
   print "error i1=";str$(i1)
   exit sub
 else
   tx$=Input$(i1,#2)
   nbcar=nbcar+i1
   'print i1  'i1=number of char in receive buffer
   'print tx$;
 end if  
 pin(26)=0
end sub


result
 
time   0.233  nb char=166  <-----
time   0.059  nb char=0
time   0.059  nb char=0
time   0.059  nb char=0


you can see that sometimes I spend in main loop more time than frame length
time without frames is main loop time (59ms to 61ms)

second versus (sub 2)

Dim tx$ As string 'length 3
Dim i1 As integer
Dim i2 As integer
Dim a$ As string length 1
Dim vai as integer

'.....
main loop
'......

sub spcomrec4
pin(26)=1
 i1=Loc(#2)
 if i1=0 then
   print "error i1=";str$(i1)
   exit sub
 else
   tx$=Input$(i1,#2)
   nbcar=nbcar+i1
   'print i1  'i1=number of char in receive buffer
   'print tx$;
 end if  
 pin(26)=0
end sub

result
time   0.061  nb char=16
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.102  nb char=166  <-----
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.065  nb char=11    <----
time   0.086  nb char=150  <----
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.082  nb char=46  <----
time   0.069  nb char=115  <----
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.097  nb char=80  <----
time   0.061  nb char=81  <----

you can see that in second case, it work very very well, (as i want)
the difference between sub1 and sub2, I use "Locale" or "Dim" function
it seem that local var are created each time subroutine is called, and this is too long (it may bee more than 1 ms)
time without frames is main loop time (61ms)

p.s: the test "if loc(2) =0" is necessary. i receive often, but this is an other probleme.

time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.102  nb char=91
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
time   0.061  nb char=16
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.102  nb char=166
time   0.061  nb char=0



finally that mean that in interrupt or fast subroutine, never use "Local" vars

thank to all for your help      
Edited 2019-10-11 18:10 by goc30
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 02:33pm 11 Oct 2019
Copy link to clipboard 
Print this post

Geoff may correct me on this but if you really want maximum speed in the interrupt:
Dimension the variables used in the interrupt at the very top of the main program.
Use very short variable names, preferably different single letters.
Avoid %, !, or $.
Remove all non-mandatory spaces and all comments and blank lines from the interrupt
Doing this will ensure the parser finds the variables as fast as possible in the interrupt.
You can also investigate the use of STATIC variables in the interrupt which will be faster than LOCAL but not as fast as GLOBAL variables declared first.
Edited 2019-10-12 00:35 by matherp
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 04:15am 12 Oct 2019
Copy link to clipboard 
Print this post

Hi peter

  matherp said  Geoff may correct me on this but if you really want maximum speed in the interrupt:
Dimension the variables used in the interrupt at the very top of the main program.
Use very short variable names, preferably different single letters.
Avoid %, !, or $.


good idea, I change my prog

  matherp said  
Remove all non-mandatory spaces and all comments and blank lines from the interrupt
Doing this will ensure the parser finds the variables as fast as possible in the interrupt.

I do that in final step, when prog is ok

  matherp said  
You can also investigate the use of STATIC variables in the interrupt which will be faster than LOCAL but not as fast as GLOBAL variables declared first.

in interpreted basic, I don't understand the utility of "static" vars in subroutine.
in compiled, I understant because vars are not initialized at each call and keep value, but in MMbasic, I think that is not possible
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 11:54pm 12 Oct 2019
Copy link to clipboard 
Print this post

Hello all

I come back with my gps problems. and of courses, a new problem


my pb is false timing of "settick" function.
I explain
I have my uart interrupt (who work weel), and hardware interrupt (from gps module tick 1 sec). and I need an timer internal interrupt, for that , I use "SETTICK 10, sub1,1" instruction.

if I close my com port (without interrupts), it work normally (100 call each seconde)
if I use Com port in interrupt mode (to receive gps frames), sub1 work only 41 to 43 time each second and most important,some time it can take 600ms between 2 calls.

my question is:
can an tick interrupt be masked by other interrupt and in this case, interrupt is lost, or tick interrupt is buffered and when most priority interrupt is finish, tick interrupt is send to programm??
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3661
Posted: 03:48pm 13 Oct 2019
Copy link to clipboard 
Print this post

  goc30 said  p.s: the test "if loc(2) =0" is necessary. i receive often, but this is an other probleme.

time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.102  nb char=91
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
error i1=0
time   0.061  nb char=16
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.061  nb char=0
time   0.102  nb char=166
time   0.061  nb char=0




I think that error means something is wrong.

Are you perhaps, due to going around your FOR loop, removing an extra character which has flagged an interrupt?  I suspect so, i.e. by having the loop you then cause the error.

John
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 04:43pm 13 Oct 2019
Copy link to clipboard 
Print this post

  JohnS said  

I think that error means something is wrong.

Are you perhaps, due to going around your FOR loop, removing an extra character which has flagged an interrupt?  I suspect so, i.e. by having the loop you then cause the error.

John


uart interrupt is send when one (minimum) char is in buffer. if I receive interrupt and "loc(#n)" say there is no char, this is wrong!!

my code

sub spcomrec5
ai1=Loc(#2)
if ai1=0 then
  print "error i1=";str$(ai1)
  exit sub
else
  dtx$=Input$(ai1,#2)
  nbcar=nbcar+ai1
end if  
end sub

ir give

> run
error i1=0
  0.012
  0.012
error i1=0
  0.014
error i1=0
error i1=0
error i1=0
  0.013
error i1=0
  0.014
error i1=0
error i1=0
error i1=0
error i1=0
  0.012

where is FOR loop ??
 
     Page 2 of 3    
Print this page
© JAQ Software 2024