Go Down

Topic: I2C accelerometer with delay using millis(). (Read 1 time) previous topic - next topic

aarg

#15
Mar 30, 2015, 12:50 pm Last Edit: Mar 30, 2015, 01:06 pm by aarg
aarg, to avoid the rollover problem, it is necessary to use "unsigned long" and timestamp the current moment. That way the "millis() - prevMillis" is always a positive number and that prevents the rollover problem.
When for example the time in the future is remembered, millis() could be higher or lower than that value, and that is not possible with unsigned long.
Here is an example that shows what it is like to use unsigned variables that rollover : http://playground.arduino.cc/Code/TimingRollover#arithmetic
There is something about this that is fishy. I modified that code to show a little more of the results of the arithmetic and comparisons. The idea behind it is to use byte to simulate unsigned time. I am giving a snippet here because a link is already provided to the source.
Code: [Select]

   p = 4;         // rollover of value 260
  q = 250;
  r = p - q;     // valid for unsigned variables
  Serial.println( F("260 (=4) - 250 = "));
  Serial.println( r, DEC);
  Serial.println( r < 10);
  Serial.println( p-q, DEC);
  Serial.println( p-q < 10);

which produces this:
Code: [Select]

260 (=4) - 250 =
10
0
-246
1

Notice that unless an assignment is made to a byte, the value is incorrect.
But! This form of comparison seems like the comparison of long ints in the actual blink code that follows, the structure of which we are all so familiar:
Code: [Select]

  if ( ul_CurrentMillis - ul_PreviousMillis > ul_Interval)

It seems to me, that if the analogy to the byte example is valid, this expression should fail, as it did with
Code: [Select]

( p-q < 10)

above.
I'm just not quite buying into this at the moment. Something is wrong. For sure, it could be the way I'm seeing it, but I'm not sure how.
I modified the unsigned long portion of the test in an identical way:
Code: [Select]

  ulNew = 705032704UL;  // rollover of value 5,000,000,000
  ulOld = 4000000000UL;
  ul = ulNew - ulOld;   // valid for unsigned long
  Serial.println( F("5,000,000,000 (=705,032,704) - 4,000,000,000 = "));
  Serial.println( ul, DEC);
  Serial.println( ul < 1000000000);
  Serial.println( ulNew - ulOld, DEC);
  Serial.println( ulNew - ulOld < 1000000000);

and the result is more encouraging:
Code: [Select]

Test with unsigned long.
3,000,000,000 - 2,000,000,000 = 1000000000
5,000,000,000 (=705,032,704) - 4,000,000,000 =
1000000000
0
1000000000
0

What is so "special" about the unsigned long comparison, that made it work, while the other unsigned data type, byte, didn't?
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

Peter_n

#16
Mar 30, 2015, 01:46 pm Last Edit: Mar 30, 2015, 01:48 pm by Peter_n
aarg, there is nothing fishy about it. As long as the millis() and micros() are used with unsigned long.

When bytes are converted to ints, then is doesn't work anymore. Some even try to detect a rollover "( if ( millis() < 100 )", or "( if ( millis() > 65530 )", that is when things go wrong.

To always get a positive valid output, even with a rollover value, no bits should be added to the left.
For the unsigned long, that means that the 33th bit should not be added. That will never happen, the compiler doesn't have a longer unsigned int.
For the byte, that means that the 9th bit should not be added, and that is what is happening when they are converted to ints.

Since unsigned ints are not converted, those will also work.
Code: [Select]

// aarg and unsigned ints

void setup()
{
  unsigned int aarg1, aarg2, r;

  Serial.begin( 9600);

  while (!Serial);    // Only for Leonardo.

  Serial.println( "-------------------");
  Serial.println( "\Started");
  Serial.println( F("Test with unsigned 16-bit."));

  aarg1 = 20000;
  aarg2 = 10000;
  r = aarg1 - aarg2;
  Serial.print( F("20000 - 10000 = "));
  Serial.println( r, DEC);

  aarg1 = 4464;         // rollover of value 70000
  aarg2 = 60000;
  r = aarg1 - aarg2;     // valid for unsigned variables
  Serial.print( F("70000 (=4464) - 60000 = "));
  Serial.println( r, DEC);
 
  Serial.println( r < 10);
  Serial.println( aarg1 - aarg2, DEC);
  Serial.println( aarg1 - aarg2 < 10);
 
  Serial.println();
}

void loop()
{
  static unsigned int fixed = 65528;
  static unsigned int counter = 65528;   // it may never be below the 'fixed'
 
  unsigned int r = counter - fixed;
 
  Serial.print( "fixed = ");
  Serial.print( fixed, DEC);
  Serial.print( ", counter = ");
  Serial.print( counter, DEC);
  Serial.print( ", r = ");
  Serial.print( r);
  Serial.print( ", counter - fixed = ");
  Serial.print( counter - fixed, DEC);
  Serial.println();
 
  counter++;
  delay(1000);
}


When those ints are converted to signed long, it doesn't work anymore.
Or when the counter is below the fixed value.

Go Up