ф-я elapsed на millis с int таймером глючит

Тема в разделе "Arduino & Shields", создана пользователем codingmonkey, 22 июл 2015.

  1. codingmonkey

    codingmonkey Нерд

    В общем исключительно в научных целях собираю метео станцию.
    запись с термистора на SD. для того чтобы не тормозить loop dalay*ем написал ф-ю elapsed() c 5-ю таймерами. если заданно время проходит он возвращает true и if выполняет блок кода, при этом все что ниже блока ни ждет ни какого dalay.

    Код (Text):
    /*
      SD card read/write

    This example shows how to read and write data to and from an SD card file
    The circuit:
    * SD card attached to SPI bus as follows:
    ** MOSI - pin 11
    ** MISO - pin 12
    ** CLK - pin 13
    ** CS - pin 4

    created  Nov 2010
    by David A. Mellis
    modified 9 Apr 2012
    by Tom Igoe

    This example code is in the public domain.

    */

    #include <SPI.h>
    #include <SD.h>

    File myFile;

    void setup()
    {
      // Open serial communications and wait for port to open:
      Serial.begin(9600);
      while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
      }


      Serial.print("Initializing SD card...");
      // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
      // Note that even if it's not used as the CS pin, the hardware SS pin
      // (10 on most Arduino boards, 53 on the Mega, 14 on the Leonardo) must be left as an output
      // or the SD library functions will not work.

      if (!SD.begin(4)) {
        Serial.println(F("initialization failed!"));
        return;
      }
      Serial.println(F("initialization done."));
    }

    bool elapsed(int ms, int t)
    {
      static long int timers[5]= {0};
      if ((timers[t]-millis()) > ms)
      {
        timers[t] = millis() + ms;
        return true;
      }
      return false;
    }

    void loop()
    {
      if (elapsed(1000, 0))
      {
      float v = analogRead(A0) * 5.0 / 1024.0;
      float t = -21.9 * log((10000.0 * v) / (5.0 - v) / 27074.0);
     
      Serial.println(t);
      myFile = SD.open("logT.txt", FILE_WRITE);
      if (myFile)
      {
        myFile.println(String(t));
        myFile.close();
      }
      }
      // nothing happens after setup
    }
    Так странность в том что если у меня таймера объявлены как
    static int timers[5]= {0};
    после 2-3 десятков тиков таймера он ускоряется т.е. не держит заданный интервал, но если обьявить таймеры как
    static long int timers[5]= {0};
    я тестировал наверное минут 5-10 все еще тикало как надо.

    В чем может быть дело?
     
  2. geher

    geher Гуру

    int в avr-gcc имеет размерность два байта, т.е. диапазон от -32768 до 32767.
    В результате примерно через 32 секунды после старта скетча значение, возвращаемое millis(), выходит ха этот диапазон и при присваивании наступает переполнение. Осюда и спецэффекты.
    Если использовать long int, то диапазон увеличивается до где-то чуть больше чем +-2 миллиарда. В результате результат millis в течении более долгого времени можно присваивать без потерь.
    Если делать совсем хорошо, то лучше будет
    static unsigned long int timers[5]= {0};
     
  3. Salk

    Salk Гик

    Т.е. я правильно понимаю, что таймеры с unsigned long int проработают корректно всего 49,71 суток?
    Т.к. имеют разрядность 4 байта - диапазон от 0 до 4 294 967 295.
    А программно никак не сбросить millis(), для увеличения срока автономной работы, не прибегая к перезагрузке Arduino?
     
  4. geher

    geher Гуру

    На самом деле все проще.
    Когда таймер дотикает до максимума, то он перескочит опять на 0.
    И дальше вступает в силу хитрая компьютерная арифметика, работающая при переполнениях.
    В результате для значений с типом unsigned long int разница между зафиксированным ранее и текущим значениями millis() будет корректной до тех пор, пока между событиями не пройдет больше чем 4 294 967 295 миллисескунд.

    Например, фиксация ранее t1 $FFFFFFFE
    Текущая фиксация t2 10
    то значение t2-t1 будет 12, т.е. равно количеству миллисекунд, прошедших с первой отсечки до второй.
    И сравнение (t2-t1)>interval будет корректно работать, несмотря на переход через 0.

    Для знакового long int и для простого int в случае переполнения все будет несколько по другому.
     
    Salk нравится это.
  5. Salk

    Salk Гик

    Ого не знал, т.е. можно абсолютно не волноваться за таймеры, и держать Arduino включенной более 2 месяцев :)
     
  6. codingmonkey

    codingmonkey Нерд

    >Когда таймер дотикает до максимума, то он перескочит опять на 0.
    а если я ниже добавлю маленький delay(1000) та часть чипа что "крутит" счетчик таймера тоже будет стоять 1с ?

    >И сравнение (t2-t1)>interval будет корректно работать, несмотря на переход через 0.
    т.е. пока есть напряжение этот вариант будет работать корректно бесконечно ?
     
  7. geher

    geher Гуру

    Нет. Таймер будет продолжать считать.

    Да.
    Только следует учитывать, что оно работает правильно только если не останавливать таймер надолго (а уж тем паче навсегда) запретом прерываний и не вызывать рестарт МК.