Странность millis() micros().

Тема в разделе "Arduino & Shields", создана пользователем DayGaik, 24 авг 2012.

  1. DayGaik

    DayGaik Гик

    Написал небольшой скетч для измерения скорости чтения аналоговых портов. Но удивили больше всего функции измерения времени millis() и micros().

    Код (Text):

    void setup() {
      Serial.begin(115200);
     
      unsigned int value; // dummy variable to prevent optimization
      unsigned int time;
     
      /*
        1. Testing  millis()  function
      */
      value = 0;
      time = millis();
      for(int i=0; i<999; i++) {
        value += analogRead(0);
      }
      time = millis() - time;
     
      Serial.println(time);  // 111
     
     
      /*
        2. Testing  micros()  function
      */
      value = 0;
      time = micros();
      for(int i=0; i<999; i++) {
        value += analogRead(0);
      }
      time = micros() - time;
     
      Serial.println(time); // 46360
     
     
      /*
        3. Output both functions value
      */
      Serial.println(millis());    // 224
      Serial.println(micros());    // 224640
     
    }
     
     
     
    void loop() {
    }
     
    Третий тест показал что МК считает значения этих функций ожидаемо.

    Почему один и тот же код измеренный функцией millis выполняется 0,111 сек, а если измерить micros, то 0,046360 сек. -- в два раза быстрее. Если поменять местами тесты - то на результат это сильно не повлияет.
     
  2. DayGaik

    DayGaik Гик

    Прошу прощения за то что поторопился. Вопрос снят.
    Я, по наивности своей, думал что int - 32 бита.
    Если заменить
    unsigned int time;
    на
    unsigned long time;
    Все станет работать ожидаемо. Спасибо за терпение.
     
  3. nailxx

    nailxx Официальный Нерд Администратор

    Эх, а я только докопался до этого. Чуть-чуть не успел :)

    А вы мерите как быстро работает analogRead? Если хотите драматически ускорить, я могу подсказать как.
     
  4. DayGaik

    DayGaik Гик

    Любопытно. Расскажите конечно.

    И еще такой вопрос. Как быстрее всего отдать с устройства (Уно) данные, через стандартный Serial, через WiFi, через Ethernet или записать на SD карту? Или все не принципиально? Хочу понять насколько часто Уно может снимать со всех аналоговых входов сигнал и сохранять его куда-нибудь.
     
  5. nailxx

    nailxx Официальный Нерд Администратор

    На самом деле чудес не бывает, бывают хаки. Чтобы получить значение аналогового сигнала, с помощью компаратора вход сравнивается с рядом известных напряжений, такт за тактом, то есть не моментально. В итоге, при настройках по умолчанию, один замер аналогового сигнала занимает от 13 до 260 мкс, как повезёт. В среднем это 121 мкс, что вы и наблюдали.

    Можно сказать, что сравнение можно делать грубее, тратя на него меньше времени. Этим управляет регистр ADCSRA. Если в setup'e вы настроите его биты так:

    ADCSRA |= (1 << ADPS2);
    ADCSRA &= ~(1 << ADPS1 | 1 << ADPS0);
    Скорость увеличится в 8 раз. Для полной картины по множителям, см. стр. 266 даташита на ATmega328.
    При уменьшении точности картину начинает портить сама функция analogRead, которая непременно склеивает результат из двух байт, прибегает к преобразованию типов и т.п., чтобы получить 10-битный диапазон, который уже не уместен. Поэтому можно её непоправимо улучшить, взяв в расчёт только старший байт результата компарации:
    Код (Text):

    int analogReadFast(uint8_t pin)
    {
    uint8_t low, high;
    #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    if (pin >= 54) pin -= 54; // allow for channel or pin numbers
    #elif defined(__AVR_ATmega32U4__)
    if (pin >= 18) pin -= 18; // allow for channel or pin numbers
    #elif defined(__AVR_ATmega1284__)
    if (pin >= 24) pin -= 24; // allow for channel or pin numbers
    #else
    if (pin >= 14) pin -= 14; // allow for channel or pin numbers
    #endif
     
    #if defined(__AVR_ATmega32U4__)
    pin = analogPinToChannel(pin);
    ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
    #elif defined(ADCSRB) && defined(MUX5)
    // the MUX5 bit of ADCSRB selects whether we're reading from channels
    // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
    ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
    #endif
     
    // set the analog reference (high two bits of ADMUX) and select the
    // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
    // to 0 (the default).
    #if defined(ADMUX)
    ADMUX = (analog_reference << 6) | (pin & 0x07) | _BV(ADLAR);
    #endif
    // start the conversion
    sbi(ADCSRA, ADSC);
    // ADSC is cleared when the conversion finishes
    while (bit_is_set(ADCSRA, ADSC));
        return ADCH;
    }
     
     
  6. nailxx

    nailxx Официальный Нерд Администратор

    А по поводу скоростей Serial, WiFi, SD и т.п. вопрос хороший. Затрудняюсь ответить.

    Serial может дать 115200 бод. SD и Ethernet работает через SPI, поэтому теоретически должно работать быстрее, если скармливать буферы подходящего размера. Для SD — это блоки по 512 байт, для Ethernet — это видимо, MTU ~ 1492 байта.
     
  7. DayGaik

    DayGaik Гик

    Спасибо за информацию! Пока проверил - штатным образом получается передать данные по serial-у со скоростью более 500 раз в сек со всех датчиков - пока хватит.
    Блин это же почти вся ОЗУ контроллера.