Author Topic: [arduino] can't subtract two time stamps  (Read 14718 times)

0 Members and 1 Guest are viewing this topic.

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #50 on: October 24, 2021, 09:11:51 am »
(paraphrasing)
Quote
Arduino doesn't allow direct access to the hardware.
Basic functions (millis(), subtraction, etc) in arduino don't work, perhaps because the ARM compiler is misconfigured.
The Arduino forums hide this by censoring their forums, forcing expert contributors off onto their own forums.
The Arduino code is full of clever and complex (and thus indecipherable) C++ code.

None of those are true.  You sound like a conspiracy theorist.


I ran your code from #5 through Arduino with all the warnings turned on, and took a general look through.  I don't have your hardware setup, so I can't duplicate all the testing.  I changed the LCD output to Serial, and it prints stuff about every 2 seconds.

These look like real problems (use of boolean "!" operator where there should be a bitwise "~"?  I'm not real clear on what it's trying to do.)
Code: [Select]
/Volumes/MacOS/HD-Downloads/Downloads/lashup/button_inputs.ino: In function 'void buttonRead(unsigned char)':
/Volumes/MacOS/HD-Downloads/Downloads/lashup/button_inputs.ino:11:46: warning: '<<' in boolean context, did you mean '<' ? [-Wint-in-bool-context]
   11 |     varButtonState = (varButtonState & !(0x1 << button)) | (0x1 << button);
      |                                         ~~~~~^~~~~~~~~~
/Volumes/MacOS/HD-Downloads/Downloads/lashup/button_inputs.ino:15:50: warning: '<<' in boolean context, did you mean '<' ? [-Wint-in-bool-context]
   15 |     varButtonPressed = (varButtonPressed & !(0x1 << button)) | (0x1 << button);
      |                                             ~~~~~^~~~~~~~~~


And here, it bothers me that tempstring[] can apparently be up to 14 characters, while the text[] string that it's being copied to can only have 5.  (whether that actually happens is a separate question, I guess.)

Code: [Select]
void asciiDistance(long vartoconvert, char *string) {
  unsigned char tempstring[15];
    :
  while (tempcounter != 0) {
    string[stringcounter] = tempstring[tempcounter];
    tempcounter--;
    stringcounter++;
  }
}

void lcdDistancePrint() {
  noInterrupts();
  long distance =  tachoDistanceCounts * tachoStep / 100;
  char text[6] = {0, 0, 0, 0, 0, 0};
 
  asciiDistance(distance, text);
    :

There is a bunch of use of "uint8_t" and "uint16_t" and similar that would be a good idea on an AVR, but is un-useful to harmful on an ARM.

The stack pointer for a SAMD21 image is initialized to 0x20008000 (the end of the 32k of RAM), so stack-overflow - the only reason I could imagine that "millis() fails if call from "too deeply nested", seems extremely unlikely.  (And I've never heard of any such thing.)

-----

There are annoying things about "Arduino", but in the end it's a very shallow framework on top of standard well-trusted tools, and it has a LOT of users.   If you're going to make extraordinary claims about serious and should-be-obvious bugs and misbehaviors, we (well, I) will need extraordinary proof.  That means code and instructions for a (hopefully very minimal; ie no LCD, no actual tach hardware) hardware setup that demonstrates the problem.

Nick Gammon was never an official Arduino employee, though he was a significant contributor to the forums for quite a while.   His personal forums and web pages pre-date the Arduino by a long time, so it makes sense that he'd post info of long-term interest there.  He hasn't been around the Arduino forums for a while, and I don't recall him posting a reason for leaving.  I suspect he got tired of rude users, rather than of the administration.)  I've never seen the forums censor people for technical info critical of Arduino code/etc; only for ... rudeness to other people.  (And I would have, having been a main player in the "Freeduino" project, back when it looked like the HW might go "closed source.")


You have just gone through code that I have not tested yet and pointed at things I have not even tested yet. As you can see all of the button pressing stuff is not in use yet so has no baring. How about you comment on the code that will not work? Oh you just have a point to make, sorry next time I will throttle information to help me control the discussion so that I can be artificially right and make you wrong despite the fact it won.t help me....

All I know is that things are not working and so far no one can say why. Your conspiracy theorist claims are stupid at best! so

As we are into conspiracy theories let's put you right:

Arduino doesn't allow direct access to the hardware. - I never said that, I know very well that i can wright directly to registers but knowing that this could compromise existing code that libraries already depend on I would not be so silly as to do so without knowing what else I am effecting and with something that is already not working not the time to do it.
Basic functions (millis(), subtraction, etc) in arduino don't work, perhaps because the ARM compiler is misconfigured. - No it does work, just look at the code, millis(), the 2s main ticker is working, but when millis() is used in deeply nested functions it stops working thus a subtraction seems to no longer work.
The Arduino forums hide this by censoring their forums, forcing expert contributors off onto their own forums. - I have been at the receiving end of the over protectiveness of the arduino forum. It does just strike me as odd that there is all this information about how to get so much more done with the arduino making it truly powerful - not on their forums or website. And yes, I can assure you that I have read in the past that the millis() function cannot be called by functions deeply nested. As someone else has said this could be normal, if there is nothing about this in the instructions well what am i to assume? if I write a function in C I can call it from anywhere.
The Arduino code is full of clever and complex (and thus indecipherable) C++ code. - I never ever said that it was complex and undecipherable. I have said from the start that I am being realistic about the fact that I not not yet understand C++ so pretending to will just make more problems for myself. I prefer to make work what I can now and get clever when I know what I am doing.

I don't know who Nick Gammon is, I just know that his name is all over the arduino forum including a discussion about how he left for a bit and from my limited experience I can see why and that when he had something truly useful to say ho chose not to put it on their forum.

Now, did you have anything useful to add?
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #51 on: October 24, 2021, 09:22:37 am »


These look like real problems (use of boolean "!" operator where there should be a bitwise "~"?  I'm not real clear on what it's trying to do.)
Code: [Select]
/Volumes/MacOS/HD-Downloads/Downloads/lashup/button_inputs.ino: In function 'void buttonRead(unsigned char)':
/Volumes/MacOS/HD-Downloads/Downloads/lashup/button_inputs.ino:11:46: warning: '<<' in boolean context, did you mean '<' ? [-Wint-in-bool-context]
   11 |     varButtonState = (varButtonState & !(0x1 << button)) | (0x1 << button);
      |                                         ~~~~~^~~~~~~~~~
/Volumes/MacOS/HD-Downloads/Downloads/lashup/button_inputs.ino:15:50: warning: '<<' in boolean context, did you mean '<' ? [-Wint-in-bool-context]
   15 |     varButtonPressed = (varButtonPressed & !(0x1 << button)) | (0x1 << button);
      |                                             ~~~~~^~~~~~~~~~

Yes there is a problem there, thank you on that one. This code is untested and that is the first writing. As you can see it's not been called yet as I stopped work on that when i could not get the speed calculation to work.

Quote


And here, it bothers me that tempstring[] can apparently be up to 14 characters, while the text[] string that it's being copied to can only have 5.  (whether that actually happens is a separate question, I guess.)

Code: [Select]
void asciiDistance(long vartoconvert, char *string) {
  unsigned char tempstring[15];
    :
  while (tempcounter != 0) {
    string[stringcounter] = tempstring[tempcounter];
    tempcounter--;
    stringcounter++;
  }
}

void lcdDistancePrint() {
  noInterrupts();
  long distance =  tachoDistanceCounts * tachoStep / 100;
  char text[6] = {0, 0, 0, 0, 0, 0};
 
  asciiDistance(distance, text);
    :

There is a bunch of use of "uint8_t" and "uint16_t" and similar that would be a good idea on an AVR, but is un-useful to harmful on an ARM.

The stack pointer for a SAMD21 image is initialized to 0x20008000 (the end of the 32k of RAM), so stack-overflow - the only reason I could imagine that "millis() fails if call from "too deeply nested", seems extremely unlikely.  (And I've never heard of any such thing.)



Yes my conversion functions are a bit messy, they have been evolving and were just pulled out of something else. Why are uint8_t type stuff a problem? I like to know that things will be what I said I wanted them to be. They are also quicker to write.

I have no idea what the stack overflow stuff is about. This is the arduino, I'm supposed to be an experienced programmer....
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #52 on: October 24, 2021, 09:31:22 am »
https://forum.arduino.cc/t/functions-limit/282065

Nesting limits, as you can see it's not like i was going that deep. Never had a problem like this on straight C.
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 9930
  • Country: nz
Re: [arduino] can't subtract two time stamps
« Reply #53 on: October 24, 2021, 09:55:25 am »
If anyone of your string functions try to write a value into an array at a position bigger than the size of the array you will overwrite memory and cause all sorts of unpredictable behavior.

Have a look at this code below

In lcdDistancePrint() This line calculates a number
Code: [Select]
long distance =  tachoDistanceCounts * tachoStep / 100;
Then in asciiDistance()
That number is used in this while loop as var to control how many iterations through the loop.
The only thing that ends the loop is the div by 10 getting var to zero. So if var is huge you will overwrite lots of memory from your writes to tempstring[] which can only take 15 bytes.
Code: [Select]
while (var > 0)
{
    tempcounter++;
    if (tempcounter == 2)
    {
      tempstring[tempcounter] = '.';
      tempcounter ++;
    }
    tempstring[tempcounter] = (var % 10) + 48;
    var = var / 10;
}

Maybe there is a variable not initialized somewhere, or maybe the timing is producing bigger numbers than you expected etc.

The one thing you can be sure on, when your overwrite memory the result is chaos and nothing makes sense. Which appears to be what you are getting.  As you change the code trying to find the problem that causes different code to be put in different locations so different things get overwritten. eg chaos.  Maybe you're overwriting the stack and that is why you cannot go very deep in function calls

Go through all your loops dealing with arrays and put an if statement in there that will do a 'break' statement if the array index gets to the max value for the array size.
« Last Edit: October 24, 2021, 10:14:40 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: [arduino] can't subtract two time stamps
« Reply #54 on: October 24, 2021, 10:31:45 am »
https://forum.arduino.cc/t/functions-limit/282065

Nesting limits, as you can see it's not like i was going that deep. Never had a problem like this on straight C.

*Absolutely* nothing to do with the Arduino IDE or libraries or language.

EVERYTHING to do with programming a tiny machine with almost no RAM, no matter what the language.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #55 on: October 24, 2021, 11:39:53 am »
If anyone of your string functions try to write a value into an array at a position bigger than the size of the array you will overwrite memory and cause all sorts of unpredictable behavior.

Have a look at this code below

In lcdDistancePrint() This line calculates a number
Code: [Select]
long distance =  tachoDistanceCounts * tachoStep / 100;
Then in asciiDistance()
That number is used in this while loop as var to control how many iterations through the loop.
The only thing that ends the loop is the div by 10 getting var to zero. So if var is huge you will overwrite lots of memory from your writes to tempstring[] which can only take 15 bytes.
Code: [Select]
while (var > 0)
{
    tempcounter++;
    if (tempcounter == 2)
    {
      tempstring[tempcounter] = '.';
      tempcounter ++;
    }
    tempstring[tempcounter] = (var % 10) + 48;
    var = var / 10;
}

Maybe there is a variable not initialized somewhere, or maybe the timing is producing bigger numbers than you expected etc.

The one thing you can be sure on, when your overwrite memory the result is chaos and nothing makes sense. Which appears to be what you are getting.  As you change the code trying to find the problem that causes different code to be put in different locations so different things get overwritten. eg chaos.  Maybe you're overwriting the stack and that is why you cannot go very deep in function calls

Go through all your loops dealing with arrays and put an if statement in there that will do a 'break' statement if the array index gets to the max value for the array size.

Yes I need to refine that code, I know in this case that the number will not exceed a certain limit. One day it will be a function in my own library file that I use as standard. As I have already explained the lower call to millis() does not give what I would expect and as soon as I made sure the call to millis was not too deeply nested it worked.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #56 on: October 24, 2021, 11:42:07 am »
https://forum.arduino.cc/t/functions-limit/282065

Nesting limits, as you can see it's not like i was going that deep. Never had a problem like this on straight C.

*Absolutely* nothing to do with the Arduino IDE or libraries or language.

EVERYTHING to do with programming a tiny machine with almost no RAM, no matter what the language.


And who decides how much RAM to allocate to the stack?
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: [arduino] can't subtract two time stamps
« Reply #57 on: October 24, 2021, 11:47:02 am »
You do. The stack is all of RAM that has not been used by global variables or malloc().
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #58 on: October 24, 2021, 11:53:27 am »
You do. The stack is all of RAM that has not been used by global variables or malloc().


And how would I do that on an arduino based system? it's not like there is not RAM to spare, I have 32kB, that little program uses a fraction.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: [arduino] can't subtract two time stamps
« Reply #59 on: October 24, 2021, 12:27:17 pm »
You don't have to do anything. It just is.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #60 on: October 24, 2021, 12:28:47 pm »
You don't have to do anything. It just is.


so there is plenty of ram available but the stack does not get enough to run the program, do you see my logic?
 

Online bingo600

  • Super Contributor
  • ***
  • Posts: 1987
  • Country: dk
Re: [arduino] can't subtract two time stamps
« Reply #61 on: October 24, 2021, 12:32:43 pm »
As Bruce says
Stack is "normally" pointing to "End of  Ram" , and grow downwards towards Zero.
This happens "automatically by HW or the init routine".

It's the programmers responsibility to make sure the stack doesn't grow down to "hit the "ram" variables".
That creates weird results and erratic (often fatal) behaviour.

/Bingo
 

Online bingo600

  • Super Contributor
  • ***
  • Posts: 1987
  • Country: dk
Re: [arduino] can't subtract two time stamps
« Reply #62 on: October 24, 2021, 12:47:17 pm »
If you have 32K Ram , and no huge "local variable" definitions , they shouldn't normally "clash".

Code: [Select]
int func1(int x)
{
  char array [20000];
  int j;

  memset(array , 0x00, sizeof(array));

  j = func2(x); 

}

int func2(int x)
{
  char array [20000];
  int j = 0;

  memset(array , 0x00, sizeof(array));

  if(x == 42)
    j = 1;

 return j;
 
}

main()
{

  while(1)
  {
      func1(42);
  }

}


This would prob. "explode" w 32k Ram.

Both func allocates 20K on the stack , and func1 calls func2 , now 40k is allocated.


Thought/constructed example , prob not realistic.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8167
  • Country: fi
Re: [arduino] can't subtract two time stamps
« Reply #63 on: October 24, 2021, 12:52:26 pm »
Don't get fixated into the idea of stack running out due to too much function nesting. It doesn't happen easily unless you are writing recursive functions, or are on a device with say 128 bytes of RAM (some TINY series AVR).

Instead, I'm almost sure you are just over/underindexing somewhere and messing up the RAM content. Such behavior is difficult to debug; I suggest you add some sort of assert around all suspect array accesses. You don't have too many. Use this pattern for example:
Code: [Select]
   int array[ARRAY_LEN];

   access: instead of:
   array[i]

   write:
   if(i < 0 || i >= ARRAY_LEN)
       error(5); // different number in each place
    array[i]

    void error(int n)
    {
        // blink LED n times, print n to uart, whatever
        while(1);
    }

This saves you a LOT of time, and do extend that to other types of error/sanity checks as well, not just array indexing.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #64 on: October 24, 2021, 01:43:47 pm »
Don't get fixated into the idea of stack running out due to too much function nesting. It doesn't happen easily unless you are writing recursive functions, or are on a device with say 128 bytes of RAM (some TINY series AVR).


But I have just been told I control this or at least can....
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 4414
  • Country: dk
Re: [arduino] can't subtract two time stamps
« Reply #65 on: October 24, 2021, 02:08:18 pm »
https://forum.arduino.cc/t/functions-limit/282065

Nesting limits, as you can see it's not like i was going that deep. Never had a problem like this on straight C.

*Absolutely* nothing to do with the Arduino IDE or libraries or language.

EVERYTHING to do with programming a tiny machine with almost no RAM, no matter what the language.


And who decides how much RAM to allocate to the stack?

it set to the top of ram so it is all the ram that is used for anything else

https://github.com/arduino/ArduinoCore-samd/blob/master/variants/mkrzero/linker_scripts/gcc/flash_with_bootloader.ld

 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8167
  • Country: fi
Re: [arduino] can't subtract two time stamps
« Reply #66 on: October 24, 2021, 02:25:09 pm »
Don't get fixated into the idea of stack running out due to too much function nesting. It doesn't happen easily unless you are writing recursive functions, or are on a device with say 128 bytes of RAM (some TINY series AVR).


But I have just been told I control this or at least can....

Usually this is configured so that stack space is all that remains after your static/global variables. Statics grow upward, stack grows downwards. If they touch, kaboom, you have corrupted something.

So you can ontrol available stack by not wasting too much in static storage.

Yes, stack overflow is a real problem which happens easily if you run out of memory.

It's much more likely you accidentally have some large like 16KB buffer and then run out of memory, than to run out of memory by calling nesting functions, each call wasting just 2 bytes (+ whatever variables they need on stack) or so. But it was posted earlier millis() only needs that 2 bytes.

But of course, that 2 bytes can be the final straw if you have wasted all the memory! But you likely didn't waste it in 2-byte things, you wasted it on something bigger.

So assuming we really have RAM corruption here, the two probable causes are,
* Out of memory causing stack colliding with static storage,
* Over/underindexing or faulty pointer somewhere

Does the Arduino IDE print out static memory usage information? If not, command line program avr-size should tell you sizes of .data and .bss sections, their sum would be the static usage.

Adding -fstack-usage on the GCC command line makes it output .su files which are quite human readable and tell you how many bytes each function needs. Add that 2-4 bytes per call for return address (depending on your architecture, I already forgot) and you have an idea how each function call chain contributes to stack usage.

General advice is to carefully consider any buffers that are larger than a few % of the RAM. On any ATMega, for example, I wouldn't think too much about creating a char[16] buffer, but if you need int[1000], that's going to need some thinking. Making it static is the simplest way to make it appear in the RAM usage as shown by even simplest of tools.
 

Offline retiredfeline

  • Frequent Contributor
  • **
  • Posts: 539
  • Country: au
Re: [arduino] can't subtract two time stamps
« Reply #67 on: October 24, 2021, 02:25:44 pm »
Assuming lashup.zip still contains the code that you can't get to work, then the bug is right there in loop(). Here's loop():

Code: [Select]
void loop()
{
    if ((millis() - varLastLoopTime) > constLoopIntervalMs) {   // check time since last loop counter update
        loopCount++;            // update loop count
        if (loopCount > 10)
            loopCount = 0;      // if loop count has eceeded 10 reset
        varLastLoopTime = millis();
    }
    if (loopCount == 10) {
        task2s();
    }
}

Let's annotate that:

Code: [Select]
void loop()
{
    if ((millis() - varLastLoopTime) > constLoopIntervalMs) {   // check time since last loop counter update
        loopCount++;            // update loop count
        if (loopCount > 10)
            loopCount = 0;      // if loop count has eceeded 10 reset
        // execution reaches here every 200ms
        varLastLoopTime = millis();
    }
    // execution reaches here very often whenever waiting for timeout
    // i.e. as fast as loop can be called, the if tested, found false, and loop returns
    if (loopCount == 10) {
        // as above but during the 200ms when loopCount is 10
        task2s();
    }
}

To fix this you could add an else to the if:

Code: [Select]
void loop()
{
    if ((millis() - varLastLoopTime) > constLoopIntervalMs) {   // check time since last loop counter update
        loopCount++;            // update loop count
        if (loopCount > 10)
            loopCount = 0;      // if loop count has eceeded 10 reset
        // execution reaches here every 200ms
        varLastLoopTime = millis();
    } else {
        return;
    }
    // execution reaches here once every 200ms
    if (loopCount == 10) {
        // execution reaches here once every 2s
        task2s();
    }
}

Or put the code inside the body of the if:

Code: [Select]
void loop()
{
    if ((millis() - varLastLoopTime) > constLoopIntervalMs) {   // check time since last loop counter update
        loopCount++;            // update loop count
        if (loopCount > 10)
            loopCount = 0;      // if loop count has eceeded 10 reset
        // execution reaches here every 200ms
        varLastLoopTime = millis();
        // execution reaches here once every 200ms
        if (loopCount == 10) {
            // execution reaches here once every 2s
            task2s();
        }
    }
}

Or if you are not doing anything in the 200ms, might as well wait inside loop instead of exiting and reentering loop:

Code: [Select]
void loop()
{
    while ((millis() - varLastLoopTime) <= constLoopIntervalMs)    // wait for timeout
        ;
    loopCount++;            // update loop count
    if (loopCount > 10)
        loopCount = 0;      // if loop count has eceeded 10 reset
    // execution reaches here every 200ms
    varLastLoopTime = millis();
    // execution reaches here once every 200ms
    if (loopCount == 10) {
        // execution reaches here once every 2s
        task2s();
    }   
}

Better still, as your loopCount actually divides by 11, not 10:

Code: [Select]
void loop()
{
    while ((millis() - varLastLoopTime) <= constLoopIntervalMs) // wait for timeout
        ;
    // execution reaches here every 200ms
    varLastLoopTime = millis();
    loopCount++;            // update loop count
    if (loopCount >= 10) {
        loopCount = 0;      // if loop count has eceeded 10 reset
        // execution reaches here once every 2s
        task2s();
    }   
}

I pointed this out in post #31 but it seems you didn't understand.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #68 on: October 24, 2021, 02:46:52 pm »
what difference does it make? yes the code will run around and around the loop constantly until a 200ms period has passed, every 200ms it has to make a note of the current time so that it can track the time from there to see when the next 200mms is up, when it has the counter is updated. When the counter gets to 10 the code executes, what did you think the program was to do when it has nothing to do other than run around in circles?

The problem with millis is in the speed calculation which in nested a couple more levels down. When I take that code and put it directly in the if count == 10 it works and displays the speed rather than 0.

In time more code will be added to the loop. This will be timed in the same way.
« Last Edit: October 24, 2021, 03:18:26 pm by Simon »
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6238
  • Country: fi
    • My home page and email address
Re: [arduino] can't subtract two time stamps
« Reply #69 on: October 24, 2021, 03:13:19 pm »
Arduino Zero, i.e. ATSAMD21?  That's a Cortex-M0+, and darnit, that one does not have a cycle counter in the DWT.  On Cortex-M4's and others, DWT_CYCCNT is a monotonic cycle counter one can read and use to accurately measure intervals of up to 2**32 cycles (minute and a half at 48 MHz, minute at 72 MHz, and so on); all one needs to do is ensure the DWT is enabled.

The core Arduino support for SAMD21 is here, with linker scripts here, pin definitions here, and the Arduino main library stuff here.

If we look at SAMD core delay.c, we'll find a note that indicates millis() is not safe in an interrupt context, but micros() is.

(micros() calculates microseconds, and uses the same systick counter.  It uses the counter value in addition to the number of cycles of the counter, thus getting somewhat better resolution.  Since a 32-bit microsecond counter rolls over every 4294 seconds or so, it suffices for intervals of up to about an hour and eleven minutes.)

My very first suggestion would be to replace every instance of millis() with (micros()/1000), and see if anything changes.  Better yet,
Code: [Select]
void lcdSpeedPrint() {
  uint32_t  tempvarTime =  micros();

  uint32_t  tempvarCounts = vTachoSpeedCounts;
  vTachoSpeedCounts = 0;

  uint32_t  tvTimeDifference = (tempvarTime - varLastSpeedTime);
  varLastSpeedTime = tempvarTime;

  uint32_t  vtSpeed = (vTachoSpeedCounts * tachoStep * 6) / (tvTimeDifference * 10); // *600/1000 = *6/10

  lcd.clear();
  lcd.print((int)tvTimeDifference, DEC);
  lcd.setCursor(0, 1);
  lcd.print((int)vtSpeed, DEC);
  lcd.setCursor(0, 2);
  lcd.print((int)vTachoSpeedCounts, DEC);
  lcd.setCursor(0, 3);
  lcd.print((int)tempvarTime, DEC);
}
With this, looking at the LCD output will tell us crucial information.  I assume it has four lines, with at least 10 digits visible on each line.

The first lcd.print prints the tachometer counting duration in microseconds: 1000 is one millisecond, 1000000 one second.

The second lcd.print prints the calculated (RPM?) value.  I didn't check the math, just copied it and adjusted to microseconds from the initial post.

(If you asked me, I'd say the correct formula is (pulses * 60000) / (interval_in_microseconds * pulses_per_revolution / 1000), but you can move the zeros from the multiplier to the divisor or vice versa to ensure both subexpressions can be properly represented as a 32-bit unsigned integer.  As it stands, at less than second intervals, it works for up to 71582 pulses per second and 4294 pulses per revolution, before it craps out.)

The third lcd.print prints, I assume, the number of tachometer pulses.  If this stays zero, it means either that the variable is not being updated, or that it is updated in an interrupt but vTachoSpeedCounts is not marked volatile, and thus the compiler assumes it does not change (unlikely, though).

The fourth lcd.print prints the microsecond timer value.  If it does not change at all, it means the systick counter (hardware) is disabled.  If only the three or four lowest/rightmost digits change, it means the systick interrupt is not firing correctly (perhaps because interrupts are disabled).

This should be a relatively simple test, but provide much needed additional information (by observing what the LCD shows).
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #70 on: October 24, 2021, 03:23:00 pm »
I will give that a go tomorrow, this is why I am looking at ways to write code to deal with the hardware directly and use things like the LCD library that do not have much to do with the hardware, so basically put my own hardware code between the high level libraries and the hardware.
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 4414
  • Country: dk
Re: [arduino] can't subtract two time stamps
« Reply #71 on: October 24, 2021, 03:36:51 pm »
Arduino Zero, i.e. ATSAMD21?  That's a Cortex-M0+, and darnit, that one does not have a cycle counter in the DWT.  On Cortex-M4's and others, DWT_CYCCNT is a monotonic cycle counter one can read and use to accurately measure intervals of up to 2**32 cycles (minute and a half at 48 MHz, minute at 72 MHz, and so on); all one needs to do is ensure the DWT is enabled.

The core Arduino support for SAMD21 is here, with linker scripts here, pin definitions here, and the Arduino main library stuff here.

If we look at SAMD core delay.c, we'll find a note that indicates millis() is not safe in an interrupt context, but micros() is.



that must be  comment left over from some other processor, there is nothing unsafe about reading a 32 bit int on an M0
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [arduino] can't subtract two time stamps
« Reply #72 on: October 24, 2021, 04:01:50 pm »
M0+
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 4414
  • Country: dk
Re: [arduino] can't subtract two time stamps
« Reply #73 on: October 24, 2021, 04:10:56 pm »
M0+

still safe, it is a 32 bit processor reading from 32 bit memory and it can't be unaligned
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6238
  • Country: fi
    • My home page and email address
Re: [arduino] can't subtract two time stamps
« Reply #74 on: October 24, 2021, 04:25:57 pm »
Yeah, reading static volatile uint32_t _ulTickCount; is atomic on Cortex-M0+; but if you look at the code, it could be one millisec behind in certain corner cases. I like micros() much better, even if it may iterate a couple of times in those corner cases, because it doesn't just count the interrupts, but uses the counter value that generates the interrupt for better resolution.

Assuming lashup.zip still contains the code that you can't get to work, then the bug is right there in loop().
Yes: during loopCount==10, every iteration of loop() causes task2s() to be called; several times each millisecond, most definitely.

I would write loop() slightly differently, and again using micros() instead of millis() (due to SAMD21 implementation of them).
Code: [Select]
static unsigned long  last_lcd_us;  // Add 'last_lcd_us = micros();' at end of setup()
static unsigned long  last_other_us;  // Add 'last_other_us = micros();' at end of setup()
static volatile unsigned long  tacho_pulses;  // Set 'tacho_pulses = 0;' in setup()

void loop(void)
{
    const unsigned long  curr_us = micros();

    if ((unsigned long)(curr_us - last_lcd_us) >= LCD_UPDATE_INTERVAL_US) {

        // Read 'tacho_pulses' and clear it to zero, while interrupts are disabled.
        noInterrupts();
        const unsigned long  pulses = tacho_pulses;
        tacho_pulses = 0;
        interrupts();

        update_lcd(tacho_pulses, curr_us - last_lcd_us);

        last_lcd_us = curr_us;

    } else
    if ((unsigned long)(curr_us - last_other_us) >= OTHER_UPDATE_INTERVAL_US) {

        do_other_thing(curr_us - last_other_us);

        last_other_us = curr_us;
    }
}
Here, update_lcd(pulses, microseconds) will be called at intervals of at least LCD_UPDATE_INTERVAL_US microseconds; the actual interval may be slightly larger, depending on what else the MCU is using.  Note the use of noInterrupts(); ... interrupts(); to disable interrupts while accessing the tacho_pulses variable.  I assume there is an ISR or something to update it (doing tacho_pulses++ in an ISR is safe, AFAICS).  If you have a HW pulse counter, then disabling interrupts may or may not be needed to read and reset the counter value; it depends on the hardware peripheral.

Similarly, do_other_thing(microseconds) will be called at intervals of at least OTHER_UPDATE_INTERVAL_US microseconds, i.e. up to 1000000/OTHER_UPDATE_INTERVAL_US times per second, if the MCU is not too busy doing other stuff.

For both functions, microseconds represents the time elapsed since the previous call.  In all cases summing consecutive values yields the correct run time; there are no blank intervals.  However, because the time is only measured once per loop() iteration, each loop() call only runs one of the functions.

The run priority is fixed, in the order listed in the code.  To add additional tasks, just add a new last_foo_us variable, and append it as a new else if () { } block, as the others.  I like to use a preprocessor macro for the intervals, but you can just as well use a variable for those if you like.

This is a simple way to do stuff at various intervals, and although not optimal in any way, it is a robust, easy-to-maintain approach.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf