millis가 소요 한 시간을 찾을 수 있습니까?


13

이 기능 millis은 100+ 마이크로 초 이하의 범위에서 실행됩니다. 단일 millis 호출로 걸리는 시간을 측정하는 신뢰할 수있는 방법이 있습니까?

기억해야 할 한 가지 방법은를 사용하는 것입니다 micros. 그러나 호출 micros에는 함수 호출에 걸리는 시간도 포함 micros되므로 마이크로가 걸리는 시간에 따라 측정 millis이 해제 될 수 있습니다.

내가 작업하고있는 응용 프로그램으로 이것을 찾아야합니다 millis.을 포함하여 코드에서 취한 모든 단계에 대해 정확한 시간 측정이 필요합니다 .


여기서 무엇을 요구하는지 명확하게 설명 할 수 있습니까? millis ()에서 정확한 시간을 얻으려고합니까, 아니면 millis () 함수를 호출하는 데 걸리는 시간을 알아 내려고하십니까?
Cybergibbons 2012

@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()합니다 (arduino 문서는 이것을 a이라고 부르지 long만 변수 크기를 명시 적으로 지정하지 않으면 올바르지 않습니다)), 마지막으로 함수 반환.

call4 클록 사이클이 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 개의 클록 사이클 과 총 29 개의 클록 사이클에 대한 호출 오버 헤드 12가 있습니다.

16Mhz 클럭 속도 (대부분의 arduino)를 가정하면 각 클럭주기는 1 / 16e6초 또는 0.0000000625 초 (62.5 나노초)입니다. 62.5ns * 29 = 1.812 마이크로 초

따라서 단일 millis()통화에 대한 총 실행 시간 대부분의 Arduino 1.812 마이크로 초 입니다.


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
The Guy with The Hat

1) 4 개 sts는 호출 오버 헤드로 계산되지 않아야합니다. 이는 일반적으로하지 않는 휘발성 변수에 결과를 저장하는 비용입니다. 2) 내 시스템 (Arduino 1.0.5, gcc 4.8.2)에는 movws 가 없습니다 . 그런 다음 호출 비용은 다음과 millis()같습니다. 4주기의 호출 오버 헤드 + 15주기 millis()자체 = 총 19주기 (16 MHz에서 ≈ 1.188µs).
Edgar Bonet

1
@EdgarBonet - 그건하지 않습니다 감각, xA는 uint16_t. 그것이 원인 인 경우에는 최대 2 부여 야합니다. 어쨌든, 문제는 결과를 무시하면서 호출 할 때가 아니라 사용될 때 시간이 얼마나 millis()걸리는가 입니다. 실제 사용에는 결과로 무언가를 수행하는 것이 포함되므로 결과를 통해 저장하도록 강요했습니다 . 일반적으로 나중에 호출의 반환 값으로 설정된 변수를 사용하여 동일한 효과를 얻을 수 있지만 추가 호출이 응답에서 공간을 차지하도록하고 싶지 않습니다. volatile
코너 울프

uint16_t소스에서 이것은 어셈블리 (RAM에 저장된 4 바이트)와 일치하지 않습니다. 아마도 두 가지 버전의 소스와 디스 어셈블리를 게시했을 것입니다.
Edgar Bonet

@ConnorWolf 놀라운 답변과 설명. 감사합니다!
Lefteris

8

루프를 만드는 것이 아니라 복사하여 붙여 넣기하여 1000 배의 스케치를 작성하십시오. 그것을 측정하고 실제 예상 시간과 비교하십시오. IDE의 버전 (특히 컴파일러)에 따라 결과가 다를 수 있습니다.

다른 옵션은 millis 호출 전후에 IO 핀을 토글 한 다음 시간을 매우 작은 값과 약간 큰 값으로 측정하는 것입니다. 측정 된 타이밍을 비교하고 오버 헤드를 계산하십시오.

가장 정확한 방법은 생성 된 코드 인 디스 어셈블리 목록을 살펴 보는 것입니다. 그러나 그것은 희미한 마음을위한 것이 아닙니다. 각 명령어주기에 걸리는 시간을 면밀히 살펴보아야합니다.


1000 번의 millis()통화 시간을 어떻게 측정 하시겠습니까?
apnorton 2019

millis ()가 timer0의 인터럽트에 의해 제공되어 틱마다 내부 변수가 증가한다는 것을 알고 있습니까?
TheDoctor

@TheDoctor와 혼합 delay하면 당신이 맞습니다. 그러나 아이디어는 동일하게 유지되므로 많은 통화 시간을 정하고 평균을 내릴 수 있습니다. O), 인터럽트 끄면 세계적으로 아주 좋은 아이디어를 생각하지 않을 수 있습니다
jippie

직렬로 문자를 인쇄하는 데 몇 밀리 초가 걸리므로 데이터 세트가 충분히 커야합니다. 정확한 시간은 기억 나지 않지만 시리얼로 전송되는 문자 당 ~ 0.6ms와 같은 것 같습니다.
Steven10172

@ Steven10172는 1000 번 문자열 (또는 그 이상)에 대해 빈 문자열의 시간을 정할 수 있으며 델타를 알고 측정이 더 정확합니다.
jippie

3

두 번째로 millis를 반복해서 호출 한 다음 실제 대 예상을 비교합니다.

약간의 오버 헤드가 있지만, 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" ::)) 과 리턴으로 .

조건부로 휘발성이있는 FOR 루프를 사용하여 약 천만 번이라고 부릅니다. volatile 키워드는 컴파일러가 루프 자체에서 최적화를 시도하지 못하게합니다.

나는 문법적으로 완벽한 다음을 보장하지 않습니다 ..

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");

내 생각에 밀리미터 호출 당 ~ 900ms 또는 약 56us가 걸립니다. (나는 aruduino 편리한 ATM이 없습니다.


1
당신은 변경해야 int temp1,temp2;volatile int temp1,temp2;잠재적으로 그들을 멀리 최적화에서 컴파일러를 방지 할 수 있습니다.
코너 울프

휘발성에 대한 좋은 전화. 나는 그것을 넣을 생각이었고 그렇지 않았다. 또한보다 적절한 벤치 마크를 수행하는 방법은 빈 루프를 실행하고 해당 실행 시간을 기록한 다음 작업을 수행하는 동안 루프를 다시 실행하는 것입니다. 차이를 빼고 반복 횟수로 나누면 실행 시간이 매우 정확합니다.
80HD

벤치 마크의 종류는하지 않는 시스템에서 작동 코드 실행을 선점. arduino 환경 에는 기본적 으로 주기적으로 실행되는 주기적 인터럽트가 있습니다. 더 나은 해결책은 모든 실행에서 핀을 토글하고 일종의 고해상도 타이머를 사용하여 문제가있는 코드를 실행하고 실행하지 않을 때 토글 속도를 측정하고 각 샘플에 대해 최소 실행 시간을 취하는 것입니다 기준을 빼고 실행 시간으로 처리 합니다 . 실행 시간이 인터럽트 사이의 최소 시간보다 짧다고 가정합니다.
코너 울프
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.