Для небольших аргументов 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
. Попробуй