Странное поведение задержки в Atmega328P - PullRequest
0 голосов
/ 18 октября 2018

Итак, я реализовал пользовательскую функцию задержки, используя стандартную функцию, найденную в utils / delay.h.

inline void delay_us(uint16_t time) {
    while (time > 0) {
        _delay_us(1);
        time--;
    }
}

Она вызывается внутри цикла в основной функции:

#define F_CPU 16000000UL

...

int main() {
    pin_mode(P2, OUTPUT);
    while (1) {
        pin_enable(P2);
        delay_us(1);
        pin_disable(P2);
        delay_us(1);
    }
}

Используя осциллограф, я могу сказать, что штифт остается на высоте 1,120 и низко на 1,120 с параметром 1.Увеличив параметр до 6, осциллограф показывает мне 6.120us.Но с 7 остается 9 нас.С 10, около 14 с нами.

Я знаю, что цикл идет с накладными расходами, но почему нет никаких накладных расходов (или почему накладные расходы не меняются) между 1 и 6 нами?

OBS: Я использую Arduino UNO (16 МГц)

1 Ответ

0 голосов
/ 07 декабря 2018

Для небольших аргументов gcc-avr развернет цикл while, эффективно объединяя несколько задержек в 1 мкс:

delay_us(5):
    ldi r24,lo8(5)
    mov r25,r24
    1: dec r25
    brne 1b
    mov r25,r24
    1: dec r25
    brne 1b
    mov r25,r24
    1: dec r25
    brne 1b
    mov r25,r24
    1: dec r25
    brne 1b
    1: dec r24
    brne 1b

В какой-то момент, однако, компилятор меняет свою стратегию с развертывания, занимающего много места, на собственно ветвлениечерез цикл while:

delay_us(6):
    ldi r24,lo8(6)
    ldi r25,hi8(6)
    ldi r19,lo8(5)
.L2:
    mov r18,r19
    1: dec r18
    brne 1b
    sbiw r24,1
    brne .L2

В это время тщательно продуманная функция _delay_us() будет более или менее побеждена.Издержки ветвления значительны по сравнению с 16 тактами, необходимыми для одного _delay_us(1), и будут оплачиваться за каждую итерацию цикла.

Внезапное увеличение времени выполнения, которое вы описываете, - это, по сути, точка, в которой ваш компилятор останавливается наразверните цикл.

Сравните это с непосредственным вызовом _delay_us(6):

_delay_us(6):
    ldi r24,lo8(32)
    1: dec r24
    brne 1b

Показанная выше сборка может несколько отличаться от того, что делает ваш компилятор, поскольку выходные данные компилятора могут значительно различаться в зависимости от версии ифлаги, но списки должны быть достаточно близко.Для примеров я использовал gcc-avr 4.6.4 с уровнем оптимизации -O2. Попробуй

...