Чи можна знайти час, який займає міліс?


13

Функція millisпрацюватиме протягом проміжку 100+ мікросекунд або менше. Чи існує надійний спосіб виміряти час, що забирається одним викликом мільйонів?

Один із підходів, який спадає на думку, використовує micros, однак, заклик до microsвключення часу, який також займає сам виклик функції micros, тому залежно від часу, який триває мікросхема, вимірювання millisможе бути вимкнено.

Мені потрібно знайти це як додаток, над яким я працюю, вимагає точних вимірювань часу для кожного кроку, зробленого в коді, в тому числі millis.


Чи можете ви уточнити, що ви тут просите? Ви намагаєтеся отримати точний час від millis () або ви намагаєтеся розібратися, скільки часу потрібно для виклику функції millis ()?
Кібергіббони

@Cybergibbons Скільки часу потрібно miilisзайняти дзвінок .
asheeshr

Відповіді:


21

Якщо ви хочете точно знати , скільки часу щось займе, є лише одне рішення: Подивіться на розбирання!

Починаючи з мінімального коду:

void setup(){};

volatile uint16_t x;
void loop()
{
  x = millis();

}

Цей код, складений та поданий у, avr-objdump -Sстворює документально розібрані. Ось цікаві уривки:

void loop() виробляє:

000000a8 <loop>:
  a8:   0e 94 a7 00     call    0x14e   ; 0x14e <millis>
  ac:   60 93 00 01     sts 0x0100, r22
  b0:   70 93 01 01     sts 0x0101, r23
  b4:   80 93 02 01     sts 0x0102, r24
  b8:   90 93 03 01     sts 0x0103, r25
  bc:   08 95           ret

Що є викликом функції ( call), чотирма копіями (які копіюють кожен байт у uint32_tзворотному значенні millis()(зауважте, що аркуїнові документи називають це a long, але вони неправильні, щоб не чітко вказувати змінні розміри)), і нарешті повернення функції.

callвимагає 4 тактових циклів, і кожен stsвимагає 2 тактових циклів, тому у нас є як мінімум 12 тактових циклів лише для накладних викликів функцій.

Тепер давайте подивимось на розбирання <millis>функції, яка знаходиться за адресою 0x14e:

unsigned long millis()
{
    unsigned long m;
    uint8_t oldSREG = SREG;
 14e:   8f b7           in  r24, 0x3f   ; 63

    // disable interrupts while we read timer0_millis or we might get an
    // inconsistent value (e.g. in the middle of a write to timer0_millis)
    cli();
 150:   f8 94           cli
    m = timer0_millis;
 152:   20 91 08 01     lds r18, 0x0108
 156:   30 91 09 01     lds r19, 0x0109
 15a:   40 91 0a 01     lds r20, 0x010A
 15e:   50 91 0b 01     lds r21, 0x010B
    SREG = oldSREG;
 162:   8f bf           out 0x3f, r24   ; 63

    return m;
}
 164:   b9 01           movw    r22, r18
 166:   ca 01           movw    r24, r20
 168:   08 95           ret

Як бачите, millis()функція досить проста:

  1. in зберігає налаштування регістра переривання (1 цикл)
  2. cli вимикає переривання (1 цикл)
  3. lds скопіюйте один із 4 байтів поточного значення лічильника мілі в тимчасовий регістр (2 тактові цикли)
  4. lds Байт 2 (2 тактових цикла)
  5. lds Байт 3 (2 тактових цикла)
  6. lds Байт 4 (2 тактових цикла)
  7. out відновити налаштування переривання (1 тактовий цикл)
  8. movw регістри перетасування навколо (1 тактовий цикл)
  9. movw і знову (1 тактовий цикл)
  10. ret повернення з підпрограми (4 цикли)

Отже, якщо додати їх усі, у нас усього millis()функція 17 тактових циклів у самій функції плюс накладні витрати на виклик 12, в цілому 29 тактових циклів.

Припускаючи тактову частоту 16 МГц (більшість ардуїно), кожен тактовий цикл становить 1 / 16e6секунди, або 0,0000000625 секунд, що становить 62,5 наносекунд. 62,5 нс * 29 = 1,812 мікросекунди.

Тому загальний час виконання одного millis()дзвінка на більшості Arduinos становитиме 1812 мікросекунд .


Довідка Асамблеї AVR

Як бічна примітка, тут є простір для оптимізації! Якщо ви оновите unsigned long millis(){}визначення функції, яка буде inline unsigned long millis(){}, ви видалите накладні виклики (ціною трохи більшого розміру коду). Крім того, схоже, що компілятор робить два непотрібні кроки (два movwдзвінки, але я не переглядав це уважно).

Дійсно, з урахуванням накладних витрат виклику функції 5 інструкцій, а фактичні змістом цього millis()функції тільки 6 інструкції, я думаю , що millis()функція повинна бути дійсно inlineза замовчуванням, але Arduino кодового досить погано оптимізований.


Ось повний розбір для всіх, хто цікавиться:

sketch_feb13a.cpp.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
    SREG = oldSREG;

    return m;
}

unsigned long micros() {
   0:   0c 94 34 00     jmp 0x68    ; 0x68 <__ctors_end>
   4:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
   8:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
   c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  10:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  14:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  18:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  1c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  20:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  24:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  28:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  2c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  30:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  34:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  38:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  3c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  40:   0c 94 5f 00     jmp 0xbe    ; 0xbe <__vector_16>
  44:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  48:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  4c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  50:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  54:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  58:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  5c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  60:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  64:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>

00000068 <__ctors_end>:
  68:   11 24           eor r1, r1
  6a:   1f be           out 0x3f, r1    ; 63
  6c:   cf ef           ldi r28, 0xFF   ; 255
  6e:   d8 e0           ldi r29, 0x08   ; 8
  70:   de bf           out 0x3e, r29   ; 62
  72:   cd bf           out 0x3d, r28   ; 61

00000074 <__do_copy_data>:
  74:   11 e0           ldi r17, 0x01   ; 1
  76:   a0 e0           ldi r26, 0x00   ; 0
  78:   b1 e0           ldi r27, 0x01   ; 1
  7a:   e2 e0           ldi r30, 0x02   ; 2
  7c:   f2 e0           ldi r31, 0x02   ; 2
  7e:   02 c0           rjmp    .+4         ; 0x84 <.do_copy_data_start>

00000080 <.do_copy_data_loop>:
  80:   05 90           lpm r0, Z+
  82:   0d 92           st  X+, r0

00000084 <.do_copy_data_start>:
  84:   a0 30           cpi r26, 0x00   ; 0
  86:   b1 07           cpc r27, r17
  88:   d9 f7           brne    .-10        ; 0x80 <.do_copy_data_loop>

0000008a <__do_clear_bss>:
  8a:   11 e0           ldi r17, 0x01   ; 1
  8c:   a0 e0           ldi r26, 0x00   ; 0
  8e:   b1 e0           ldi r27, 0x01   ; 1
  90:   01 c0           rjmp    .+2         ; 0x94 <.do_clear_bss_start>

00000092 <.do_clear_bss_loop>:
  92:   1d 92           st  X+, r1

00000094 <.do_clear_bss_start>:
  94:   ad 30           cpi r26, 0x0D   ; 13
  96:   b1 07           cpc r27, r17
  98:   e1 f7           brne    .-8         ; 0x92 <.do_clear_bss_loop>
  9a:   0e 94 f0 00     call    0x1e0   ; 0x1e0 <main>
  9e:   0c 94 ff 00     jmp 0x1fe   ; 0x1fe <_exit>

000000a2 <__bad_interrupt>:
  a2:   0c 94 00 00     jmp 0   ; 0x0 <__vectors>

000000a6 <setup>:
  a6:   08 95           ret

000000a8 <loop>:
  a8:   0e 94 a7 00     call    0x14e   ; 0x14e <millis>
  ac:   60 93 00 01     sts 0x0100, r22
  b0:   70 93 01 01     sts 0x0101, r23
  b4:   80 93 02 01     sts 0x0102, r24
  b8:   90 93 03 01     sts 0x0103, r25
  bc:   08 95           ret

000000be <__vector_16>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
  be:   1f 92           push    r1
  c0:   0f 92           push    r0
  c2:   0f b6           in  r0, 0x3f    ; 63
  c4:   0f 92           push    r0
  c6:   11 24           eor r1, r1
  c8:   2f 93           push    r18
  ca:   3f 93           push    r19
  cc:   8f 93           push    r24
  ce:   9f 93           push    r25
  d0:   af 93           push    r26
  d2:   bf 93           push    r27
    // copy these to local variables so they can be stored in registers
    // (volatile variables must be read from memory on every access)
    unsigned long m = timer0_millis;
  d4:   80 91 08 01     lds r24, 0x0108
  d8:   90 91 09 01     lds r25, 0x0109
  dc:   a0 91 0a 01     lds r26, 0x010A
  e0:   b0 91 0b 01     lds r27, 0x010B
    unsigned char f = timer0_fract;
  e4:   30 91 0c 01     lds r19, 0x010C

    m += MILLIS_INC;
  e8:   01 96           adiw    r24, 0x01   ; 1
  ea:   a1 1d           adc r26, r1
  ec:   b1 1d           adc r27, r1
    f += FRACT_INC;
  ee:   23 2f           mov r18, r19
  f0:   2d 5f           subi    r18, 0xFD   ; 253
    if (f >= FRACT_MAX) {
  f2:   2d 37           cpi r18, 0x7D   ; 125
  f4:   20 f0           brcs    .+8         ; 0xfe <__vector_16+0x40>
        f -= FRACT_MAX;
  f6:   2d 57           subi    r18, 0x7D   ; 125
        m += 1;
  f8:   01 96           adiw    r24, 0x01   ; 1
  fa:   a1 1d           adc r26, r1
  fc:   b1 1d           adc r27, r1
    }

    timer0_fract = f;
  fe:   20 93 0c 01     sts 0x010C, r18
    timer0_millis = m;
 102:   80 93 08 01     sts 0x0108, r24
 106:   90 93 09 01     sts 0x0109, r25
 10a:   a0 93 0a 01     sts 0x010A, r26
 10e:   b0 93 0b 01     sts 0x010B, r27
    timer0_overflow_count++;
 112:   80 91 04 01     lds r24, 0x0104
 116:   90 91 05 01     lds r25, 0x0105
 11a:   a0 91 06 01     lds r26, 0x0106
 11e:   b0 91 07 01     lds r27, 0x0107
 122:   01 96           adiw    r24, 0x01   ; 1
 124:   a1 1d           adc r26, r1
 126:   b1 1d           adc r27, r1
 128:   80 93 04 01     sts 0x0104, r24
 12c:   90 93 05 01     sts 0x0105, r25
 130:   a0 93 06 01     sts 0x0106, r26
 134:   b0 93 07 01     sts 0x0107, r27
}
 138:   bf 91           pop r27
 13a:   af 91           pop r26
 13c:   9f 91           pop r25
 13e:   8f 91           pop r24
 140:   3f 91           pop r19
 142:   2f 91           pop r18
 144:   0f 90           pop r0
 146:   0f be           out 0x3f, r0    ; 63
 148:   0f 90           pop r0
 14a:   1f 90           pop r1
 14c:   18 95           reti

0000014e <millis>:

unsigned long millis()
{
    unsigned long m;
    uint8_t oldSREG = SREG;
 14e:   8f b7           in  r24, 0x3f   ; 63

    // disable interrupts while we read timer0_millis or we might get an
    // inconsistent value (e.g. in the middle of a write to timer0_millis)
    cli();
 150:   f8 94           cli
    m = timer0_millis;
 152:   20 91 08 01     lds r18, 0x0108
 156:   30 91 09 01     lds r19, 0x0109
 15a:   40 91 0a 01     lds r20, 0x010A
 15e:   50 91 0b 01     lds r21, 0x010B
    SREG = oldSREG;
 162:   8f bf           out 0x3f, r24   ; 63

    return m;
}
 164:   b9 01           movw    r22, r18
 166:   ca 01           movw    r24, r20
 168:   08 95           ret

0000016a <init>:

void init()
{
    // this needs to be called before setup() or some functions won't
    // work there
    sei();
 16a:   78 94           sei

    // on the ATmega168, timer 0 is also used for fast hardware pwm
    // (using phase-correct PWM would mean that timer 0 overflowed half as often
    // resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
    sbi(TCCR0A, WGM01);
 16c:   84 b5           in  r24, 0x24   ; 36
 16e:   82 60           ori r24, 0x02   ; 2
 170:   84 bd           out 0x24, r24   ; 36
    sbi(TCCR0A, WGM00);
 172:   84 b5           in  r24, 0x24   ; 36
 174:   81 60           ori r24, 0x01   ; 1
 176:   84 bd           out 0x24, r24   ; 36
    // this combination is for the standard atmega8
    sbi(TCCR0, CS01);
    sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
    // this combination is for the standard 168/328/1280/2560
    sbi(TCCR0B, CS01);
 178:   85 b5           in  r24, 0x25   ; 37
 17a:   82 60           ori r24, 0x02   ; 2
 17c:   85 bd           out 0x25, r24   ; 37
    sbi(TCCR0B, CS00);
 17e:   85 b5           in  r24, 0x25   ; 37
 180:   81 60           ori r24, 0x01   ; 1
 182:   85 bd           out 0x25, r24   ; 37

    // enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
    sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
    sbi(TIMSK0, TOIE0);
 184:   ee e6           ldi r30, 0x6E   ; 110
 186:   f0 e0           ldi r31, 0x00   ; 0
 188:   80 81           ld  r24, Z
 18a:   81 60           ori r24, 0x01   ; 1
 18c:   80 83           st  Z, r24
    // this is better for motors as it ensures an even waveform
    // note, however, that fast pwm mode can achieve a frequency of up
    // 8 MHz (with a 16 MHz clock) at 50% duty cycle

#if defined(TCCR1B) && defined(CS11) && defined(CS10)
    TCCR1B = 0;
 18e:   e1 e8           ldi r30, 0x81   ; 129
 190:   f0 e0           ldi r31, 0x00   ; 0
 192:   10 82           st  Z, r1

    // set timer 1 prescale factor to 64
    sbi(TCCR1B, CS11);
 194:   80 81           ld  r24, Z
 196:   82 60           ori r24, 0x02   ; 2
 198:   80 83           st  Z, r24
#if F_CPU >= 8000000L
    sbi(TCCR1B, CS10);
 19a:   80 81           ld  r24, Z
 19c:   81 60           ori r24, 0x01   ; 1
 19e:   80 83           st  Z, r24
    sbi(TCCR1, CS10);
#endif
#endif
    // put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
    sbi(TCCR1A, WGM10);
 1a0:   e0 e8           ldi r30, 0x80   ; 128
 1a2:   f0 e0           ldi r31, 0x00   ; 0
 1a4:   80 81           ld  r24, Z
 1a6:   81 60           ori r24, 0x01   ; 1
 1a8:   80 83           st  Z, r24

    // set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
    sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
    sbi(TCCR2B, CS22);
 1aa:   e1 eb           ldi r30, 0xB1   ; 177
 1ac:   f0 e0           ldi r31, 0x00   ; 0
 1ae:   80 81           ld  r24, Z
 1b0:   84 60           ori r24, 0x04   ; 4
 1b2:   80 83           st  Z, r24

    // configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
    sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
    sbi(TCCR2A, WGM20);
 1b4:   e0 eb           ldi r30, 0xB0   ; 176
 1b6:   f0 e0           ldi r31, 0x00   ; 0
 1b8:   80 81           ld  r24, Z
 1ba:   81 60           ori r24, 0x01   ; 1
 1bc:   80 83           st  Z, r24
#if defined(ADCSRA)
    // set a2d prescale factor to 128
    // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
    // XXX: this will not work properly for other clock speeds, and
    // this code should use F_CPU to determine the prescale factor.
    sbi(ADCSRA, ADPS2);
 1be:   ea e7           ldi r30, 0x7A   ; 122
 1c0:   f0 e0           ldi r31, 0x00   ; 0
 1c2:   80 81           ld  r24, Z
 1c4:   84 60           ori r24, 0x04   ; 4
 1c6:   80 83           st  Z, r24
    sbi(ADCSRA, ADPS1);
 1c8:   80 81           ld  r24, Z
 1ca:   82 60           ori r24, 0x02   ; 2
 1cc:   80 83           st  Z, r24
    sbi(ADCSRA, ADPS0);
 1ce:   80 81           ld  r24, Z
 1d0:   81 60           ori r24, 0x01   ; 1
 1d2:   80 83           st  Z, r24

    // enable a2d conversions
    sbi(ADCSRA, ADEN);
 1d4:   80 81           ld  r24, Z
 1d6:   80 68           ori r24, 0x80   ; 128
 1d8:   80 83           st  Z, r24
    // here so they can be used as normal digital i/o; they will be
    // reconnected in Serial.begin()
#if defined(UCSRB)
    UCSRB = 0;
#elif defined(UCSR0B)
    UCSR0B = 0;
 1da:   10 92 c1 00     sts 0x00C1, r1
#endif
}
 1de:   08 95           ret

000001e0 <main>:
#include <Arduino.h>

int main(void)
 1e0:   cf 93           push    r28
 1e2:   df 93           push    r29
{
    init();
 1e4:   0e 94 b5 00     call    0x16a   ; 0x16a <init>

#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();
 1e8:   0e 94 53 00     call    0xa6    ; 0xa6 <setup>

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
 1ec:   c0 e0           ldi r28, 0x00   ; 0
 1ee:   d0 e0           ldi r29, 0x00   ; 0
#endif

    setup();

    for (;;) {
        loop();
 1f0:   0e 94 54 00     call    0xa8    ; 0xa8 <loop>
        if (serialEventRun) serialEventRun();
 1f4:   20 97           sbiw    r28, 0x00   ; 0
 1f6:   e1 f3           breq    .-8         ; 0x1f0 <main+0x10>
 1f8:   0e 94 00 00     call    0   ; 0x0 <__vectors>
 1fc:   f9 cf           rjmp    .-14        ; 0x1f0 <main+0x10>

000001fe <_exit>:
 1fe:   f8 94           cli

00000200 <__stop_program>:
 200:   ff cf           rjmp    .-2         ; 0x200 <__stop_program>

Ух, чудова відповідь! +1
Хлопець з капелюхом

1) Четверо stsне слід вважати накладними викликами: це вартість зберігання результату в мінливій змінній, що зазвичай не робиться. 2) У моїй системі (Arduino 1.0.5, gcc 4.8.2) у мене немає movws. Тоді вартість дзвінка millis()становить: 4 цикли накладних витрат + 15 циклів millis()само по собі = 19 циклів (≈ 1,188 мкс при 16 МГц).
Едгар Бонет

1
@EdgarBonet - Це не має сенсу, xє uint16_t. Має бути не більше 2 примірників, якщо це є причиною. У будь-якому разі, питання полягає в тому, скільки часу millis()займає при використанні , а не коли викликається при ігноруванні результату. Оскільки будь-яке практичне використання передбачає щось робити з результатом, я змусив результат зберігатись через volatile. Як правило, такого ж ефекту було б досягнуто пізнішим використанням змінної, встановленої на зворотне значення виклику, але я не хотів, щоб цей додатковий виклик займав місце у відповіді.
Вонор Коннор

Це uint16_tв джерелі не відповідає збірці (4 байти, що зберігаються в оперативній пам'яті). Можливо, ви розмістили джерело та розбирання двох різних версій.
Едгар Бонет

@ConnorWolf Дивовижна відповідь та пояснення. Дякую!
Лефтеріс

8

Напишіть ескіз, який міліс 1000 разів, не роблячи цикл, а копіюючи та вставляючи. Виміряйте це та порівняйте його з фактично очікуваним часом. Зауважте, що результати можуть відрізнятися в різних версіях IDE (і зокрема його компілятора).

Інший варіант - переключити IO-контакт до та після виклику міліс, потім виміряти час для дуже малого та дещо більшого значення. Порівняйте виміряні терміни та обчисліть накладні витрати.

Найточніший спосіб - подивитися список розбирання, згенерований код. Але це не для слабкого серця. Вам доведеться уважно вивчити таблицю даних, скільки часу займає кожен цикл інструкцій.


Як би ви оцінили час, зайнятий 1000 millis()дзвінками?
apnorton

Ви знаєте, що millis () надається перериванням на timer0, що збільшує внутрішню змінну кожного галочки?
TheDoctor

@TheDoctor, з яким я змішався delay, ти маєш рацію. Але ідея залишається такою ж, ви можете затримати велику кількість дзвінків і провести їх середнє значення. Вимкнення переривань в усьому світі може бути не дуже хорошою ідеєю; o)
jippie

Переконайтеся, що ваш набір даних досить великий, оскільки друк символів на серіалі займає кілька мілісекунд самостійно. Я не пам'ятаю точного часу, але я думаю, що це щось на зразок ~ 0,6 мс на персонаж, що надсилається на серійний.
Стівен10172

@ Steven10172 ви можете призначити порожній рядок проти 1000-кратного рядка (або більше), тоді ви знаєте дельту і вимірювання є більш точним.
jippie

3

Я вдруге дзвоню в міліс кілька разів, а потім порівнюю фактичні та очікувані.

Буде кілька мінімальних накладних витрат, але це зменшиться в значенні, тим більше разів ви називатимете millis ().

Якщо ви подивитеся

C:\Program Files (x86)\Arduino\Arduino ERW 1.0.5\hardware\arduino\cores\arduino\wiring.c

Ви можете бачити, що millis () дуже крихітний за допомогою лише 4 інструкцій (cli is simply # define cli() \__asm__ \__volatile__ ("cli" ::))та повернення.

Я б назвав це приблизно в 10 мільйонів разів, використовуючи петлю FOR, яка є мінливою як умовна. Мінливе ключове слово не дозволить компілятору спробувати будь-яку оптимізацію в циклі.

Я не гарантую, що наступне буде синтаксично досконалим ..

int temp1,temp2;
temp1=millis();
for (volatile unsigned int j=0;j<1000000;++j){
temp2=millis();}
Serial.print("Execution time = ");
Serial.print((temp2-temp1,DEC);
Serial.print("ms");

я гадаю, що на мільйони потрібно ~ 900 мс або приблизно 56us за дзвінок. (У мене немає зручного банкомату з арудуїно.


1
Вам слід змінити, int temp1,temp2;щоб volatile int temp1,temp2;запобігти потенційній оптимізації компілятора.
Вонор Коннор

Хороший дзвінок на летючі. Я, безумовно, мав намір це вкласти, а потім ні. Я також повинен був зазначити, що спосіб зробити більш правильний орієнтир - це запустити порожній цикл, записати цей час виконання, а потім запустити цикл ще раз, виконуючи роботу. Відніміть різницю, поділіть на кількість ітерацій, і настав ваш дуже точний час виконання.
80ХД

Такий рід еталон працює тільки в системі , яка завжди передбачити ваш код. Середовище arduino за замовчуванням має періодичні переривання, які виконуватимуться періодично. Кращим рішенням буде переключення штифта на кожне виконання та використання таймера високої роздільної здатності для вимірювання швидкості перемикання під час запуску та не запуску відповідного коду, взяти мінімальний час виконання для кількох зразків для кожного , відніміть базову лінію і розглядайте це як час виконання. Якщо припустити, що ваш термін виконання коротший, ніж мінімальний час між перервами.
Коннор Вольф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.