FlexBASIC feature request

I do this a lot:
'set timeout to 5ms
timeout = timer + 5

do
    if timer > timeout then exit do
loop


Or is this the way to do it?:
timeout = getcnt() + (clkfreq/200)

do
  if getcnt() > timeout then exit do
loop


Would there be problems with getcnt() rollover?

Comments

  • localrogerlocalroger Posts: 3,424
    edited 2020-11-09 - 19:50:17
    Yes, cnt rollover will getcha in the second example. Not sure this is correct FB format but here's the logic you need to use, doing the subtraction each loop:
    timeout = clkfreq / 200
    timestart = getcnt()
    do
      if getcnt() - timestart > timeout then exit do
    loop
    

    This way even if the subtraction spans a rollover, the result will still be positive and compare properly against the timeout value.
  • Took a while for me to get my head around this....many thanks.

    I still need a general purpose ms timer as I like to monitor the response times of many actuators for diagnostic purposes.

    One of my processes only needs a 1ms scan so I think I will increment a variable in that cog, after a waitcnt and use that as a reference :smile:
  • Even if you use a millisecond timer you have the rollover problem every 50 days or so.
  • Not a problem. A typical production cycle is less than one minute so I simply reset the counter to zero.

    If I name it Timer:
    Timer=0
    
    Execute some process code 
    
    Timetaken=Timer
    
    

    Micromite BASIC has a 64bit ms counter which I would never have to worry about but I still zero it because I can :smile:
  • JonnyMacJonnyMac Posts: 7,110
    edited 2020-11-12 - 19:09:28
    The Arduino has a convenient function called millis() that returns the milliseconds since reset. We don't yet have that in Spin, so I coded it like this:
    pub millis() : result | now0, now1, cf
    
      org
                    getct     now1                          wc      ' snapshot 64-bit cnt register
                    getct     now0
    
                    stalli                                          ' hold interrupts during division
                    rdlong    cf, #$44                              ' read ticks per second
                    qdiv      cf, ##1000                            ' calc ticks per ms
                    getqx     cf
                    allowi                                          ' enable interrupts
                    
                    nop                                             ' let interrupts be processed
    
                    stalli
                    setq      now1                                  ' divide system counter by ticks/ms
                    qdiv      now0, cf    
                    getqx     result
                    allowi
      end
    
    Since FlexBASIC allows inline assembly, you should be able to adjust this -- I don't know FlexBASIC well enough to do that. I have tested this method in Spin and it works (it's based on Chip's code getsec() code in the interpreter).
  • As a mental exercise, I gave it a whack. After looking at the compiler listing to see which scratch variables I could use, I came up with this.
    function millis() as uinteger
    
      asm
                    getct     arg02                         wc      ' snapshot 64-bit cnt register
                    getct     arg01
    
                    stalli                                          ' hold interrupts during division
                    rdlong    arg03, #$44                           ' read ticks per second
                    qdiv      arg03, ##1000                         ' calc ticks per ms
                    getqx     arg03
                    allowi                                          ' enable interrupts
                    
                    nop                                             ' let interrupts be processed
    
                    stalli
                    setq      arg02                                 ' divide system counter by ticks/ms
                    qdiv      arg01, arg03    
                    getqx     result1
                    allowi  
      end asm
    
    end function
    
    It compiles, but always returns 0. Looking at the new listing I see why: the compiler replaces the getqx result1 line with mov result1, #0.
    005dc                 | ' function millis() as uinteger
    005dc                 | _millis
    005dc                 | ' 
    005dc                 | '   asm
    005dc     1A FA 70 FD | 	getct	arg02 wc
    005e0     1A F8 60 FD | 	getct	arg01
    005e4     24 42 60 FD | 	stalli
    005e8     44 FC 04 FB | 	rdlong	arg03, #68
    005ec     01 00 00 FF 
    005f0     E8 FD 14 FD | 	qdiv	arg03, ##1000
    005f4     18 FC 60 FD | 	getqx	arg03
    005f8     24 40 60 FD | 	allowi
    005fc     00 00 00 00 | 	nop
    00600     24 42 60 FD | 	stalli
    00604     28 FA 60 FD | 	setq	arg02
    00608     7E F8 10 FD | 	qdiv	arg01, arg03
    0060c     24 40 60 FD | 	allowi
    00610     00 EA 04 F6 | 	mov	result1, #0
    00614                 | _millis_ret
    00614     2D 00 64 FD | 	ret
    

    I don't know why. It seems odd that the compiler would arbitrarily change inline assembly code. Maybe Eric can assist -- or even add the millis() function to FlexBASIC.
  • @JonnyMac

    Hey Jon.

    Isn't that solution still prone to falling into the rollover trap?

    BTW, congrats on the excellent N&V article. If it was mentioned here then I missed it. I found the link on another forum.
    The bonus was the comments section 😂😂😂. Some guy made a snide remark and I'm pretty sure the reply came from @ManAtWork, right? It was brilliant :+1:

    As they say; the article needs to go viral :smile:
  • Until there is a language with 64-bit variables, yes. Treating the millis() value as an unsigned long, the roll-over is 49.7 days. I have a variation that let's me reset a sync point if I want, but that same capability is easily done in high-level code. As Roger pointed out above, if you do the subtraction in the loop the roll-over is not a problem.

    Using the 64-bit version of cnt gives you more range -- but you have to do this from assembly. You can use the lower half of cnt (getct() in the P2) if your events are very short. I run at 200MHz, so using the lower half of cnt means I have to access that register faster than every 21.4s, otherwise I will get a roll-over error.

    I'm glad you enjoyed the article.
  • JonnyMacJonnyMac Posts: 7,110
    edited 2020-11-12 - 22:15:38
    Got it! I think... we'll have to check with @ersmith on the rules of ASM..END ASM. That said, this works. Demo program is attached.
    function millis() as uinteger
    
      dim as uinteger now1, now0
      dim as uinteger cf
      dim as uinteger ms
    
      asm
                    getct     now1                          wc      ' snapshot 64-bit cnt register
                    getct     now0
    
                    rdlong    cf, #20                               ' read ticks per second
                    qdiv      cf, ##1000                            ' calc ticks per ms
                    getqx     cf
                    
                    setq      now1                                  ' divide system counter by ticks/ms
                    qdiv      now0, cf    
                    getqx     ms
      end asm
      
      return ms
    
    end function
    
    Once I defined local variables and looked in the right place for the system frequency (I checked the listing file for getsec()), it came together.
  • @JonnyMac : thanks for posting that code. I think you've groked asm/end asm just fine :).

    Ironically, there is a builtin _getms() function, but I forgot to document it and/or provide an alias for BASIC. @Mickster : Jon's function above should work, or you could call "_getms()" (underscore at the beginning) which does the same thing: provides a 32 bit millisecond timer.

    The next version of FlexBASIC will have a "getms()" function (no underscore). The underscore version will continue to be supported.
  • @Mickster If you throw Spin2 into your mix of programming languages, Chip just showed a way to add custom interrupt handlers. I took his blinking LED example and converted it to a free running timer that is installed in the Spin interpreter cog. I have two variations: one that does milliseconds, the other that is a 24-hour RTC with hours, minutes, seconds, and 1/100ths seconds. Both allow the timer to be put on hold and updated.

    See this thread: http://forums.parallax.com/discussion/172453/free-running-timer-in-the-spin-interpreter-cog#latest

    With FlexBASIC, you could probably do something similar by creating a timer cog with access methods that suited your needs. Given that the Flex system compiles to native PASM, you don't have the Spin cog like I do to install an ISR within.
  • @ersmith
    @JonnyMac

    Thanks guys, it's all looking great. :+1:

    Away working right now. Didn't bring my Prop stuff (easily sidetracked)
Sign In or Register to comment.