Jump to content
43oh

[Tiva] delayMicroseconds unreliable


Recommended Posts

  • Replies 31
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

I'm not paying a lot of attention to this because it's Energia and Tiva neither of which I use right now, but for low-overhead delays of reasonable duration you might want to look at how BSPACM does i

@@energia Patched version of delayMicroseconds for lm4f/tiva wiring.c   void delayMicroseconds(unsigned int us) { // Systic timer rolls over every 1000000/SYSTICKHZ microseconds if (us > 10000

I think I spot the problem with the CC3200. [ebrundic@spock cc3200]$ grep -i systick * grep: driverlib: Is a directory Energia.h:void initSysTick(); Energia.h:void registerSysTickCb(void (*userFunc)

I'm not paying a lot of attention to this because it's Energia and Tiva neither of which I use right now, but for low-overhead delays of reasonable duration you might want to look at how BSPACM does it. For microsecond counts you should just be able to convert the count to cycles based on the CPU frequency. Wouldn't be dependent on systick then.

 

There's some discussion of this on the Stellarisiti forums somewhere.

 

For what that's worth.

Link to post
Share on other sites

 

These are confusing me a bit-

        // Systick timer rolls over every 1000000/SYSTICKHZ microseconds 
        if (us > 1000000UL/SYSTICKHZ/10*8) {
                delay(us / 1000);  // delay milliseconds
 
If I'm not mistaken, that means if us > 125 (1000000 / 100 / 80) then it tries to do a delay(125/1000), or a delay(0) in other words.  I think I'll just make that if (us > 999) since SYSTICKHZ is 100 anyhow (every 10ms).
edit: er no, that would cause an infinite loop, so it's if (us > 9999) ... that should work.
 

 

I read that calculation as:  (1000000 / 100 / 10) * 8 and not (1000000 / 100 / (10*8), a factor of 64

 

David

Link to post
Share on other sites

These are confusing me a bit-

        // Systick timer rolls over every 1000000/SYSTICKHZ microseconds 

        if (us > 1000000UL/SYSTICKHZ/10*8) {

                delay(us / 1000);  // delay milliseconds

 

If I'm not mistaken, that means if us > 125 (1000000 / 100 / 80) then it tries to do a delay(125/1000), or a delay(0) in other words.  I think I'll just make that if (us > 999) since SYSTICKHZ is 100 anyhow (every 10ms).

edit: er no, that would cause an infinite loop, so it's if (us > 9999) ... that should work.

 

Or programmatically... 1000000UL / SYSTICKHZ - 1

 

Looks pretty good now, issued the pullreq.

 

The intention here was to come up with a value close to but less than 1000000UL / SYSTICKHZ 

In this case I was aiming for 0.8 * 1000000UL / SYSTICKHZ (8000, or 8 milliseconds, as noted above).

 

At least in Energia 13, the result of 1000000UL/SYSTICKHZ/10*8 is the same as 8 * 1000000UL/SYSTICKHZ/10 or 8000

I thought that was pretty standard for how most programming languages deal with division vs. multiplication, (i.e. I didn't think about it being unclear) but doing a little search I see the interpretation of such an expression outside of programming languages has given rise to some discussion and a variety of interpretations are in common use.  So it is something one should avoid writing.

 

No major reason for choosing 0.8 (could be anything close to 1).

I wrote it as a fraction rather than a decimal just to avoid having a float in there (sure it is all done by the compiler, but I still avoid floats where don't need them).

Subtracting a value from the limiting value works fine too.  (Well there were reasons why I tend to go for multiply by a fraction in such cases, but enough said.)

 

Sorry for the confusion.

Link to post
Share on other sites

I'm not paying a lot of attention to this because it's Energia and Tiva neither of which I use right now, but for low-overhead delays of reasonable duration you might want to look at how BSPACM does it. For microsecond counts you should just be able to convert the count to cycles based on the CPU frequency. Wouldn't be dependent on systick then.

 

There's some discussion of this on the Stellarisiti forums somewhere.

 

For what that's worth.

 

http://forum.stellarisiti.com/topic/1908-execution-time-the-easy-way

http://forum.stellarisiti.com/topic/1980-delay-cycles/

(Thanks @@pabigot for the reference on TimerWrap - it was in the process of trying to really understand that that I stumbled on the problem with the SysTick delay here.)

 

I had thought about using the cycle counter as another way to test the delay function.

Link to post
Share on other sites

@@igor Thanks again for your code & help, what I have done is changed tack a bit - I got the CC3200 core working fantastically, since it's already implemented as a pure-SysTick implementation, and now I am going to backport to Tiva so we can remove the TIMER5 requirement once and for all.  I'll let you know when that's ready to go :)

 

Also micros() was mixed up on cc3200, as it used SysTickValueGet() instead of (SysTickPeriodGet, aka F_CPU / SYSTICKHZ) - SysTickValueGet()...

Link to post
Share on other sites

@@energia is going to review the pull request before merging, but for now I made a zip "service pack" with all the recent changes from github issue #475.

E13_servicepack_timers_10272014.zip

 

Changes:

- cc3200's SysTick-based implementation fixed for delays and micros, and its implementation backported to lm4f

- lm4f (Stellaris/Tiva) now uses SysTick exclusively for millis, micros, delays; Timer5 has been relieved of service.

- cc3200 now has sleep, sleepSeconds, suspend and wakeup just like lm4f; it's the same (strawman, non-low-power) implementation.  I want to make this legitimately low-power soon.

- also a cc3200 change in here to let C++ constructors work correctly for pinMode, digitalWrite, etc. (not recommended library designers use this, but some Arduino libraries unfortunately do that; Tiva already had the fix in E13)

Link to post
Share on other sites

@spirilis 

 

I notice that the service pack version changes from reading the start time as early as possible in delayMicroseconds, to reading it a little bit later.

(changes from this version http://forum.43oh.com/topic/6011-tiva-delaymicroseconds-unreliable/#entry52527

to this slightly older version http://forum.43oh.com/topic/6011-tiva-delaymicroseconds-unreliable/#entry52526 )

 

(Difference is whether the 

unsigned long startTime = HWREG(NVIC_ST_CURRENT) ;

appears before checking for long delays, or after.  Of course if it is before, then a re-read of the register is needed if a long delay was used.)

 

If moving that check a little later in the code was intentional - fine, (it makes the code a little more compact.  I don't know how much time difference it makes, suspect it won't make a lot of difference - time for a compare, jump, and probably a pipeline refill).  Just wanted to be sure it wasn't inadvertent (change seems to have come when backported the CC3200 version).

 

Thanks for all the work on this.

 

--

On a totally different topic

 

- also a cc3200 change in here to let C++ constructors work correctly for pinMode, digitalWrite, etc. (not recommended library designers use this, but some Arduino libraries unfortunately do that; Tiva already had the fix in E13)

 

 

 

Is there some place I can read more about this topic (the advisability of pinMode in constructors)?  It sounds like putting pinMode and digitalWrite in a constructor may be frowned upon in library design?  Is it frowned on in Arduino in general, is it just in Energia that it is not recommended?  I was exploring this issue regarding a library I was revising.  The conclusion I came to (based on the arduino documentation) was that pinMode, etc. in a constructor was approved of (maybe even encouraged) in libraries.  This item sounds like perhaps there is more information on this topic which I missed.  If the answer is involved and there isn't already something about it that you have a quick link to, I should probably start a separate thread (or fork this one) to ask the question.  Thanks again.

Link to post
Share on other sites

@@igor I moved the start time back after I noticed a very slight *increase* in the delay with the starttime declared at the beginning, which is puzzling.

 

As for constructor init of pinMode et al, this is an Arduino thing in general, and I found discussions on the arduino forum referring to it (would have to search again to find it). In any case, one issue with C++ constructors is you can't predict their order of execution - so it's generally advised to perform only variable initialization within a constructor and nothing that alters hardware state. Complete init is meant for a .begin() function. I've seen some constructors actually perform delay() - what if you had multiple libraries do that? Could have some long startup delays...

 

Sent from my Galaxy Note II with Tapatalk 4

Link to post
Share on other sites

I don't think this was the exact thread I had in mind, but here's some examples:

 

http://forum.arduino.cc/index.php?topic=78114.0

 

http://forum.arduino.cc/index.php?topic=142937.0

 

 

 

To elaborate...

The constructor is called before the main() function which does all the configuration and initialisation of the Arduino.

Anything you put in a constructor has a chance (depending on what it is) of being either over-written by defaults set in main(), or of not doing anything at all because it relies on initialisation that hasn't been performed yet.

Anything which is "arduinoesque", i.e., anything like pinMode(), digitalWrite() etc will deffinately not work in a constructor.  The loosely agreed standard is to provide a .begin() method which you call from setup() to configure any IO etc that needs to be configured.  You can either move your setting parameters to that method, or keep them in the constructor but store them in properties for the .begin() method to use later.
Link to post
Share on other sites

So in practice, those pinMode's et al often do work inside constructors (with Arduino AVR and MSP430 whose peripheral clocking systems are automatically configured behind the scenes), but as we've seen with Energia support on Tiva and CC3200, that assumption is fundamentally a bad one -- because an "out of the box" C++ application system will crash & burn when such assumptions are used on ARM, unless special measures are taken (in this case moving HW init and GPIO peripheral module clock initialization into a separate _init() function and modifying the Reset ISR to execute _init() before the C++ ctors)

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...